반응형
01. 리액트는 어쩌다 만들어졌을까?
- JavaScript를 사용하여 HTML 로 구성한 UI 를 제어한다면, 브라우저의 DOM Selector API 를 사용해서 특정 DOM 을 선택한 뒤, 특정 이벤트가 발생하면 변화를 주도록 설정해야 한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Number Counter</title>
</head>
<body>
<h2 id="number">0</h2>
<div>
<button id="increase">+1</button>
<button id="decrease">-1</button>
</div>
<script>
const number = document.getElementById('number');
const increase = document.getElementById('increase');
const decrease = document.getElementById('decrease');
increase.onclick = () => {
const current = parseInt(number.innerText, 10);
number.innerText = current + 1;
};
decrease.onclick = () => {
const current = parseInt(number.innerText, 10);
number.innerText = current - 1;
};
</script>
</body>
</html>
- 위 html/JS 코드는 +1 버튼이 눌리면, id 가 number 인 DOM 을 선택해서 innerText 속성을 1씩 더하는 규칙이 있다.
- 위와 같은 방식은 인터랙션이 자주 발생한다면 이벤트 처리 로직이 복잡해지고 관리하기 힘들어진다.
- DOM이란?
- Document Object Model의 약자로, HTML요소를 JavaScript Object처럼 조작할 수 있는 Model이다.
- DOM의 구조는 트리구조로, 아래에 여러 구성요소가 부모-자식 관계를 가지고 있다.
- DOM에서 제공하는 API를 변경하여 Element의 상태를 변경할 수 있다.
- DOM이 변경되고 업데이트가 된다는 것은 결국 Browser의 렌더링 엔진 또한 Reflow 및 Repaint 한다는 것을 의미한다.
- DOM을 수정할 때는 해당 Element를 찾고 해당 Element와 자녀 Element를 제거한 뒤 수정된 Element들로 교체한다. 그리고 CSS와 Layout을 수정하고 Browser에 새롭게 업데이트 하는 과정을 거친다.
- 매번 DOM을 조작할 때마다 UI를 새롭게 그리는 작업은 비용이 많이 드는 작업이기에 비효율적이다.
- React와 Virtual DOM
- Browser Reflow 및 Repaint를 최소화 하기 위해 React는 Virtual DOM을 사용한다.
- Virtual DOM은 실제 DOM에 접근하며 조작하는 대신, 이를 추상화한 자바스크립트 객체를 구성하여 DOM의 상태를 메모리에 저장하고, 변경 전과 변경 후의 상태를 비교 한뒤 최소한의 내용만 반영하여 성능 향상을 이끌어낸다.
- 상태를 변경하는 작업(Change of State)이 발생하면, React는 Virtual DOM에 저장된 이전 상태와 변경된 현재 상태를 비교한다.
- 비교 과정에서 React는 Diffing 알고리즘을 사용하여 변경된 부분을 감지한다.
- 이후 Virtual DOM과 새로운 Virtual DOM을 비교하여 변경이 필요한 부분만 실제 DOM에 업데이트(재조정, Reconciliation) 한다.
- 이러한 과정을 통해 React는 성능을 최적화하고 불필요한 리랜더링을 최소화할 수 있다.
02. 작업환경 준비
- VS Code 설치 (https://code.visualstudio.com/)
- Yarn 설치 (https://yarnpkg.com/en/docs/install)
- Node.js 설치 (https://nodejs.org/)
$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
$ nvm install --lts
- 새 프로젝트 만들어보기
- 터미널에 다음 명령어를 입력하여 새로운 프로젝트 생성
$ npx create-react-app begin-react
- begin-react 디렉터리 생성 및 하위 React 프로젝트 생성
- 이후 cd 명령어를 사용하여 해당 디렉터리에 들어간 다음 yarn start 명령어를 입력(yarn 이 없다면 npm start).
$ cd begin-react
$ yarn start
03. 나의 첫 번째 리액트 컴포넌트
- src 디렉터리에 Hello.js 라는 파일을 다음과 같이 작성
Hello.js
import React from 'react';
function Hello() {
return <div>안녕하세요</div>
}
export default Hello;
- React 컴포넌트를 만들 때는 "import React from 'react';" 를 통하여 React를 불러와야 한다.
- React 컴포넌트는 함수형태로 작성 할 수도 있고 클래스형태로도 작성 할 수 있다.
- 최하단의 "export default hello;" 코드는 Hello 라는 컴포넌트를 내보내겠다는 의미
APP.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
</div>
);
}
export default App;
- 컴포넌트는 재사용이 가능한 각각의 독립된 모듈이다. (재사용 가능한 UI 코드 조각)
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
- 여기서 ReactDOM.render 의 역할은 브라우저에 있는 실제 DOM 내부에 리액트 컴포넌트를 렌더링하겠다는 것을 의
- id 가 root 인 DOM 을 선택, public/index.html 내부의 <div id="root"></div>를 지칭
- React 컴포넌트가 렌더링 될 때에는, React 애플리케이션의 루트 컴포넌트가 주어진 루트 DOM 요소에 렌더링된다.
04. JSX의 기본 규칙 알아보기
- JSX(Javascript Syntax eXtension) 는 리액트에서 생김새를 정의할 때, 사용하는 문법(JavaScript)이다.
- 리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel 이 JSX 를 JavaScript 로 변환을 해준다.
- 아직 지원되지 않는 최신 문법이나, 편의상 사용하거나 실험적인 자바스크립트 문법들을 정식 자바스크립트 형태로 변환해줌으로서 구형 브라우저같은 환경에서도 제대로 실행 할 수 있게 해주는 역할이다.
- 태그는 닫혀 있어야 한다
mport React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
<div>
</div>
);
}
export default App;
- 위 코드와 같이 <div>를 열고 닫지 않으면 컴파일 에러가 난다.
- 태그와 태그 사이에 내용이 들어가지 않을 때에는, Self Closing 태그 라는 것을 사용해야 한다.
- 현재 Hello 컴포넌트를 사용 할 때 Self Closing 태그를 사용
- 두 개 이상의 태그는 무조건 하나의 태그로 감싸져 있어야 한다.
import React from 'react';
import Hello from './Hello';
function App() {
return (
<>
<Hello />
<div>안녕히계세요</div>
</>
);
}
export default App;
- <div>로 감싸기 어려운 경우 React의 Fragment를 사용하여 감쌀 수도 있다.
- 태그를 이름 없이 작성 시 Fragment가 만들어지는데, Fragment는 Browser상에서 별도의 Element로 나타나지 않는다.
- JSX 안에 자바스크립트 값 사용하기
import React from 'react';
import Hello from './Hello';
function App() {
const name = 'react';
return (
<>
<Hello />
<div>{name}</div>
</>
);
}
export default App;
- JSX 내부에 자바스크립트 변수를 보여줘야 할 때에는 {} 으로 감싸서 보여준다.
- style 과 className
- JSX 에서 태그에 style 과 CSS class 를 설정하는 방법은 HTML 에서 설정하는 방법과 다르다.
- 우선, 인라인 스타일은 객체 형태로 작성 해야 하며, camelCase 형태로 네이밍 해야 한다.
- CSS class 를 설정 할 때에는 "class= "가 아닌 "className=" 으로 설정을 해야 한다.
import React from 'react';
import Hello from './Hello';
import './App.css';
function App() {
const name = 'react';
const style = {
backgroundColor: 'black',
color: 'aqua',
fontSize: 24, // 기본 단위 px
padding: '1rem' // 다른 단위 사용 시 문자열로 설정
}
return (
<>
<Hello />
<div style={style}>{name}</div>
<div className="gray-box"></div>
</>
);
}
export default App;
- 주석
- JSX에서 주석은 "{/* 주석 */}" 와 같은 형태로 작성할 수 있다.
- 열리는 태그 내부에서는 "//" 를 사용하여 뒤에 오는 문장을 주석 처리할 수 있다.
05. props를 통해 컴포넌트에게 값 전달하기
- props는 상위 컴포넌트가 하위 컴포넌트에 값을 전달할 때 사용하는 속성
- 상위 컴포넌트가 하위 컴포넌트에 값을 전달하기 때문에 단방향 데이터 흐름을 갖는다.
- 부모 컴포넌트는 수정 가능하지만, 자식 컴포넌트는 읽기만 가능
- props는 properties의 줄임말
- props의 기본 사용법
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" />
);
}
export default App;
Hello.js
import React from 'react';
function Hello(props) {
return <div>안녕하세요 {props.name}</div>
}
export default Hello;
- 컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 할 수 있다.
- props 는 객체 형태로 전달되며, name 값을 조회하고 싶다면 props.name 을 조회하면 된다.
- 여러개의 props, 비구조화 할당
App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" color="red"/>
);
}
export default App;
Hello.js
import React from 'react';
function Hello(props) {
return <div style={{ color: props.color }}>안녕하세요 {props.name}</div>
}
export default Hello;
- Hello 컴포넌트에서 color 값을 조회해서 폰트의 색상으로 설정하는 코드
- 함수의 파라미터에서 비구조화 할당 (혹은 구조 분해) 문법을 사용하면 조금 더 코드를 간결하게 작성 가능
- 비구조화할당이란, 배열이나 객체 속성을 해체하여 개별 변수에 값을 담을 수 있는 JavaScript 표현식을 말한다.
- 위의 코드에서 { color, name }은 객체 구조 분해를 나타낸다.
- 함수의 매개변수로 전달되는 객체에서 color와 name 속성을 추출하여 새로운 변수에 할당한다.
- defaultProps 로 기본값 설정
수정된 Hello.js 코드
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
- 컴포넌트에 props 를 지정하지 않았을 때 기본 값을 설정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정하면 된다.
- props.children
Wrapper.js
import React from 'react';
function Wrapper({ children }) {
const style = {
border: '2px solid black',
padding: '16px',
};
return (
<div style={style}>
{children}
</div>
)
}
export default Wrapper;
App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red"/>
<Hello color="pink"/>
</Wrapper>
);
}
export default App;
- 컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 땐, props.children 을 조회하면 된다.
- 내부의 내용이 보여지게 하기 위해 Wrapper 에서 props.children 을 렌더링해주어야 함.
06. 조건부 렌더링
App.js
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red" isSpecial={true}/>
<Hello color="pink" />
</Wrapper>
)
}
export default App;
Hello.js
import React from 'react';
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{ isSpecial ? <b>*</b> : null }
안녕하세요 {name}
</div>
);
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
- 조건부 렌더링이란, 특정 조건에 따라 다른 결과물을 렌더링 하는 것을 의미
- App 컴포넌트에서 Hello 컴포넌트를 사용 할 때, isSpecial 이라는 props 를 설정
- true 는 자바스크립트 값이기 때문에 중괄호로 감싸줌
- Hello 컴포넌트에서는 isSpecial 이 true 이냐 false 이냐에 따라 컴포넌트의 좌측에 * 출력
- isSpecial 값이 true 라면 <b>*</b> 를, 그렇지 않다면 null 을 보여준다.
- JSX 에서 null, false, undefined 를 렌더링하면 아무것도 나타나지 않는다.
- props 값 설정을 생략하면 ={true}
import React from 'react';
import Hello from './Hello';
import Wrapper from './Wrapper';
function App() {
return (
<Wrapper>
<Hello name="react" color="red" isSpecial />
<Hello color="pink"/>
</Wrapper>
);
}
export default App;
- 컴포넌트의 props 값을 설정하게 될 때 만약 props 이름만 작성하고 값 설정을 생략한다면, 이를 true 로 설정한 것으로 간주한다.
- 위 코드와 같이 isSpecial 이름만 넣어주면 isSpecial={true} 와 동일한 의미
07. useState 를 통해 컴포넌트에서 바뀌는 값 관리하기
Counter.js
import React from 'react';
function Counter() {
const onIncrease = () => {
console.log('+1')
}
const onDecrease = () => {
console.log('-1');
}
return (
<div>
<h1>0</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
App.js
import React from 'react';
import Counter from './Counter';
function App() {
return (
<Counter />
);
}
export default App;
- onIncrease 와 onDecrease 는 화살표 함수를 사용하여 구현
- 함수를 만들고, button 의 onClick 으로 각 함수를 연결
- 리액트에서 엘리먼트에 이벤트를 설정해줄때에는 on이벤트이름={실행하고싶은함수} 형태로 설정해야 한다.
- 동적인 값 끼얹기, useState
useState를 사용한 Counter.js
import React, { useState } from 'react';
function Counter() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(number + 1);
}
const onDecrease = () => {
setNumber(number - 1);
}
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
- 컴포넌트에서 동적인 값을 상태(state)라고 부른다.
- useState 함수를 사용하여 컴포넌트에서 상태 관리 가능
- Setter 함수는 파라미터로 전달 받은 값을 최신 상태로 설정
- 함수형 업데이트
함수형 업데이트 Counter.js
import React, { useState } from 'react';
function Counter() {
const [number, setNumber] = useState(0);
const onIncrease = () => {
setNumber(prevNumber => prevNumber + 1);
}
const onDecrease = () => {
setNumber(prevNumber => prevNumber - 1);
}
return (
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
- onIncrease 와 onDecrease 에서 setNumber 를 사용 할 때 그 다음 상태를 파라미터로 넣어준것이 아니라, 값을 업데이트 하는 함수를 파라미터로 전달
- 함수형 업데이트는 주로 컴포넌트를 최적화 할 때 사용
08. input 상태 관리하기
InputSample.js
import React, { useState } from 'react';
function InputSample() {
const [text, setText] = useState('');
const onChange = (e) => {
setText(e.target.value);
};
const onReset = () => {
setText('');
};
return (
<div>
<input onChange={onChange} value={text} />
<button onClick={onReset}>초기화</button>
<div>
<b>값: {text}</b>
</div>
</div>
);
}
export default InputSample;
App.js
import React from 'react';
import InputSample from './InputSample';
function App() {
return (
<InputSample />
);
}
export default App;
- useState를 사용하여 동적 상태 관리
- Input 의 onChange 라는 이벤트 사용
- 이벤트 등록 함수에서 이벤트 객체 e 를 파라미터로 받아와 사용 할 수 있다.
- 이 객체의 e.target 은 이벤트가 발생한 DOM 인 input DOM 을 가르킨다.
- 이 DOM 의 value 값, 즉 e.target.value 를 조회하면 현재 input 에 입력한 값이 무엇인지 알 수 있다.
09. 여러개의 input 상태 관리하기
InputSample.js
import React, { useState } from 'react';
function InputSample() {
const [inputs, setInputs] = useState({
name: '',
nickname: ''
});
const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출
const onChange = (e) => {
const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
setInputs({
...inputs, // 기존의 input 객체를 복사한 뒤
[name]: value // name 키를 가진 값을 value 로 설정
});
};
const onReset = () => {
setInputs({
name: '',
nickname: '',
})
};
return (
<div>
<input name="name" placeholder="이름" onChange={onChange} value={name} />
<input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
export default InputSample;
- React 상태에서 객체를 수정해야 할 때 "inputs[name] = value;"와 같이 직접 수정하면 안된다.
- 새로운 객체를 생성한 뒤 객체에 변화를 주고, 이를 상태로 사용해야 한다.
반응형
'WINK-(Web & App) > React.js 스터디' 카테고리의 다른 글
[2024 React.js 스터디] 박지민 #4주차 "리액트 입문 1-9" (1) | 2024.05.13 |
---|---|
[2024 React.js 스터디] 정호용 #4주차 "React.JS 맛보기" (0) | 2024.05.13 |
[2024 JS 심화 프론트 스터디] 한승훈 #2주차 (0) | 2024.05.07 |
[2024 JS 심화 프론트 스터디] 류상우 #2주차 (0) | 2024.05.07 |
[2024 JS 심화 프론트 스터디] 김지나 #2주차 "나머지" (0) | 2024.05.06 |