반응형
1. 리액트는 어쩌다 만들어졌을까?
- 처리해야 할 이벤트, 관리해야 할 상태값, DOM까지 다양해지게 된다면 이에 따라 업데이트하는 규칙도 복잡 -> 업데이트 간소화 필요
- 리액트는 어떠한 상태가 바뀌었을때, 그 상태에 따라 DOM을 어떻게 업데이트 할 지 규칙을 정함 X, 아예 다 날려버리고 처음부터 모든걸 새로 만들어서 보여준다는 아이디어에서 시작
- 정말로 동적인 UI를 보여주기 위해서 모든걸 다 날려버리고 새로 만들게 된다면, 속도가 굉장히 느림 -> 리액트에서는 Virtual DOM이라는 것을 사용
- Virtual DOM
- 가상의 DOM으로, 브라우저에 실제로 보여지는 DOM X
- 그냥 메모리에 가상으로 존재하는 DOM - 그냥 JavaScript 객체 -> 작동 성능이 실제로 브러우저에서 DOM을 보여주는 것보다 속도가 훨씬 빠름
- 리액트에서 Virtual DOM이 사용되는 과정
- 리액트는 상태가 업데이트 되면, 업데이트가 필요한 곳의 UI 를 Virtual DOM 을 통해서 렌더링
- 비교 알고리즘을 통하여 실제 브라우저에 보여지고 있는 DOM 과 비교를 한 후, 차이가 있는 곳을 감지하여 이를 실제 DOM 에 패치
2. 작업환경 준비
- Node.js
- Webpack과 Babel 같은 도구들이 자바스크립트 런타임인 Node.js 기반으로 만들어짐
- Webpack, Babel은 무슨 용도?
- 리액트 프로젝트를 만들게 되면서, 컴포넌트를 여러가지 파일로 분리해서 저장 & 이 컴포넌트는 일반 자바스크립트가 아닌 JSX 라는 문법으로 작성
- 여러가지의 파일을 한개로 결합하기 위해서 Webpack 사용
- JSX 를 비롯한 새로운 자바스크립트 문법들을 사용하기 위해서 Babel 사용
- Yarn
- 조금 개선된 버전의 npm
- Node.js를 설치하게 될 때 같이 딸려오는 패키지 매니저 도구
- 프로젝트에서 사용되는 라이브러리를 설치 & 해당 라이브러리들의 버전 관리
- 만약 npm 이 이미 익숙하고, yarn 을 설치하기 귀찮다면 생략해도 상관 X
- 코드 에디터
- ex) VSCode, Atom, WebStorm, Sublime
- Git bash
- 윈도우의 경우, Git for Windows 를 설치해서 앞으로 터미널에 무엇을 입력하라는 내용이 있으면 함께 설치되는 Git Bash 를 사용
- 윈도우가 아니라면 설치 X 상관없음
3. 나의 첫번째 리액트 컴포넌트
- 리액트 컴포넌트는 함수형태 & 클래스 형태로도 작성 가능 (이 글의 단계에서는 함수로 작성하는 방법에 대해서만 알아봄)
- JSX: 리액트 컴포넌트에서는 XML 형식의 값을 반환 가능
// Hello.js
import React from 'react'; // 리액트 컴포넌트를 만들 때 리액트 불러오기
function Hello() {
return <div>안녕하세요</div>
}
export default Hello; // Hello 라는 컴포넌트를 내보내겠다는 의미 - 다른 컴포넌트에서 불러와서 사용 가능
// App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<div>
<Hello /> // 컴포넌트는 일종의 UI 조각 - 쉽게 재사용 가능
<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 을 선택하고 있는데, 이 DOM은 public/index.html 내부에 존재
- 결국, 리액트 컴포넌트가 렌더링 될 때에는, 렌더링된 결과물이 위 div 내부에 렌더링되는 것
4. JSX
- 리액트에서 생김새를 정의할 때, 사용하는 문법 - HTML 같이 생겼지만 실제로는 JavaScript
- 리액트 컴포넌트 파일에서 XML 형태로 코드를 작성하면 babel 이 JSX 를 JavaScript 로 변환
- Babel 은 자바스크립트의 문법을 확장해주는 도구 - 편의상 사용하거나 실험적인 자바스크립트 문법들을 정식 자바스크립트 형태로 변환해줌으로서 구형 브라우저같은 환경에서도 제대로 실행 할 수 있게 해주는 역할
- JSX 가 JavaScript 로 제대로 변환이 되려면 지켜주어야 하는 규칙
- 꼭 닫혀야 하는 태그
- 태그를 열었으면 꼭, <div></div> 이렇게 닫아주어야 함
- 태그와 태그 사이에 내용이 들어가지 않을 때에는, Self Closing 태그 라는 것을 사용 - 열리고, 바로 닫히는 태그를 의미
- 꼭 감싸져야 하는 태그
- 두 개 이상의 태그는 무조건 하나의 태그로 감싸져 있어야 함
- 스타일 관련 설정을 하다가 복잡해지게 되는 상황도 올 수 있고, table 관련 태그를 작성 할 때에도 내용을 div 같은걸로 감싸기엔 애매 - 그럴 땐, 리액트의 Fragment 라는 것을 사용
- 태그를 작성 할 때 이름 없이 작성을 하게 되면 Fragment 만들어짐 - 브라우저 상에서 따로 별도의 엘리먼트로 나타나지 않음
- JSX 안에 자바스크립트 값 사용하기
- JSX 내부에 자바스크립트 변수를 보여줘야 할 때에는 {} 으로 감싸서 보여줌
- sytle과 className
- 인라인 스타일은 객체 형태로 작성 -> - 로 구분되어 있는 이름들은 camelCase 형태로 네이밍
- ex) background-color -> backgroundColor
- CSS class 를 설정 할 때에는 class= 가 아닌 className= 으로 설정
- 인라인 스타일은 객체 형태로 작성 -> - 로 구분되어 있는 이름들은 camelCase 형태로 네이밍
- 주석
- JSX 내부의 주석은 {/* 이런 형태로 */} 작성
- 열리는 태그 내부에서는 // 주석 처럼 작성 가능
- 꼭 닫혀야 하는 태그
5. props를 통해 컴포넌트에게 값 전달하기
- props 는 properties 의 줄임말
- props의 기본 사용법
- App 컴포넌트에서 Hello 컴포넌트를 사용 할 때 name 이라는 값을 전달해주고 싶다고 가정
// App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" />
);
}
export default App;
- Hello 컴포넌트에서 name 값을 사용 하고 싶을 때
// Hello.js
import React from 'react';
function Hello(props) {
return <div>안녕하세요 {props.name}</div>
}
export default Hello;
- 컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 X
- props 는 객체 형태로 전달 -> 만약 name 값을 조회하고 싶다면 props.name 을 조회
- 여러개의 props, 비구조화 할당
- Hello 컴포넌트에 또 다른 props 를 전달
// App.js
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" color="red"/>
);
}
export default App;
- Hello 컴포넌트에서 color 값을 조회해서 폰트의 색상으로 설정
// Hello.js
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
export default Hello;
- props 내부의 값을 조회할 때마다 props. 을 입력하고 있다. 함수의 파라미터에서 비구조화 할당(혹은 구조 분해) 문법을 사용 -> 조금 더 코드를 간결하게 작성 가능
// Hello.js
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
export default Hello;
- defaultProps로 기본값 설정
- 컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용 할 값을 설정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정
// Hello.js
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
- App 에서 name 이 없는 Hello 컴포넌트를 렌더링 -> "이름없음"이 name으로 뜸
- props.children
- 컴포넌트 태그 사이에 넣은 값을 조회하고 싶을 땐, props.children 을 조회
- Wrapper 태그 내부에 Hello 컴포넌트를 넣고 브라우저 확인하면 컴포넌트 보임 X -> 내부의 내용이 보여지게 하기 위해서는 Wrapper에서 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;
6. 조건부 렌더링
- 특정 조건에 따라 다른 결과물을 렌더링 하는 것
- App 컴포넌트에서 Hello 컴포넌트를 사용 할 때, isSpecial 이라는 props 를 설정
// 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}/> // true는 자바스크립트 값 -> 중괄호로 감싸줌
<Hello color="pink" />
</Wrapper>
)
}
export default App;
- Hello 컴포넌트에서는 isSpecial 이 true 이냐 false 이냐에 따라서 컴포넌트의 좌측에 * 표시 - 삼항연산자 사용
// 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;
- isSpecial 값이 true 라면 <b>*</b> 를, 그렇지 않다면 null 보여줌
- 참고로 JSX 에서 null, false, undefined 를 렌더링하게 된다면 아무것도 나타나지 않음
- 보통 삼항연산자를 사용한 조건부 렌더링을 주로 특정 조건에 따라 보여줘야 하는 내용이 다를 때 사용
- 단순히 특정 조건이 true 이면 보여주고, 그렇지 않다면 숨겨주는 상황 -> && 연산자를 사용해서 처리하는 것이 더 간편
function Hello({ color, name, isSpecial }) {
return (
<div style={{ color }}>
{isSpecial && <b>*</b>}
안녕하세요 {name}
</div>
);
}
- isSpecial && <b>*</b> 의 결과는 isSpecial 이 false 일땐 false 이고, isSpecial이 true 일 땐 <b>*</b>
- props 값 설정을 생략하면 ={true}
- 컴포넌트의 props 값을 설정하게 될 때 만약 props 이름만 작성하고 값 설정을 생략한다면, 이를 true 로 설정한 것으로 간주
// App.js
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;
- isSpecial 이름만 넣어주면 isSpecial={true} 와 동일한 의미
7. useState를 통해 컴포넌트에서 바귀는 값 관리하기
- 컴포넌트에서 보여줘야 하는 내용이 사용자 인터랙션에 따라 바뀌어야 할 때 어떻게 구현할 수 있는가
- 리액트 16.8 에서 Hooks 라는 기능이 도입되면서 함수형 컴포넌트에서도 상태를 관리 가능
- ex) 버튼을 누르면 숫자가 바뀌는 Counter 컴포넌트
// Counter.js
import React from 'react';
function Counter() {
return (
<div>
<h1>0</h1>
<button>+1</button>
<button>-1</button>
</div>
);
}
export default Counter;
- 이벤트 설정: Counter 에서 버튼이 클릭되는 이벤트가 발생 했을 때, 특정 함수가 호출되도록 수정
// Counter.js
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>
);
}
- onIncrease 와 onDecrease 는 화살표 함수를 사용하여 구현
- 함수를 만들고, button 의 onClick 으로 각 함수를 연결 - 리액트에서 엘리먼트에 이벤트를 설정해줄때에는 on이벤트이름={실행하고싶은함수} 형태로 설정
- 동적인 값 끼얹기, useState
- 컴포넌트에서 동적인 값을 상태(state)
- 리액트에 useState 라는 함수가 있는데요, 이것을 사용하면 컴포넌트에서 상태를 관리 가능
// Counter.js
import React, { useState } from 'react'; // 리액트 패키지에서 useState 함수 불러옴
function Counter() {
// 상태의 기본값을 파라미터로 넣어서 호출
const [number, setNumber] = useState(0); // 배열 반환: 첫번째 원소는 현재 상태, 두번째 원소는 Setter 함수
// 비구조화 할당을 통화여 각 원소를 추출
// Setter 함수는 파라미터로 전달 받은 값을 최신 상태로 설정
const onIncrease = () => {
setNumber(number + 1);
}
const onDecrease = () => {
setNumber(number - 1);
}
return (
<div>
<h1>{number}</h1> // h1 태그에서는 이제 0 이 아닌 {number} 값을 보여주어야 함
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
);
}
export default Counter;
- 함수형 업데이트
- 지금은 Setter 함수를 사용 할 때, 업데이트 하고 싶은 새로운 값을 파라미터로 넣어주고 있음 - 그 대신에 기존 값을 어떻게 업데이트 할 지에 대한 함수를 등록하는 방식으로도 값을 업데이트 가능
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를 사용 할 때 그 다음 상태를 파라미터로 넣어준것이 아니라, 값을 업데이트 하는 함수를 파라미터로 넣어줌
- 함수형 업데이트는 주로 나중에 컴포넌트를 최적화를 하게 될 때 사용
8. input 상태 관리하기
// InputSample.js
import React from 'react';
function InputSample() {
return (
<div>
<input />
<button>초기화</button>
<div>
<b>값: </b>
</div>
</div>
);
}
export default InputSample;
- input 에 입력하는 값이 하단에 나타나게 하고, 초기화 버튼을 누르면 input 이 값이 비워지도록 구현할 것
- input 의 onChange 라는 이벤트를 사용 - 이벤트에 등록하는 함수에서는 이벤트 객체 e 를 파라미터로 받아와서 사용 할 수 있는데 이 객체의 e.target 은 이벤트가 발생한 DOM 인 input DOM 을 가르킴
- 이 DOM 의 value 값, 즉 e.target.value 를 조회하면 현재 input 에 입력한 값이 무엇인지 알 수 있음
- 이 값을 useState 를 통해서 관리
// InputSample.js 수정
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>
);
}
- input 의 상태를 관리할 때에는 input 태그의 value 값도 설정해주는 것이 중요
- 그렇게 해야, 상태가 바뀌었을때 input 의 내용도 업데이트
9. 여러개의 input 상태 관리하기
// InputSample.js
import React, { useState } from 'react';
function InputSample() {
const onChange = (e) => {
};
const onReset = () => {
};
return (
<div>
<input placeholder="이름" />
<input placeholder="닉네임" />
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
이름 (닉네임)
</div>
</div>
);
}
export default InputSample;
- input 의 개수가 여러개가 됐을때는, 단순히 useState 를 여러번 사용하고 onChange 도 여러개 만들어서 구현 가능
- 더 좋은 방법은, input 에 name 을 설정하고 이벤트가 발생했을 때 이 값을 참조하는 것
- useState 에서는 문자열이 아니라 객체 형태의 상태를 관리
// InputSample.js 수정
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>
);
}
- 리액트 상태에서 객체를 수정해야 할 때에는, inputs[name] = value; 처럼 직접 수정 X
- 그 대신, 새로운 객체를 만들어서 새로운 객체에 변화를 주고, 이를 상태로 사용
setInputs({
...inputs,
[name]: value
});
- 여기서 사용한 ... 문법은 spread 문법: 객체의 내용을 모두 "펼쳐서" 기존 객체를 복사
- 이러한 작업을, "불변성을 지킨다" 라고 부름 - 불변성을 지켜주어야만 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리렌더링이 진행
- 기존 상태를 직접 수정하게 되면, 값을 바꿔도 리렌더링 X
- 추가적으로, 리액트에서는 불변성을 지켜주어야만 컴포넌트 업데이트 성능 최적화를 제대로 가능
반응형
'WINK-(Web & App) > React.js 스터디' 카테고리의 다른 글
[2024 React.js 스터디] 류상우 #4주차 (0) | 2024.05.14 |
---|---|
[2024 React.js 스터디] 김지나 #4주차 "리액트 1~9" (0) | 2024.05.14 |
[2024 React.js 스터디] 정호용 #4주차 "React.JS 맛보기" (0) | 2024.05.13 |
[2024 React.js 스터디] 박건민 #4주차 (0) | 2024.05.12 |
[2024 JS 심화 프론트 스터디] 한승훈 #2주차 (0) | 2024.05.07 |