React 세미나
세미나 진행내용
내용 | 소요시간 |
---|---|
React 소개 - 개요 - 개발 환경 구성 - 프로젝트 생성 및 구조 |
3분 2분 3분 |
React 기본 개념 - 컴포넌트 개념 및 작성 - JSX 기본 문법 |
2분 4분 |
props와 useState - props - 이벤트 처리 - Stats Hooks |
7분 6분 8분 |
조건부 처리 | 5분 |
동적 리스트 | 5분 |
목표
- React의 기본 개념을 이해한다.
- React의 컴포넌트, 상태관리 개념을 이해한다.
-
React의 상태 관리 개념을 적용하여 Tab, List UI 컴포넌트를 만든다.
1. React 소개
1-1 개요
! [[ Pasted image 20240625165938.png ]] Javascript로 html DOM을 변형 시켜 동적으로 UI를 표현하고자 할 때, DOM을 직접 제어하는 수고로움을 덜기 위하여 만들어진 라이브러리 ==자바스크립트의 특정 값이 바뀌면 특정 DOM의 속성이 바뀌도록 연결하는 방식으로 페이지가 업데이트 된다 ==
- 컴포넌트 단위로 관리하여 코드의 재사용성을 높인다.
- html 태그와 js 코드가 하나의 파일에 함께 작성한다.
- 서로 다른 컴포넌트는 각각 독립적이다.
컴포넌트 생태계
— start-multi-column: ExampleRegion1
number of columns: 2 largest column: left border : off
! [[ Pasted image 20240625170048.png ]]
— end-column —
React.js Angular Flutter Vue.js Svelt.js
— end-multi-column
1-2 개발 환경 구성
설치
- 샌드박스 https://codesandbox.io/p/sandbox/react-new?file=%2Fsrc%2Findex.js
- Node.js
-
yarn or npm
토스 기술 블로그 - yarn, npm 차이 https://toss.tech/article/lightning-talks-package-manager
$ npm install --global yarn
1-3 프로젝트 생성 및 구조
(신규)프로젝트 생성
``` // 현위치 하위에 디렉토리 생성 $ npx create-react-app begin-react
Need to install the following packages: create-react-app@5.0.1 Ok to proceed? (y) y
$ cd begin-react $ yarn start
http://localhost:3000/
##### (기존)프로젝트 실행
// git clone 또는 zip 파일 다운로드 후 해당 폴더에서 다음 실행 $ npm install $ yarn install
// 로컬 스타트 $ yarn start
##### 프로젝트 구조
!<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> Pasted image 20240625133625.png <span class='invalid-link-brackets'>]]</span></span>
my-react-app/ │ ├── public/ │ └── index.html │ ├── src/ │ ├── components/ │ │ ├── List.js │ │ ├── ListItem.js │ │ └── Form.js │ ├── App.js │ └── index.js │ ├── package.json └── README.md
---
## 2. React 기본 개념
#### 2-1 컴포넌트 개념 및 작성
##### 컴포넌트란?
- UI 조각
!<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> Component.png <span class='invalid-link-brackets'>]]</span></span>
###### 기본 규칙
!<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> 스크린샷 2024-06-27 092640.png <span class='invalid-link-brackets'>]]</span></span>
- 대문자로 시작
- 함수에서 렌더링이 가능한 return값
**Hello.js**
import React from ‘react’;
function Hello() { return <div>안녕하세요</div> }
export default Hello;
**App.js**
import React from ‘react’; import Hello from ‘./Hello’;
function App() {
return (
<div>
export default App;
**index.js**
import React from ‘react’; import ReactDOM from ‘react-dom/client’; import ‘./index.css’; import App from ‘./App’; import reportWebVitals from ‘./reportWebVitals’;
const root = ReactDOM.createRoot(document.getElementById(‘root’));
root.render(
> [!NOTE] **index.js > index.html**
> !<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> Pasted image 20240625145034.png <span class='invalid-link-brackets'>]]</span></span>
> !<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> 2024-06-25 144730.png <span class='invalid-link-brackets'>]]</span></span>
###### 렌더링 과정
```mermaid
flowchart LR
id0(Hello.js) --> id1(App.js) --> id3(index.js)
! [[ 다운로드.jpeg ]]
[!NOTE] React의 렌더링 과정
- 초기 렌더링: 처음 컴포넌트를 렌더링할 때, React는 Virtual DOM을 생성하고, 이를 기반으로 실제 DOM을 생성.
- 업데이트: 상태(state)나 props가 변경되면, React는 새로운 Virtual DOM을 생성.
- Diffing: 새로운 Virtual DOM과 이전 Virtual DOM을 비교하여 변경된 부분을 찾기.
- 패칭: 변경된 부분만 실제 DOM에 적용.
2-2 JSX 기본 문법
JSX
JavaScript XML의 약자로, JavaScript 내에서 XML/HTML 같은 구문을 사용할 수 있게 해주는 문법 확장이다. 리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel 이 JSX를 JavaScript 로 변환해준다.
==한마디로 리액트 컴포넌트는 xml 형식의 값을 반환한다.==
확장자 : .jsx
.js
태그 닫기
...
function App() {
return (
<div>
<Hello />
);
}
...
- 반드시
</div>
태그를 닫아야 함Self Closing
... function App() { return ( <div> <Hello /> ...
- 닫기
/
기호로 반드시 태그가 닫힘을 표시해야 한다.태그 감싸기 or Fragment
``` …
function App() {
return (
<div>
function App() {
return (
<>
- 두 개의 태그는 반드시 하나의 태그로 감싸야 한다.
- 너무 많은 감싸기 태그를 사용한다면 `<>` `</>` Fragment 로 감싸준다.
##### JSX안에 자바스크립트 값 사용하기
…
function App() {
const name = ‘react’;
return (
<>
…
##### style 과 className
@App.css .gray-box { background: gray; width: 64px; height: 64px; }
… function App() { const name = ‘react’; const style = { backgroundColor: ‘black’, color: ‘aqua’, fontSize: 24, // 기본 단위 px padding: ‘1rem’ // 다른 단위 사용 시 문자열로 설정 }
return (
<>
##### 주석
return ( <> {/* 주석은 화면에 보이지 않습니다 /} / 중괄호로 감싸지 않으면 화면에 보입니다 */ <Hello // 열리는 태그 내부에서는 이렇게 주석을 작성 할 수 있습니다. /> <div style={style}>{name}</div> <div className="gray-box"></div> </> ); }
- 내부 주석 `{/* 이런 형태로 */}`
- 열리는 태그 내부 `// 주석 가능`
**코드분량비교**
다음의 코드를 GPT 에게 JS, html 로 구현해보라고 하기
**App.js**
import img from ‘./assets/react-core-concepts.png’;
const word = [‘신나는’, ‘재밌는’, ‘즐거운’];
function getRandom(max){ return Math.floor(Math.random() * (max + 1)); }
function Header(){ const desc = word[getRandom(2)];
return( <header> <img src={img} alt=”img desc” /> <h1>React Seminar</h1> <p> {desc} 리액트 세미나 입니다! </p> </header> ); }
function App() { return ( <div> <Header /> <main> <h2>Time to get started!</h2> </main> </div> ); }
export default App;
---
여기서부터 추가
import { CORE_CONCEPTS } from ‘./data’; import Header from ‘./components/Header.jsx’; import Concept from ‘./components/Concept.jsx’; import TabButton from ‘./components/TabBtn.jsx’; import { useState } from ‘react’; import { EXAMPLES } from ‘./data’;
function App() { // 내부함수 중첩 X, 반드시 컴포넌트 함수의 최상위에서 호출 할 것. // const [ selectedTopic , setSelectedTopic ] = useState(‘버튼을 클릭해보세요!’); const [ selectedTopic , setSelectedTopic ] = useState(‘’);
function handleSelect(selectedBtn) { setSelectedTopic(selectedBtn); }
console.log();
return (
<div>
<Header />
<main>
<section id="core-concepts">
<h2>주요 개념</h2>
<ul>
{/* <Concept
title={CORE_CONCEPTS[0].title}
description={CORE_CONCEPTS[0].description}
image={CORE_CONCEPTS[0].image}
/>
<Concept {…CORE_CONCEPTS[1]} /> /}
{CORE_CONCEPTS.map((conceptItem) => <Concept key={conceptItem.title} {…conceptItem} />)}
</ul>
</section>
<section id="examples">
<h2>컴포넌트 예</h2>
<menu>
<TabButton isSeleted={selectedTopic === ‘components’} onSelect={() => handleSelect(‘components’)}>Componenets</TabButton>
{/
{EXAMPLES[selectedTopic].code}
</pre>
</div>)}
</section>
</main>
</div>
);
}
export default App;
### 3. props와 useState
##### 3-1 props
properties 의 줄임말.
- 컴포넌트의 특징을 설정하는 속성
- 어떠한 값을 컴포넌트에서 컴포넌트로 전달해줘야 할 때, props를 사용
- 부모 컴포넌트에서 자식 컴포넌트 전달
**App.js**
function Concept(props){ return ( <li> <img src={props.image} alt=”…” /> <h3>Title</h3> <p>Description</p> </li> ); }
- 컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 할 수 있다.
(모든 커스텀 특성은 Key가 되고 속성값(properties)은 값으로)
- props는 객체 형태로 전달된다.
!<span title='There is no note that matches this link.' class='invalid-link'> <span class='invalid-link-brackets'>[[</span> Pasted image 20240628170814.png <span class='invalid-link-brackets'>]]</span></span>
- props에 접근하기 props.name으로 조회
- Props는 읽기 전용. 자식 컴포넌트는 props를 변경할 수 없음. 부모 컴포넌트에서만 props를 변경할 수 있다.
> [!NOTE] **props vs children**
> 쓰이는 곳(App.js)에서 값을 정함 = props(부모)
> 쓰임 당하는 곳(Concept.js)에서 값을 정함 = children
###### 여러개의 props, 비구조화 할당(구조분해)
- props가 여러 개 일 때
**App()**
<Concept
title={CORE_CONCEPTS[0].title}
description={CORE_CONCEPTS[0].description}
image={CORE_CONCEPTS[0].image}
/>
<Concept {...CORE_CONCEPTS[1]} /> ```
Concept() - 구조분해 적용
function Concept(){
return (
<li>
<img src={image} alt={title} />
<h3>{title}</h3>
<p>{description}</p>
</li>
);
}
프로젝트 구조 변경(컴포넌트 분리)
- export or export default(권장) 모두 사용 가능
- 컴포넌트에 종속되는 파일들 항목들을 되도록 같은 폴더에 위치하기
컴포넌트 합성(props.children)
- 컴포넌트 태그 사이에 넣은 값,
props.children
App.jsx
<section id="examples">
<h2>컴포넌트 예</h2>
<menu>
<TabButton >컴포넌트</TabButton>
</menu>
</section>
TabButton.jsx
export default function TabButton(props){
return(
<li>
<button>{props.children}</button>
</li>
);
}
Children
<TabButton >컴포넌트</TabButton>
export default function TabButton({children}){
return(
<li>
<button>{children}</button>
</li>
);
}
Attributes
<TabButton label="컴포넌트" />
export default function TabButton({label}) {
return(
<li>
<button>{label}</button>
</li>
)
}
Simple Hands-on
export default function TabButton({children}){
//App.jsx 로 옮기기
function handleClick() {
console.log('버튼 눌림')
}
console.log('TabBtn.js 가 실행되었습니다.')
return(
<li>
{/* handleClick() 실행 함수 넣지 않도록 주의 */}
<button onClick={handleClick}>{children}</button>
</li>
);
}
3-2 이벤트 처리
코드 재평가
// 1
let tabContent = '버튼을 눌러주세요!';
// 2
function handleSelect(selectedBtn) {
tabContent = selectedBtn;
console.log(tabContent);
}
console.log('App.jsx 가 렌더링 되었습니다.');
// 3
export default function TabButton({ children, onSelect }){
console.log('TabBtn.jsx 가 렌더링 되었습니다.');
...
}
- 일반 변수로는 컴포넌트 재평가 → 렌더링(재실행)이 이뤄지지 않음
- 재평가가 가능하도록 변수를 등록해주는 리액트 라이브러리 → State
- 모든 컴포넌트 및 함수는 다른 리액트
Hook
안에서 실행되어야 함
3-3 State와 Hooks
useState
- 컴포넌트에서 동적인 값을 상태(state)
- 컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 useState 함수를 사용한다.
-
import { useState } from 'react';
Hooks
- React 프로젝트에서
use
로 시작하는 모든 함수는 리액트Hooks
- 내부함수 중첩하여 사용할 수 없음
- 반드시 컴포넌트 함수의 최상위에서 호출
flowchart LR
id0(초기 렌더링) --> id1(상태 업데이트) --> id2(상태 업데이트 스케줄링) --> id3(컴포넌트 재실행) --> id4(UI업데이트)
import { useState } from 'react';
function App() {
// 내부함수 중첩 X, 반드시 컴포넌트 함수의 최상위에서 호출 할 것
// 1 value, 2 function
const [ selectedTopic , setSelectedTopic ] = useState('버튼을 클릭해보세요!');
function handleSelect(selectedBtn) {
tabContent = selectedBtn;
}
console.log();
- useState가 반환하는 값은 기본적으로 배열(val, setVal)
현재 값 | 상태를 업데이트 시키는 함수 | 초기값 | |
---|---|---|---|
const | [ selectedTopic, | setSelectedTopic ] = | useState(‘버튼을 클릭해보세요!’); |
-
useState
는 상태를 업데이트 하기 위해 실행되며, 저장된 값(현재 값)을 업데이트[!NOTE] 최초 실행 값이 보이지 않는 이유 • 초기값:
useState
는 컴포넌트가 처음 렌더링될 때만 초기값을 사용. • 상태 유지: 상태가 변경되면 컴포넌트는 다시 렌더링되나,useState
는 초기값 대신 최신 상태 값 유지 • 재렌더링: 상태 변경으로 인한 재렌더링 시,useState
는 기존 상태 값으로 컴포넌트의 상태 유지. -
setSelectedTopic
(요소2, 상태를 업데이트 시키는 함수)를 호출 할 때 이 상태 업데이트의 스케쥴을 조정하여 해당 컴포넌트 함수를 재실행 한다. 따라서 App 컴포넌트 함수를 다시 실행하고 나서야 업데이트 된 값을 사용할 수 있음
import { CORE_CONCEPTS } from './data';
import Header from './components/Header.jsx';
import Concept from './components/Concept.jsx';
import TabButton from './components/TabBtn.jsx';
import { useState } from 'react';
import { EXAMPLES } from './data';
function App() {
// 내부함수 중첩 X, 반드시 컴포넌트 함수의 최상위에서 호출 할 것.
// const [ selectedTopic , setSelectedTopic ] = useState('버튼을 클릭해보세요!');
const [ selectedTopic , setSelectedTopic ] = useState('components');
function handleSelect(selectedBtn) {
setSelectedTopic(selectedBtn);
}
console.log();
return (
<div>
<Header />
<main>
<section id="core-concepts">
<h2>Time to get started!</h2>
<ul>
<Concept
title={CORE_CONCEPTS[0].title}
description={CORE_CONCEPTS[0].description}
image={CORE_CONCEPTS[0].image}
/>
<Concept {...CORE_CONCEPTS[1]} />
</ul>
</section>
<section id="examples">
<h2>컴포넌트 예</h2>
<menu>
<TabButton onSelect={() => handleSelect('components')}>Componenets</TabButton>
{/* <TabButton label="컴포넌트" /> */}
<TabButton onSelect={() => handleSelect('jsx')}>JSX</TabButton>
<TabButton onSelect={() => handleSelect('props')}>Props</TabButton>
<TabButton onSelect={() => handleSelect('state')}>State</TabButton>
</menu>
{/* {selectedTopic} */}
<div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>
{EXAMPLES[selectedTopic].code}
</code>
</pre>
</div>
</section>
</main>
</div>
);
}
export default App;
4. 조건부 렌더링
삼항연산자
{!selectedTopic ? <p>버튼을 눌러주세요</p> : null}
{selectedTopic ? (<div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>
{EXAMPLES[selectedTopic].code}
</code>
</pre>
</div>) : null}
논리 연산자 &&
{!selectedTopic && <p>버튼을 눌러주세요!</p>}
{selectedTopic && <div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>
{EXAMPLES[selectedTopic].code}
</code>
</pre>
</div>}
조건문
let tabContent = <p>버튼을 눌러주세요!</p>;
if (selectedTopic) {
tabContent = (<div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>
{EXAMPLES[selectedTopic].code}
</code>
</pre>
</div>
);
}
...
{tabContent}
추가 + 동적 스타일링
<TabButton isSeleted={selectedTopic === 'state'} onSelect={() => handleSelect('state')}>State</TabButton>
5. 동적 리스트
<ul>
<Concept
title={CORE_CONCEPTS[0].title}
description={CORE_CONCEPTS[0].description}
image={CORE_CONCEPTS[0].image}
/>
<Concept {...CORE_CONCEPTS[1]} />
{CORE_CONCEPTS.map((conceptItem) => <Concept {...conceptItem} />)}
</ul>
[!NOTE] Warning: Each child in a list should have a unique “key” prop. React는 리스트를 렌더링할 때 각 리스트 아이템을 추적하기 위해 고유한 key 속성을 사용. 이 key 속성은 React가 각 아이템을 고유하게 식별할 수 있도록 합니다. 리스트가 변경되면(예: 아이템이 추가, 삭제, 순서 변경) React는 key 속성을 사용하여 어떤 아이템이 변경되었는지 효율적으로 파악하고, 필요한 부분만 업데이트
{CORE_CONCEPTS.map((conceptItem) => <Concept key={conceptItem.title} {...conceptItem} />)}
배포
! [[ Pasted image 20240628164314.png ]]
참고자료 모던 리액트 https://react.vlpt.us/ 가상돔이 중요한 이유 리액트 샌드박스 https://codesandbox.io/p/sandbox/react-new?file=%2Fsrc%2Findex.js (환경설치가 귀찮을 때) 모던 리액트 https://www.notion.so/995688057a6143d8a57527429cee2436?v=b156d55de25e4f34bc9a9fa2fdc8b259&pvs=4 프론트엔드 프레임워크 비교하기 https://jacky0831.tistory.com/100 리액트 빠르게 시작하기 (공식문서 한국어 튜토리얼 업데이트) https://ko.react.dev/learn
Angula 양방향 데이터 바인딩
flowchart LR
id0(모델) <-- 데이터바인딩 --> id1(뷰)
- 모델과 뷰가 서로 영향을 주고받음.
- 예: 사용자 입력이 모델을 변경하고, 모델 변경이 뷰를 업데이트.
React의 단방향 데이터 흐름
```mermaid
flowchart LR
id00(부모 상태) –> id1(자식 컴포넌트) –> id2(가상 DOM) –> id3(실제 업데이트)
```
- 데이터는 부모에서 자식으로만 전달됨.
- 상태 변경 시 가상 DOM에서 변경 사항을 계산한 후, 실제 DOM을 최소한으로 업데이트.
React Next.js Angular 주요 차이점
특성 | React | Next.js | Angular |
---|---|---|---|
기본 개념 |
UI 라이브러리 | React 기반 프레임워크 | 완전한 프레임워크 |
테이터 흐름 |
단방향 데이터 흐름 | 단방향 데이터 흐름, SSR/SSG 지원 |
양방향 데이터 바인딩 |
DOM 관리 |
가상 DOM | 가상DOM, SSR/SSG | 실제 DOM, 템플릿 기반 |
라우팅 | 별도 라이브러리 사용 (React router) |
파일 기반 라우팅 | 내장 라우팅 모듈 |
상태 관리 |
useState, useReducer 등 | React 와 동일 | 내장 라우팅 모듈 |
SEO | 클라이언트 사이드 렌더링으로 SEO 문제 |
SSR/SSG로 SEO문제 해결 | 서버 사이트 렌더링 지원 |
생태계 | 자유로운 라이브러리 선택 | React 생태계와 호환 | 통합된 생태계 |
[!multi-column]
[!note]+ Work your notes or lists here. using markdown formatting
[!warning]+ Personal your notes or lists here. using markdown formatting
[!summary]+ Charity your notes or lists here. using markdown formatting
This line appears after every note.
Notes mentioning this note
There are no notes linking to this note.