Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jeonggoncho/react-practice-diary
๐ React๋ฅผ ์ด์ฉํ์ฌ ์ ์ํ 'Diary ํด๋ก ์ฝ๋ฉ'์
๋๋ค.
https://github.com/jeonggoncho/react-practice-diary
clone-coding javascript jsx react
Last synced: 5 days ago
JSON representation
๐ React๋ฅผ ์ด์ฉํ์ฌ ์ ์ํ 'Diary ํด๋ก ์ฝ๋ฉ'์ ๋๋ค.
- Host: GitHub
- URL: https://github.com/jeonggoncho/react-practice-diary
- Owner: JeonggonCho
- Created: 2024-01-05T05:21:59.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2024-01-26T14:33:40.000Z (9 months ago)
- Last Synced: 2024-10-10T23:54:31.901Z (26 days ago)
- Topics: clone-coding, javascript, jsx, react
- Language: JavaScript
- Homepage:
- Size: 11.6 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ์ผ๊ธฐ์ฅ
๋ณธ ํ๋ก์ ํธ๋ ["ํ์ ํฌ๊ธฐ๋ก ์๋ผ ๋จน๋ ๋ฆฌ์กํธ ๊ฐ์"](https://www.udemy.com/course/winterlood-react-basic/)์ "์ผ๊ธฐ์ฅ ๋ง๋ค์ด ๋ณด๊ธฐ"๋ฅผ ํด๋ก ํ ํ๋ก์ ํธ๋ก "๋ฆฌ์กํธ"์ ๊ธฐ์ด ์ง์์ ํ์ตํ๋ ๊ฒ์ ๋ชฉํํ์์ต๋๋ค.
## ๋ชฉ์ฐจ
1. [React์์ ์ฌ์ฉ์ ์ ๋ ฅ ์ฒ๋ฆฌ - useState](#1-react์์-์ฌ์ฉ์-์ ๋ ฅ-์ฒ๋ฆฌ---usestate)
2. [React์์ DOM ์กฐ์ํ๊ธฐ - useRef](#2-react์์-dom-์กฐ์ํ๊ธฐ---useref)
3. [React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ1 - ๋ฆฌ์คํธ ๋ ๋๋ง(์กฐํ)](#3-react์์-๋ฆฌ์คํธ-์ฌ์ฉํ๊ธฐ1---๋ฆฌ์คํธ-๋ ๋๋ง์กฐํ)
4. [React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ2 - ๋ฐ์ดํฐ ์ถ๊ฐ](#4-react์์-๋ฆฌ์คํธ-์ฌ์ฉํ๊ธฐ2---๋ฐ์ดํฐ-์ถ๊ฐ)
5. [React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ3 - ๋ฐ์ดํฐ ์ญ์ ](#5-react์์-๋ฆฌ์คํธ-์ฌ์ฉํ๊ธฐ3---๋ฐ์ดํฐ-์ญ์ )
6. [React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ4 - ๋ฐ์ดํฐ ์์ ](#6-react์์-๋ฆฌ์คํธ-์ฌ์ฉํ๊ธฐ4---๋ฐ์ดํฐ-์์ )
7. [React Lifecycle ์ ์ดํ๊ธฐ - useEffect](#7-react-lifecycle-์ ์ดํ๊ธฐ---useeffect)
8. [React์์ API ํธ์ถํ๊ธฐ](#8-react์์-api-ํธ์ถํ๊ธฐ)
9. [์ต์ ํ1 ์ฐ์ฐ ๊ฒฐ๊ณผ ์ฌ์ฌ์ฉ - useMemo](#9-์ต์ ํ1-์ฐ์ฐ-๊ฒฐ๊ณผ-์ฌ์ฌ์ฉ---usememo)
10. [์ต์ ํ2 ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ - React.memo](#10-์ต์ ํ2-์ปดํฌ๋ํธ-์ฌ์ฌ์ฉ---reactmemo)
11. [์ต์ ํ3 ์ปดํฌ๋ํธ & ํจ์ ์ฌ์ฌ์ฉ - useCallback](#11-์ต์ ํ3-์ปดํฌ๋ํธ--ํจ์-์ฌ์ฌ์ฉ---usecallback)
12. [์ต์ ํ4 - React.memo + useCallback](#12-์ต์ ํ4---reactmemo--usecallback)
13. [๋ณต์กํ ์ํ ๊ด๋ฆฌ ๋ก์ง ๋ถ๋ฆฌํ๊ธฐ - useReducer](#13-๋ณต์กํ-์ํ-๊ด๋ฆฌ-๋ก์ง-๋ถ๋ฆฌํ๊ธฐ---usereducer)
14. [์ปดํฌ๋ํธ ํธ๋ฆฌ์ ๋ฐ์ดํฐ ๊ณต๊ธํ๊ธฐ - Context](#14-์ปดํฌ๋ํธ-ํธ๋ฆฌ์-๋ฐ์ดํฐ-๊ณต๊ธํ๊ธฐ---context)
## ํ์ต๋ด์ฉ
- ์ฌ์ฉ์ ์ ๋ ฅ ๋ฐ ๋ฐฐ์ด ๋ฆฌ์คํธ ์ฒ๋ฆฌํ๊ธฐ
- React Lifecycle๊ณผ API
- React App ํ๋ก์ฒ๋ผ ์ฑ๋ฅ ์ต์ ํํ๊ธฐ with ๋๊ตฌ ์ฌ์ฉ
- React ์ปดํฌ๋ํธ ํธ๋ฆฌ์ ์ ์ญ ๋ฐ์ดํฐ ๊ณต๊ธํ๊ธฐ
## 1. React์์ ์ฌ์ฉ์ ์ ๋ ฅ ์ฒ๋ฆฌ - useState
### 1-1. ์ฌ์ ์ค๋น
- `npx create-react-app emotional_diary` ์ ๋ ฅ์ผ๋ก ํ๋ก์ ํธ ์์ฑ
- ํ๋ก์ ํธ ํด๋์ ์ปจํ ์ธ ๋ค์ ํ ๋จ๊ณ ์์ ํด๋๋ก ์ด๋์ํค๊ณ ๊ธฐ์กด ์์ฑ ํด๋ ์ญ์
- ์ ์ฌ์ฉ๋์ง ์๋ ํ์ผ๋ค ์ ๋ฆฌ (logo.svg, App.test.js, reactWebVitals.js, setupTest.js)
### 1-2. ๋ชฉํ
![๋ค์ํ ์ฌ์ฉ์ ์ ๋ ฅ ์ฒ๋ฆฌํ๊ธฐ](./README_img/๋ค์ํ_์ฌ์ฉ์_์ ๋ ฅ_์ฒ๋ฆฌํ๊ธฐ.png)
- `DiaryEditor`๋ผ๋ ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ
- ํ ์ค ์ ๋ ฅ ์ฒ๋ฆฌํ๊ธฐ (input)
- ์ฌ๋ฌ ์ค ์ ๋ ฅ ์ฒ๋ฆฌํ๊ธฐ (textarea)
- ์ ํ ๋ฐ์ค ์ ๋ ฅ ์ฒ๋ฆฌํ๊ธฐ (select)
- ์ฌ์ฉ์ ์ ๋ ฅ ๋ฐ์ดํฐ ํธ๋ค๋งํ๊ธฐ
### 1-3. DiaryEditor ์ปดํฌ๋ํธ๊ฐ ํ์ํ ๊ฒ
![DiaryEditor ์ปดํฌ๋ํธ๊ฐ ํ์ํ ๊ฒ](./README_img/DiaryEditor_์ปดํฌ๋ํธ๊ฐ_ํ์ํ_๊ฒ.png)
- ์์ฑ์
- ์ผ๊ธฐ ๋ณธ๋ฌธ
- ๊ฐ์ ์ ์
### 1-4. ์์ฑ์ ๋ฐ ์ผ๊ธฐ ๋ณธ๋ฌธ ์ ๋ ฅ๋ฐ๊ธฐ
### - ์์ฑ์ ์ ๋ ฅ๋ฐ๊ธฐ
- useState(์ํ)์ input ํ๊ทธ ํ์ฉ
```jsx
// ์์ฑ์ ์ ๋ ฅ๋ฐ๋ ์ฝ๋import { useState } from "react";
const [author, setAuthor] = useState("");
;
{
setAuthor(e.target.value);
}}
/>
```- input ํ๊ทธ์ ๊ฐ์ useState์ author๋ก ์ค์
- onChange ์์ฑ์ ํตํด ๊ฐ์ด ๋ฐ๋ ๋๋ง๋ค ์ด๋ฒคํธ๊ฐ ๋ฐ์
- ์ด๋ฒคํธ ๋ฐ์ ์, ์ฝ๋ฐฑํจ์๋ฅผ ์ํํ๊ณ ๋งค๊ฐ๋ณ์๋ก ์ด๋ฒคํธ ๊ฐ์ฒด e๋ฅผ ๋ณด๋
- ํจ์ ๋ด๋ถ์์ ์ํ ๋ณํ ํจ์ setAuthor๊ฐ ์ด๋ฒคํธ ํ๊ฒ ๊ฐ(e.target.value)๋ฅผ ๋ฐ์ author๋ก ๋ณด๋
- ๋ฐ๋ author ๊ฐ์ด ๋ค์ input ํ๊ทธ์ value๋ก ๋ค์ด๊ฐ์ ํ๋ฉด์ ๋ ๋๋ง๋จ
### - ์ผ๊ธฐ ๋ณธ๋ฌธ ์ ๋ ฅ ๋ฐ๊ธฐ
- ์์ ์์ฑ์ ์ ๋ ฅ๋ฐ๊ธฐ์ ๋์ผ
- input ํ๊ทธ ๋์ textarea ํ๊ทธ ํ์ฉ```jsx
// ์ผ๊ธฐ ๋ณธ๋ฌธ ์ ๋ ฅ๋ฐ๋ ์ฝ๋import { useState } from "react";
const [content, setContent] = useState("");
;
{
setContent(e.target.value);
}}
/>
```- ์์ฑ์ ์ ๋ ฅ๊ณผ ์ผ๊ธฐ ๋ณธ๋ฌธ ์ ๋ ฅ ๋ชจ๋ ๋ฌธ์์ด ์ํ ๊ฐ์ ๊ฐ์ง
- onChange ์์ฑ์ ์ด์ฉ
- ์ํ ๋ณํ ํจ์์ e.target.value๋ฅผ ์ ๋ฌ
### - ๋น์ทํ ๋์์ State ๋ฌถ๊ธฐ
- ์์ ์์ฑ์ ์ ๋ ฅ, ์ผ๊ธฐ ๋ณธ๋ฌธ ์ ๋ ฅ๊ณผ ๊ฐ์ด ์ ์ฌํ๊ฒ ๋์ํ๋ State๋ ๋ฐ๋ก ๋์ง ์๊ณ ํ๋์ State๋ก ๋ฌถ์ด ์ค ์ ์์
```jsx
// State ๋ฌถ๊ธฐimport { useState } from "react";
const [state, setState] = useState({
author: "",
content: "",
});;
{
setState({
...state,
author: e.target.value,
// content: state.content,
});
}}
/>
{
setState({
...state,
// author: state.author,
content: e.target.value,
});
}}
/>
```- ์ด๊ธฐ์ author์ ๊ฐ ๊ณต๋ฐฑ(""), content๋ ๊ณต๋ฐฑ("")์
- ์ดํ ๊ฐ์ด ๋ณํํ๋ฉด ๋ณํํ๋ ๊ฐ์ e.target.value๋ก ์ด๋ฒคํธ๋ฅผ ํตํด ๋ณํํ ๊ฐ์ ์ํ ๋ณํ ํจ์์ ์ ๋ฌ
- ๊ฐ์ด ๋ฌถ์ฌ์์ง๋ง ๊ฐ์ด ๋ณํํ์ง ์์ ๊ฐ์ state.(์๋ ๊ฐ)์ผ๋ก ํจ๊ป ๊ฐ์ฒด๋ก ์ ๋ฌ
- ํ์ง๋ง ๋ ๋ง์ ๊ฐ์ฒด๊ฐ ํ๋์ State์ ๋ฌถ์ฌ์์ ๊ฒฝ์ฐ, ์ํ ๋ณํ ํจ์๋ก ์ ๋ฌํ๋ ๊ฐ์ฒด๊ฐ ๊ธธ์ด์ง ์ ์์
- ๋ฐ๋ผ์ Spread ์ฐ์ฐ์(...)๋ฅผ ํ์ฉํ์ฌ ๋ฐ๋๋ ๊ฐ ์ธ์๋ ...state๋ก ์ฒ๋ฆฌํ ์ ์์
- ์ฃผ์์ฌํญ : ๊ธฐ์กด์ ๊ฐ์ฒด(...state)๋ฅผ ์์ ์์ ํด์ผ ํจ
- ๋ค์ ์์ ํ ๊ฒฝ์ฐ, ์ ๋ฐ์ดํธ ์์๊ฐ ๋ฐ๋๊ธฐ์ ์๋ชป๋ ๊ฒฐ๊ณผ ๋ฐ์
### - onChange ์์ฑ ํฉ์น๊ธฐ
```jsx
// onChange ์์ฑ์ ์ฝ๋ฐฑํจ์ ๋ฌถ์ด๋ด๊ธฐconst handleChangeState = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};;
```- '[e.target.name]: e.target.value'๋ฅผ ํตํด ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ํ๊ทธ๋ฅผ ๊ตฌ๋ถํจ
### 1-5. ๊ฐ์ ์ ์ ์ ํ
- useState์ select ํ๊ทธ(+ option ํ๊ทธ)๋ฅผ ํ์ฉ
- ์์ input๊ณผ textarea์ ์์ฑ๊ณผ ๋๊ฐ์ด ์ ์ฉ๋จ```jsx
// ์ ํ ์ ๋ ฅconst [state, setState] = useState({
author: "",
content: "",
emotion: 1,
});const handleChangeState = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
});
};;
1
2
3
4
5
```
### 1-6. ์ ์ฅ ๋ฒํผ
```jsx
// DiaryEditor.jsconst handleSubmit = () => {
console.log(state);
alert("์ ์ฅ ์ฑ๊ณต");
};;
์ผ๊ธฐ ์ ์ฅํ๊ธฐ
```- ๋ฒํผ ํด๋ฆญ ์, ํด๋ฆญํ๊ธฐ ๋๋ฌธ์ button์ onClick ์์ฑ์ handleSubmit ํจ์๊ฐ ์คํ
- ์ฝ์๋ก ํ์ฌ ์์ฑ๋ state ๊ฐ์ฒด๋ฅผ ์ถ๋ ฅ
- alert ๋ฉ์๋๋ก ์ ์ฅ ์ฑ๊ณต ์๋ฆผ ๋์ฐ๊ธฐ
## 2. React์์ DOM ์กฐ์ํ๊ธฐ - useRef
### 2-1. ๋ชฉํ
- ๋ฆฌ์กํธ์์ DOM ์กฐ์ํ๊ธฐ
- ์ผ๊ธฐ ์ ์ฅ ๋ฒํผ ํด๋ฆญ ์, ์์ฑ์์ ์ผ๊ธฐ๊ฐ ์ ์์ ์ผ๋ก ์ ๋ ฅ๋์๋์ง ํ์ธ
- ์ ์์ ์ธ ์ ๋ ฅ ์๋๋ผ๋ฉด focusํ๊ธฐ
### 2-2. ์ ์์ ์ธ ์ ๋ ฅ์ด ์๋ ๊ฒฝ์ฐ, alert ๋์ฐ๊ธฐ
- handleSubmit ์์ ํ๊ธฐ
- ์กฐ๊ฑด๋ฌธ์ผ๋ก ์์ฑ์์ ๋ณธ๋ฌธ๋ด์ฉ์ ๊ธธ์ด์ ๋ฐ๋ผ alert ๋์ฐ๊ธฐ
- alert ์คํ ํ, ์ดํ ์ฝ๋๋ฅผ ์คํํ์ง ์๋๋ก return ์ถ๊ฐ```jsx
// DiaryEditor.jsconst handleSubmit = () => {
if (state.author.length < 1) {
alert("์์ฑ์๋ ์ต์ 1๊ธ์ ์ด์ ์ ๋ ฅํด์ฃผ์ธ์");
return;
}if (state.content.length < 5) {
alert("์ผ๊ธฐ ๋ณธ๋ฌธ์ ์ต์ 5๊ธ์ ์ด์ ์ ๋ ฅํด์ฃผ์ธ์");
return;
}alert("์ ์ฅ ์ฑ๊ณต");
};
```- ํ์ง๋ง, ์ ๋ ฅ์ด ์ ์์ด ์๋๋๋ผ๋ `alert`๋ฅผ ๋์ฐ๋ ๊ฒ์ `UX ๊ฒฝํ์ ์ผ๋ก ์ข์ง ์์`
### 2-3. ์ ์์ ์ธ ์ ๋ ฅ์ด ์๋ ๊ฒฝ์ฐ, focus ์ฃผ๊ธฐ
- `useRef` ์ฌ์ฉ
```jsx
// useRef import ํด์ค๊ธฐ
import {useRef, useState} from "react";// useRef ์ฌ์ฉํ์ฌ DOM ์์ ์ ๊ทผ ๊ฐ๋ฅํ ๊ธฐ๋ฅ ๋ถ์ฌ
const authorInput = useRef();
const contentInput = useRef();// ์กฐ๊ฑด๋ฌธ์ ๋ฐ๋ผ ๋ถ๋ง์กฑ ์, ํด๋น ํ์ฌ์์์ focusํ๊ธฐ
const handleSubmit = () => {
if (state.author.length < 1) {
authorInput.current.focus();
return;
}if (state.content.length < 5) {
contentInput.current.focus();
return;
}alert("์ ์ฅ ์ฑ๊ณต");
};
```- useRef()๋ฅผ ์ง์ ํ๋ฉด ํด๋น ๋ณ์๋ `mutableRefObject`๊ฐ ๋จ
- mutableRefObject : HTML DOM ์์์ ์ ๊ทผํ ์ ์๋ ๊ธฐ๋ฅ
![๋ฆฌ์กํธ DOM ์กฐ์](README_img/๋ฆฌ์กํธ_DOM_์กฐ์ํ๊ธฐ.gif)
<๋ฆฌ์กํธ DOM ์กฐ์ ๊ฒฐ๊ณผ>
## 3. React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ1 - ๋ฆฌ์คํธ ๋ ๋๋ง(์กฐํ)
### 3-1. ๋ชฉํ
- `DiaryList` ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ
- `๋ฐฐ์ด`์ ์ด์ฉํ์ฌ list ๋ ๋๋ง ํด๋ณด๊ธฐ
- ๊ฐ๋ณ์ ์ธ ์ปดํฌ๋ํธ ๋ง๋ค์ด๋ณด๊ธฐ
### 3-2. ๋๋ฏธ๋ฐ์ดํฐ ๋ง๋ค๊ณ ๋ฆฌ์คํธ ์ปดํฌ๋ํธ์ props๋ก ๋ณด๋ด๊ธฐ
```jsx
// App.jsconst dummyList = [
{
id: 1,
author: "์กฐ์ ๊ณค",
content: "ํ์ด~1",
emotion: 5,
created_date: new Date().getTime(),
},
{
id: 2,
author: "๊น์ฒ ์",
content: "ํ์ด~2",
emotion: 2,
created_date: new Date().getTime(),
},
{
id: 3,
author: "์ด์์",
content: "ํ์ด~3",
emotion: 3,
created_date: new Date().getTime(),
},
];;
```- `dummyList ๋ฐฐ์ด`์ ๋ฐ์ดํฐ(id, ์์ฑ์, ๋ด์ฉ, ๊ฐ์ ์ ์, ์์ฑ์๊ฐ)๋ฅผ `๊ฐ์ฒดํ์ `์ผ๋ก ๋ด์ ๋ง๋ค๊ธฐ
- `DiaryList` ์ปดํฌ๋ํธ์ diaryList ์์ฑ์ผ๋ก dummyList ๋ฐฐ์ด props๋ก ๋ณด๋ด๊ธฐ
```jsx
// DiaryList.jsconst DiaryList = ({ diaryList }) => {
return (
์ผ๊ธฐ ๋ฆฌ์คํธ
{diaryList.length}๊ฐ์ ์ผ๊ธฐ๊ฐ ์์ต๋๋ค.
{diaryList.map((it, idx) => (
์์ฑ์ : {it.author}
์ผ๊ธฐ : {it.content}
๊ฐ์ : {it.emotion}
์์ฑ ์๊ฐ(ms) : {it.created_date}
))}
);
};DiaryList.defaultProps = {
diaryList: [],
};export default DiaryList;
```- DiaryList ์ปดํฌ๋ํธ์์ diaryList๋ฅผ props๋ก ๋ฐ๊ธฐ
- `map ๋ฉ์๋`๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฐ์ด ์์ฑ
- map ๋ฉ์๋์ ์ฝ๋ฐฑํจ์๋ก ๋ฐฐ์ด ์ํํ์ฌ ๊ฐ์ฒด ๋ฐ์ดํฐ ๊ฐ๊ฐ์ํ๊ทธ๋ฅผ ์์ฑํ๊ณ ์ต์์ํ๊ทธ์ ๋ด๊ธฐ
- ์ด ๊ฒฝ์ฐ, ์์ฑ๋ ๋ฐฐ์ด ๊ฐ์ ํค๊ฐ ์ง์ ๋์ด์์ง ์์ ์ฝ์ ์๋ฌ๊ฐ ๋ฐ์
- ๋ฐ๋ผ์ ์ต์์์์์ `key ์์ฑ`์ ๊ฐ๊ฐ์ ๋ฐฐ์ด์ ๊ตฌ๋ถํ ์ ์๋ `id`๋ฅผ ๋ฃ์ด ์๋ฌ ํด๊ฒฐ
- ์ฝ๋ฐฑํจ์์ `๋ ๋ฒ์งธ ์ธ์์ธ ์ธ๋ฑ์ค(idx)`๋ฅผ ๋ฐ์ id ๋์ key ์์ฑ์ ๋ฃ์ด ์ฌ์ฉํ ์ ์์
- ํ์ง๋ง ์ถํ ์์๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ, ์์ ๋ฐ ์ญ์ ์ ๋์ ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ด id๊ฐ ๊ฐ์ฒด์ ์๋ค๋ฉด id๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์งํฅํจ
- map ๋ฉ์๋ ์์ ์์๋ ๊ณ์ ๋ฐ๋ณต๋๋ ์์๋ก ๋ ๋ฆฝ์ ์ธ ์ปดํฌ๋ํธ๋ก ๊ด๋ฆฌํ ์ ์์
### 3-3. ๋ฆฌ์คํธ์ ์์ดํ ์ ๊ด๋ฆฌํ ์ปดํฌ๋ํธ ์์ฑ
```jsx
// DiaryList.jsimport DiaryItem from "./DiaryItem";
const DiaryList = ({ diaryList }) => {
return (
์ผ๊ธฐ ๋ฆฌ์คํธ
{diaryList.length}๊ฐ์ ์ผ๊ธฐ๊ฐ ์์ต๋๋ค.
{diaryList.map((it, idx) => (
))}
);
};
```- DiaryItem ์ปดํฌ๋ํธ๋ฅผ ๋ฐ์ ์ฝ๋ฐฑํจ์์์ ์ฌ์ฉ
- ๋์ผํ๊ฒ `key ์์ฑ`์ผ๋ก `id ๊ฐ`์ ์ฌ์ฉํ๊ณ , ๋๋จธ์ง ๋ฐ์ดํฐ๋ฅผ `Spread ์ฐ์ฐ์(...)`๋ฅผ ์ฌ์ฉํ์ฌ `props`๋ก ๋ณด๋ด๊ธฐ
```jsx
// DiaryItem.jsconst DiaryItem = ({ author, content, created_date, emotion, id }) => {
return (
์์ฑ์ : {author} | ๊ฐ์ ์ ์ : {emotion}
{new Date(created_date).toLocaleString()}
{content}
);
};export default DiaryItem;
```- `DiaryItem` ์ปดํฌ๋ํธ ์์ฑ
- props๋ก ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ์์์ ๋ด์ ์ถ๋ ฅ
![๋ฆฌ์คํธ ๋ ๋๋ง](README_img/๋ฆฌ์คํธ_๋ฐ์ดํฐ_๋ ๋๋ง.png)
<๋ฆฌ์คํธ ๋ฐ์ดํฐ ๋ ๋๋ง ์์ ๊ฒฐ๊ณผ>
## 4. React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ2 - ๋ฐ์ดํฐ ์ถ๊ฐ
### 4-1. ํ์ต๋ชฉํ
- ๋ฐฐ์ด์ ์ด์ฉํ React์ List์ ์์ดํ ์ ๋์ ์ผ๋ก ์ถ๊ฐํด๋ณด๊ธฐ
### 4-2. ์ปดํฌ๋ํธ & ๋ฐ์ดํฐ ๊ตฌ์กฐ ์๊ฐํด๋ณด๊ธฐ
![state ๋์ด์ฌ๋ฆฌ๊ธฐ1](README_img/State_lifting_01.png)
- DiaryEditor ์ปดํฌ๋ํธ์์ ์์ฑ๋ ์ผ๊ธฐ ๋ด์ฉ์ DiaryList ์ปดํฌ๋ํธ์์ ๋ ๋๋ง์ ํ๊ธธ ์ํจ
- ํ์ง๋ง, ๋ฆฌ์กํธ์์๋ `๋๋ฑํ ๋ ๋ฒจ`์์ `๋ฐ์ดํฐ ์ ๋ฌ์ด ๋ถ๊ฐ๋ฅ`
![state ๋์ด์ฌ๋ฆฌ๊ธฐ2](README_img/State_lifting_02.png)
- ๋ฆฌ์กํธ๋ `๋จ๋ฐฉํฅ`์ผ๋ก `๋ฐ์ดํฐ`๊ฐ ํ๋ฅด๋ ํน์ฑ์ ์ง๋
![state ๋์ด์ฌ๋ฆฌ๊ธฐ3](README_img/State_lifting_03.png)
- ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ถ๋ชจ์ธ `App ์ปดํฌ๋ํธ`์ `[data, setData]์ State`๋ฅผ ๋ง๋ฆ
- `DiaryEditor` ์ปดํฌ๋ํธ์๋ ์ํ ํจ์์ธ `setData`๋ฅผ, `DiaryList` ์ปดํฌ๋ํธ์๋ ์ ๋ฐ์ดํธ๋๋ `data`๋ฅผ `props`๋ก ๊ฐ๊ฐ ์ ๋ฌ
![state ๋์ด์ฌ๋ฆฌ๊ธฐ4](README_img/State_lifting_04.png)
- ์ด๋ ๊ฒ ๋๋ฉด ์ผ๊ธฐ๊ฐ ์์ฑ๋ ๋, DiaryEditor๋ ์ํ ํจ์์ธ setData๋ฅผ ํธ์ถ
- ์๋กญ๊ฒ ์์ฑ๋ ์ผ๊ธฐ ๋ฐ์ดํฐ๋ state์ data๋ก ์ ๋ฌ๋จ
- ์ด ์ ๋ฐ์ดํธ ๋ data๋ฅผ DiaryList์์ ๋ฐ์ ๋ฆฌ๋ ๋๋งํ๊ฒ ๋จ
![state ๋์ด์ฌ๋ฆฌ๊ธฐ5](README_img/State_lifting_05.png)
- ์ฆ, `๋ฐ์ดํฐ`๋ ๋ถ๋ชจ์์ ์์, ์ฆ, `์์์ ์๋`๋ก ์ ๋ฌ
- ์์์์๋ ๋ถ๋ชจ๋ก ์ํ ํจ์๋ฅผ ํธ์ถํ๊ธฐ์ `์ด๋ฒคํธ`๋ `์๋์์ ์`๋ก ์ ๋ฌ
### 4-3. State ๋ง๋ค๊ธฐ
### - App ์ปดํฌ๋ํธ state ์์ฑ
```jsx
// App.jsconst [data, setData] = useState([]);
const dataId = useRef(0);
const onCreate = (author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
};
```- ์ด๋ฒคํธ onCreate ์์ฑ
- useRef()์ ์ด๊ธฐ๊ฐ์ผ๋ก 0 ์ง์ => <๋ณ์๋ช >.current๋ก ๊ฐ์ ํ์ธ ํ ์ ์์
- ์ด ํ <๋ณ์๋ช >.current๋ฅผ 1์ฉ ์ฆ๊ฐ์์ผ id๋ฅผ 1์ฉ ์ฆ๊ฐ์ํด
- ์๋ก ์์ฑ๋ ์ผ๊ธฐ๋ด์ฉ์ ๊ธฐ์กด ๋ด์ฉ๋ค์ ์์ ์์น์ํด
### - props๋ก ์ด๋ฒคํธ์ ๋ฐ์ดํฐ ์ ๋ฌ
```jsx
// App.js
```- DiaryEditor๋ก onCreate ํจ์ ์ ๋ฌ
- DiaryList๋ก data ์ ๋ฌ
### - ์ด๋ฒคํธ ๋ฐ๊ธฐ
```jsx
// DiaryEditor.jsconst DiaryEditor = ({ onCreate }) => {...}
```- props๋ก ์ ๋ฌ๋ onCreate ๋ฐ๊ธฐ
### - ์ผ๊ธฐ๋ฅผ ์ ์ถํ๋ฉด ์ด๋ฒคํธ ํธ์ถ
```jsx
// DiaryEditor.jsconst handleSubmit = () => {
if (state.author.length < 1) {
authorInput.current.focus();
return;
}if (state.content.length < 5) {
contentInput.current.focus();
return;
}
onCreate(state.author, state.content, state.emotion);
alert("์ ์ฅ ์ฑ๊ณต");
setState({
author: "",
content: "",
emotion: 1,
});
};
```- ์ ์ถ(handleSubmit) ์, onCreate()ํจ์๋ฅผ ํธ์ถํจ
- ์ธ์๋ก state์ author, content, emotion์ ์ ๋ฌ
- ์ ์ฅ ํ, ์ ๋ ฅ ์นธ ๋น์ฐ๊ณ , ๊ฐ์ 1๋ก ์ด๊ธฐํ
![state๋์ด์ฌ๋ฆฌ๊ธฐ ๊ฒฐ๊ณผ](README_img/state๋์ด์ฌ๋ฆฌ๊ธฐ_๋ฐ์ดํฐ_๋ ๋๋ง.gif)
## 5. React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ3 - ๋ฐ์ดํฐ ์ญ์
### 5-1. ํ์ต๋ชฉํ
- ์ญ์ ๋ฒํผ์ ๊ฐ ์ผ๊ธฐ ๋ฆฌ์คํธ ๋ชฉ๋ก๋ง๋ค ์์ฑ
- ์ญ์ ๋ฒํผ์ ํด๋ฆญํ ๊ฒฝ์ฐ, ํ์ธ ๋ฉ์์ง๊ฐ ์ ๋ฌ๋๊ณ ํ์ธ์ ๋๋ฅด๋ฉด ํด๋น ์ผ๊ธฐ ์์ดํ ์ด ์ญ์ ๋๊ณ ๋ฆฌ๋ ๋๋ง ๋จ
### 5-2. onDelete ํจ์ ์์ฑ
```jsx
// App.js// onDelete ํจ์
const onDelete = (targetId) => {
console.log(`${targetId}๊ฐ ์ญ์ ๋์์ต๋๋ค`);
const newDiaryList = data.filter((it) => it.id !== targetId);
setData(newDiaryList);
};return (
// DiaryList๋ก props๋ก onDelete ์ ๋ฌ
);
```- onDelete ํจ์๋ ์ญ์ ๋ฒํผ๊ณผ ํจ๊ป ํด๋น ์ผ๊ธฐ ์์ดํ ์ id๊ฐ์ targetId์ธ์๋ก ๋ฐ์
- ํํฐ๋ฅผ ์ฌ์ฉํ์ฌ data์์ id๊ฐ ์ธ์๋ก ๋ฐ์ targetId์ ๊ฐ์ง ์์ ๋๋จธ์ง ์์ดํ ๋ค์ ๋ชจ์
- ์๋กญ๊ฒ ์์ฑ๋ ์ผ๊ธฐ ๋ฆฌ์คํธ๋ค์ setData() ์ํ ํจ์์ ๋ฃ์ด ์ํ ๋ฐ๊พธ๊ธฐ
### 5-3. DiaryList๋ก ์ ๋ฌ๋ onDelete ํจ์๋ฅผ DiaryItem ์ปดํฌ๋ํธ๋ก ์ ๋ฌ
```jsx
// DiaryList.jsconst DiaryList = ({ onDelete, diaryList }) => {
return (
์ผ๊ธฐ ๋ฆฌ์คํธ
{diaryList.length}๊ฐ์ ์ผ๊ธฐ๊ฐ ์์ต๋๋ค.
{diaryList.map((it, idx) => (
// DiaryItem์ผ๋ก onDelete ์ ๋ฌ
))}
);
};
```- onDelete ํจ์๋ฅผ DiaryItem ์ปดํฌ๋ํธ๋ก props๋ก ์ ๋ฌ
### 5-4. ์ญ์ ์, onDelete ํจ์ ํธ์ถํ๊ณ ํด๋น ์์ดํ ์ id๊ฐ ์ ๋ฌ
```jsx
// DiaryItem.js// onDelete ํจ์ ์ ๋ฌ ๋ฐ์
const DiaryItem = ({
onDelete,
author,
content,
created_date,
emotion,
id,
}) => {
return (
// ...
{
console.log(id);
if (window.confirm(`${id}๋ฒ์งธ ์ผ๊ธฐ๋ฅผ ์ ๋ง ์ญ์ ํ์๊ฒ ์ต๋๊น?`)) {
onDelete(id);
}
}}
>
์ญ์ ํ๊ธฐ
);
};
```- onClick์ ์ฝ๋ฐฑํจ์๋ฅผ ์ง์
- `window.confirm()` : ํ์ธ ๋ฉ์์ง ๋์ฐ๊ธฐ
- ํ์ธ ํด๋ฆญ(true)์ผ ๊ฒฝ์ฐ, `onDelete` ํจ์ ํธ์ถํ์ฌ ํ๋ผ๋ฏธํฐ๋ก ํด๋น ์ผ๊ธฐ ์์ดํ ์ id ์ ๋ฌ
- ์ทจ์ ํด๋ฆญ(false)์ผ ๊ฒฝ์ฐ, ์๋ฌด ์ผ๋ ๋ฐ์ํ์ง ์์
![๋ฆฌ์คํธ ์ญ์ ๊ฒฐ๊ณผ](README_img/๋ฆฌ์คํธ_๋ฐ์ดํฐ_์ญ์ ํ๊ธฐ.gif)
<๋ฆฌ์คํธ ๋ฐ์ดํฐ ์ญ์ ์์ ๊ฒฐ๊ณผ>
## 6. React์์ ๋ฆฌ์คํธ ์ฌ์ฉํ๊ธฐ4 - ๋ฐ์ดํฐ ์์
### 6-1. ์์ ํ๊ธฐ ๊ธฐ๋ฅ์์ ํ์ํ ๊ฒ
- ์์ ํ๊ธฐ ๋ฒํผ
- ์์ ํ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด ๊ธฐ์กด ๋ด์ฉ์ด ์์ ํผ์ผ๋ก ๋ณํ
- ํน์ ๊ธ์ ์๋ฅผ ์ถฉ์กฑ ์, ์์ ์๋ฃ๋๊ณ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ, ์์ ํผ์ ํฌ์ปค์ค ๋๋๋ก ํ๊ธฐ
### 6-2. isEdit์ ์ํ์ ๋ฐ๋ฅธ ๋ฒํผ๊ณผ ์ผ๊ธฐ๋ด์ฉ ๋ณํ
### - ์์ ํ๊ธฐ ๋ฒํผ ๋ง๋ค๊ธฐ
```jsx
// DiaryItem.js์ญ์ ํ๊ธฐ
์์ ํ๊ธฐ
```- ๊ธฐ์กด ์ญ์ ํ๊ธฐ ๋ฒํผ๊ณผ ํจ๊ป ์์ ํ๊ธฐ ๋ฒํผ ๋ง๋ค๊ธฐ
- ์์ ํ๊ธฐ ๋ฒํผ ํด๋ฆญ ์, toggleIsEdit ํจ์ ์คํ
### - state์ toggleIsEdit
```jsx
// DiaryItem.jsconst [isEdit, setIsEdit] = useState(false);
const toggleIsEdit = () => setIsEdit(!isEdit);
```- `isEdit`์ด๋ ๋ณ์๋ฅผ ์ค์ ํ๊ณ state๋ก ๊ธฐ๋ณธ ๊ฐ์ `false`๋ก ์ง์
- toggleIsEdit์ ์ํ ํจ์ setIsEdit์ ์ธ์๋ก `!isEdit`์ ๋์ด isEdit์ด false๋ฉด true๋ก, true๋ฉด false๋ก `๋ฐ์ `๋๋๋ก ํจ(์ค์์น)
- true๋ ์์ ์ํ, false๋ ๋น ์์ ์ํ
- ์์ ํ๊ธฐ ๋ฒํผ์ onClick์ toggleIsEdit ์ง์
### - ์ผํญ ์ฐ์ฐ์๋ฅผ ์ด์ฉํ ์ผ๊ธฐ๋ด์ฉ, ์์ ํผ, ๋ฒํผ ๋ณํ
```jsx
// DiaryItem.jsconst [localContent, setLocalContent] = useState(content);
...
{isEdit ? (
{
setLocalContent(e.target.value);
}}
/>
) : (
<>{content}>
)}{isEdit ? (
<>
์์ ์ทจ์
์์ ์๋ฃ
>
) : (
<>
์ญ์ ํ๊ธฐ
์์ ํ๊ธฐ
>
)}
```- ์ผํญ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ฌ isEdit์ด true, ์ฆ ์์ ์ํ์ด๋ฉด, textarea ์์๊ฐ ์๊น
- ์์ ํผ textarea์ ๋ด์ฉ์ ๋ณ์ localContent๋ก ์ง์ ํ๊ณ state๋ฅผ ์ฌ์ฉํ์ฌ `์ด๊ธฐ ๊ฐ`์ ๊ธฐ์กด์ ์์ฑ๋์ด์๋ `์ผ๊ธฐ ๋ด์ฉ์ธ content`๋ก ์ค์
- ๋ฒํผ์ ๊ฒฝ์ฐ์๋ ์ผํญ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ฌ isEdit์ด true, ์ฆ ์์ ์ํ์ด๋ฉด `์์ ์ทจ์`, `์์ ์๋ฃ` ๋ฒํผ์ ๋ณด์ฌ์ฃผ๊ณ , false๋ก ์์ ์ํ๊ฐ ์๋๋ฉด ๊ธฐ์กด์ `์ญ์ ํ๊ธฐ` ๋ฐ `์์ ํ๊ธฐ` ๋ฒํผ์ด ๋ณด์ด๋๋ก ํจ
### 6-3. ๋ฒํผ์ onClick ์ค์ ๋ฐ ๋ฐ์ดํฐ ์ด๋ฒคํธ ํจ์ ์์์ผ๋ก ์ ๋ฌ
### - onClick ์ค์
```jsx
// DiaryItem.js// textarea์ DOM ์ ๊ทผ ํ ์ ์๋๋ก localContentInput ์์ฑ
const localContentInput = useRef();// ์์ ์๋ฃ ๋ฒํผ ํด๋ฆญ ์, ์คํ๋๋ hadleEdit ํจ์
const handleEdit = () => {
if (localContent.length < 5) {
localContentInput.current.focus();
return;
}
if (window.confirm(`${id}๋ฒ์งธ ์ผ๊ธฐ๋ฅผ ์์ ํ์๊ฒ ์ต๋๊น?`)) {
onEdit(id, localContent);
toggleIsEdit();
}
};// ์์ ์ทจ์ ๋ฒํผ ํด๋ฆญ ์, ์คํ๋๋ handleQuitEdit ํจ์
const handleQuitEdit = () => {
setIsEdit(false);
setLocalContent(content);
};...
// ๋ด์ฉ ์์ ํผ textarea
// ์์ ์ทจ์, ์์ ์๋ฃ ๋ฒํผ
<>
์์ ์ทจ์
์์ ์๋ฃ
>
```- ๋จผ์ ์์ ์ทจ์ ๋ฒํผ์ `handleQuitEdit`์ ๋ณด๋ฉด, `setIsEdit(false);`์ ํตํด ์์ ํ์ง ์์์ผ๋ก ๋ณ๊ฒฝ
- ๋ง์ฝ, ์์ ์ค์ด์๋ค๋ฉด ์์ ์ทจ์๋ฅผ ๋๋ฅด๊ณ ๋ค์ ์์ ํ๊ธฐ ๋ฒํผ์ ํด๋ฆญํ ๊ฒฝ์ฐ, ์์ ์์ ๋ด์ญ์ด ๊ทธ๋๋ก ๋จ์์๊ธฐ์ ์ทจ์ ์, ์ด๋ฅผ `์ด๊ธฐํ`ํ๊ธฐ ์ํด `setLocalContent(content);`์ผ๋ก ๊ธฐ์กด ์ผ๊ธฐ๋ด์ฉ ๋ฃ๊ธฐ
- ์์ ์๋ฃ๋ฅผ ํด๋ฆญํ๋ฉด `handleEdit` ํจ์๊ฐ ์คํ ๋จ
- textarea์ `useRef()`์ธ `localContentInput` ์ง์ ํ์ฌ DOM ์ ๊ทผ ํ ์ ์๋๋ก ํ๊ธฐ
- handleEdit ํจ์๋ ๊ธฐ์กด ์ผ๊ธฐ๋ด์ฉ์ ๊ท์น์ฒ๋ผ 5๊ธ์ ๋ฏธ๋ง์ผ๋ก ์์ฑ๋ ๊ฒฝ์ฐ, `focus()` ์ฃผ๊ธฐ
- ํต๊ณผ๋ ๊ฒฝ์ฐ, ํ์ธ ์ฐฝ์ ๋์ฐ๊ณ `onEdit()` ํจ์์ id์ localContent๋ฅผ ๋ณด๋ด ๋ฐ์ดํฐ ์์
- toggleIsEdit() ํจ์๋ก ๋ค์ ์์ ํ์ฑํ ์ํ ๋ซ๊ธฐ
### - App ์ปดํฌ๋ํธ์์ onEdit ์ ๋ฌํ๊ธฐ
```jsx
// App.jsconst onEdit = (targetId, newContent) => {
setData(
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it,
),
);
};...
```
```jsx
// DiaryList.jsconst DiaryList = ({ onEdit, onRemove, diaryList }) => {
...
};
```- onEdit ํจ์๋ ๋ฐ์ดํฐ์ `id`์ `์์ ๋ ๋ด์ฉ`์ ์ ๋ฌ๋ฐ์
- data๋ค์ `map`์ผ๋ก ์ํํ์ฌ ์ธ์๋ก ์ ๋ฌ๋ฐ์ id์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ content๋ง ์์ ๋ newContent๋ก `๋ฎ์ด์์`
- App ์ปดํฌ๋ํธ์์ ์์ฑ๋ onEdit ํจ์๋ฅผ DiaryList ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๊ณ ๋ค์ DiaryItem ์ปดํฌ๋ํธ๋ก ์ ๋ฌํจ
![๋ฆฌ์คํธ ์์ ๊ฒฐ๊ณผ](README_img/๋ฆฌ์คํธ_๋ฐ์ดํฐ_์์ ํ๊ธฐ.gif)
<๋ฆฌ์คํธ ๋ฐ์ดํฐ ์์ ์์ ๊ฒฐ๊ณผ>
## 7. React Lifecycle ์ ์ดํ๊ธฐ - useEffect
### 7-1. Lifecycle
- ์์ ์ฃผ๊ธฐ๋ก ์ผ๋ฐ์ ์ผ๋ก `์๊ฐ์ ํ๋ฆ`์ ๋ฐ๋ผ ํ์๋ถํฐ ์ฃฝ์๊น์ง ์ด๋ฅด๋ `๋จ๊ณ์ ` ๊ณผ์
- React์ ์ปดํฌ๋ํธ ์ญ์ ์๋ช ์ฃผ๊ธฐ(Lifecycle)์ ๊ฐ์ง
-![๋ฆฌ์กํธ ๋ผ์ดํ์ฌ์ดํด](README_img/lifecycle.png)
- React์ Lifecycle์ ํฌ๊ฒ 3๊ฐ์ง์ ๋จ๊ณ๋ก ๋๋จ
- `ํ์`(Mount) : ํ๋ฉด์ ๋ํ๋๋ ๊ฒ
- `๋ณํ`(Update) : ์ ๋ฐ์ดํธ(๋ฆฌ๋ ๋)
- `์ฃฝ์`(Unmount) : ํ๋ฉด์์ ์ฌ๋ผ์ง
- ๊ฐ๊ฐ์ ๋จ๊ณ๋ง๋ค `ํน์ ํ ์์ `์ ์ํํ๋๋ก ํ ์ ์์
### - Lifecycle ๊ฐ๊ฐ์ ๋จ๊ณ์์ ์ฌ์ฉํ๋ ๋ฉ์๋
- ์ง๊ธ๊น์ง๋ ํ์ดํ ํจ์๋ฅผ ์ด์ฉํ `ํจ์ํ ์ปดํฌ๋ํธ`๋ง ์ด์ฉํด์์
- ํ์ง๋ง, ์ด ๋ฉ์๋๋ค์ `ํด๋์คํ ์ปดํฌ๋ํธ`์์๋ง ์ฌ์ฉ ๊ฐ๋ฅ ํ๋ค.
- `ref`, `state`์ ๊ฒฝ์ฐ๋ ํจ์ํ ์ปดํฌ๋ํธ์์๋ ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํ๊ณ `ํด๋์คํ ์ปดํฌ๋ํธ`์์๋ง ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค.
1. Mount ๋จ๊ณ : ComponentDidMount()
2. Update ๋จ๊ณ : ComponentDidUpdate()
3. Unmount ๋จ๊ณ : ComponentWillUnmount()
### 7-2. React Hooks
- 2019๋ 6์ ์ ์ ์ถ์๋ ๊ธฐ๋ฅ
- ์์ Lifecycle์์ ์ฌ์ฉํ๋ ๋ฉ์๋, state, ref๋ฅผ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ React์์ `์ฌ์ฉํ๊ธฐ ์ด๋ ค์`์ด ์์์
- ๋ฐ๋ผ์ ์ด๋ฌํ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ณ ์, `use` ํค์๋๋ฅผ ์์ ๋ถ์ฌ `ํด๋์คํ ์ปดํฌ๋ํธ`๊ฐ ์ฌ์ฉํ๋ ์ด ๊ธฐ๋ฅ๋ค์ `ํจ์ํ ์ปดํฌ๋ํธ`์์ ์ฌ์ฉํ ์ ์๋๋ก Hooking(๋์)ํ ๊ฒ
- ex) useState, useEffect, useRef, ...
### - ์ ์ด์ React์์ ํด๋์คํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ง ์์ ์ด์ ๋?
- ํด๋์คํ ์ปดํฌ๋ํธ์ ์ฝ๋๊ฐ ๋งค์ฐ ๊ธธ์ด์ง ์ ์๊ณ ๋ณต์กํด์ง ์ ์์
- ์ค๋ณต ์ฝ๋, ๊ฐ๋ ์ฑ ๋ฌธ์ ๋ฑ ์ฌ๋ฌ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ธฐ ์ํด์ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉ
### 7-3. useEffect
- ์์ ๊ฐ `Lifecycle์ ๋จ๊ณ์์ ์ฌ์ฉํ๋ ๋ฉ์๋`๋ค์ ํจ์ํ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๊ฒ ํด์ค
- 2๊ฐ์ ํ๋ผ๋ฏธํฐ์ธ `์ฝ๋ฐฑํจ์`, `์์กด์ฑ ๋ฐฐ์ด`์ ๋ฐ์```jsx
// useEffect ์ฌ์ฉimport React, { useEffect } from "react";
useEffect(() => {
// ์ฝ๋ฐฑํจ์ ์์ฑ
}, []); // '[]'๋ Dependency Array(์์กด์ฑ ๋ฐฐ์ด) : ์ด ๋ฐฐ์ด ๋ด์ ๋ค์ด์๋ ๊ฐ์ด ๋ณํํ๋ฉด ์ฝ๋ฐฑํจ์๊ฐ ์ํ๋จ
```
### - Mount ๋จ๊ณ์์ ์์ ์ํ
```jsx
// ์๋ก ๋ง๋ Lifecycle.js// Mount ๋จ๊ณ์์ ์์ ์ํ
import React, { useEffect, useState } from "react";
const Lifecycle = () => {
// Mount ๋จ๊ณ์ ์คํ๋จ -> dependency array์ ๋น๋ฐฐ์ด
useEffect(() => {
console.log("Mount!");
}, []);
}
```- dependency ๋ฐฐ์ด์ ์๋ฌด ๊ฐ๋ ๋ฃ์ง ์์ผ๋ฉด(`๋น๋ฐฐ์ด`) Mount ๋จ๊ณ์์ ์ฝ๋ฐฑํจ์๊ฐ ์ํ๋จ
### - Update ๋จ๊ณ์์ ์์ ์ํ
```jsx
// Lifecycle.jsimport React, { useEffect, useState } from "react";
const Lifecycle = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState("");// Update ๋จ๊ณ์ ์คํ๋จ -> dependency array ์์ ๊ธฐ: ์ด๋ ์์๋ผ๋ ์ ๋ฐ์ดํธ ๋๋ฉด ์ฝ๋ฐฑํจ์ ์ํ
useEffect(() => {
console.log("Update!");
});
// ํน์ ์์๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ์ฝ๋ฐฑํจ์ ์ํ
// count์ state๊ฐ ๋ณํ๋ ์๊ฐ ์ฝ๋ฐฑํจ์ ์ํ
useEffect(() => {
console.log(`count is update : ${count}`);
if (count > 5) {
alert("count๊ฐ 5๋ฅผ ๋์์ต๋๋ค. ๋ฐ๋ผ์ 1๋ก ์ด๊ธฐํ ํฉ๋๋ค.");
setCount(1);
}
}, [count]);
// text์ state๊ฐ ๋ณํ๋ ์๊ฐ ์ฝ๋ฐฑํจ์ ์ํ
useEffect(() => {
console.log(`text is update : ${text}`);
}, [text]);
}
```- `dependency ๋ฐฐ์ด์ ์์ฒด๋ฅผ ์์ ๋ฉด` ์ด๋ ์์๋ผ๋ ์ ๋ฐ์ดํธ ๋๋ฉด ์ฝ๋ฐฑํจ์ ์ํ
- `dependency ๋ฐฐ์ด์ ๊ฐ์ ๋ฃ์ผ๋ฉด`, ํด๋น ๋ณ์ ๊ฐ์ด ์ ๋ฐ์ดํธ ๋๋ฉด ์ฝ๋ฐฑํจ์ ์ํ
![update](README_img/useEffect_update.gif)
### - Unmount ๋จ๊ณ์์ ์์ ์ํ
```jsx
// Lifecycle.jsimport React, { useEffect, useState } from "react";
const UnmountTest = () => {
// Unmount ๋จ๊ณ์์ ์ํ๋๋ ์์ ์ ๋ง๋ค๊ธฐ ์ํด์๋ ์ฝ๋ฐฑํจ์ ์์์
// ํจ์๋ฅผ ๋ฆฌํดํ๊ฒ ํ๋ฉด, Unmount๋๋ ๊ฒฝ์ฐ, ๋ฆฌํด๋ ํจ์๊ฐ ์ํ๋จ
useEffect(() => {
console.log("Mount!");
return () => {
// Unmount ๊ฒฝ์ฐ, ์ํ
console.log("Unmount!");
};
}, []);
returnUnmount Testing Component;
};
```- Unmount ๋จ๊ณ์์ ์ํ๋๋ ์์ ์ ๋ง๋ค๊ธฐ ์ํด์๋ useEffect์ ์ฝ๋ฐฑํจ์์ `๋ฆฌํด ํจ์๋ฅผ ์ ์`ํ๋ฉด ํด๋น ํจ์๊ฐ Unmount ๋จ๊ณ์์ ์ํ๋จ
![mount, Unmount](README_img/useEffect_mount_unmount.gif)
## 8. React์์ API ํธ์ถํ๊ธฐ
### 8-1. ํ์ต๋ชฉํ
- useEffect๋ฅผ ์ฌ์ฉํ์ฌ Mount ์์ ์ API๋ฅผ ํธ์ถํ๊ณ ํด๋น API ๊ฒฐ๊ณผ ๊ฐ์ ์ผ๊ธฐ ๋ฐ์ดํฐ์ ์ด๊ธฐ ๊ฐ์ผ๋ก ์ด์ฉํ๊ธฐ
### 8-2. fetch๋ก API ๋ฐ์์ค๊ธฐ
```javascript
// App.jsconst getData = async () => {
const res = await fetch(
"https://jsonplaceholder.typicode.com/comments",
).then((res) => res.json());
const initData = res.slice(0, 20).map((it) => {
return {
author: it.email,
content: it.body,
emotion: Math.floor(Math.random() * 5) + 1,
created_date: new Date().getTime(),
id: dataId.current++,
};
});setData(initData);
};
```- jsonplaceholder(๋๋ฏธ json์ ์๋ตํ๋ API)๋ฅผ ํตํด fetch๋ก API ๋ฐ๊ธฐ
- .then()์ ์ฌ์ฉํ์ฌ resolve๋๋ฉด, ํด๋น ์๋ต ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
- ํ์ง๋ง ํด๋น ๋ฐ์ดํฐ๋ ํค๋๋ง ์๊ธฐ์ `.json()`๋ก ์ฌ์ฉ๊ฐ๋ฅํ `Promise ์ํ`๋ก ๋ณํ
- ์ด์ค 20๊ฐ๋ฅผ sliceํ๊ณ , map์ ํตํด ๊ฐ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ด์ ๋ฐฐ์ด์ ๋ฆฌํดํ๋ค.
- ์ด ๋ฐฐ์ด์ `setData`์ ๋ด์ data๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
### 8-3. useEffect๋ก Mount์ ๋ฐ์ดํฐ ํธ์ถ
```javascript
// App.jsuseEffect(() => {
getData();
}, []);
```- useEffect๋ฅผ ์ฌ์ฉํ๊ณ dependency ๋ฐฐ์ด์ `๋น ๋ฐฐ์ด`๋ก ์ค์ ํ์ฌ `Mount ๋จ๊ณ`์์ ํด๋น ๋ธ๋ก์ ์ํ
- ์์ API๋ฅผ ํธ์ถํ๋ getData() ํจ์๋ฅผ ํธ์ถ์ํจ๋ค.
![API ํธ์ถ](README_img/API์ฌ์ฉ.gif)
<ํ๋ฉด ๋ ๋๋ง ์, API๋ฅผ ํตํด ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉ>
## 9. ์ต์ ํ1 ์ฐ์ฐ ๊ฒฐ๊ณผ ์ฌ์ฌ์ฉ - useMemo
### 9-1. ํ์ต๋ชฉํ
- ํ์ฌ ์ผ๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๋ ํจ์์ ์
- ์ผ๊ธฐ ๋ฐ์ดํฐ์ ๊ธธ์ด๊ฐ ๋ณํํ์ง ์์ ๋, ํจ์๊ฐ ๊ฐ์ ๋ค์ ๊ณ์ฐํ์ง ์๋๋ก ํ๊ธฐ
- Memoization ๊ฐ๋ ์ดํดํ๊ธฐ
### 9-2. Memoization
- ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ ์ค ํ๋
- ์ด๋ฏธ ๊ณ์ฐํด๋ณธ `์ฐ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์ต`ํด๋๊ณ ๋์ผํ ๊ณ์ฐ์ ์ํํ ๊ฒฝ์ฐ, ์ฐ์ฐ์ `์ฌ์ํํ์ง ์๊ณ `, `๊ธฐ์ตํด๋ ๋ฐ์ดํฐ`๋ฅผ ๋ฐ๋ก ๋ฐํ
### 9-3. App ์ปดํฌ๋ํธ์์ ๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ, ๊ธฐ๋ถ ๋์ ์ผ๊ธฐ ๊ณ์ฐ
- ํ์ํ ๋ฐ์ดํฐ 3๊ฐ์ง
- ๊ธฐ๋ถ ๋์ ์ผ๊ธฐ(1~2์ ) ๊ฐ์
- ๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ(3~5์ ) ๊ฐ์
- ๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ์ ๋น์จ
### - ํ์ํ ๋ฐ์ดํฐ ๋ถ์ ํจ์ ์์ฑ
```javascript
// App.jsconst getDiaryAnalysis = () => {
console.log("์ผ๊ธฐ ๋ถ์ ์์");const goodCount = data.filter((it) => it.emotion >= 3).length;
const badCount = data.length - goodCount;
const goodRatio = (goodCount / data.length) * 100;
return { goodCount, badCount, goodRatio };
};const { goodCount, badCount, goodRatio } = getDiaryAnalysis();
```- getDiaryAnalysis ํจ์ ์์ฑํ๊ณ , ํจ์๊ฐ ํธ์ถ๋ ๋๋ง๋ค ์ฝ์์ "์ผ๊ธฐ ๋ถ์ ์์"์ ์ถ๋ ฅํ๋ค.
- goodCount๋ data ๋ฐฐ์ด์์ ๊ฐ์ฒด์ emotion ๊ฐ์ด 3์ด์์ธ ๊ฒ๋ง ํํฐ๋ก ๋ชจ์ length๋ก ๊ฐ์๋ฅผ ์ง์
- badCount๋ ์ ์ฒด ์ผ๊ธฐ ๊ฐ์ data.length์์ goodCount๋ฅผ ๋บ ๋๋จธ์ง ๊ฐ์
- goodRatio๋ ์ ์ฒด ์ผ๊ธฐ ๊ฐ์์์ ์ข์ ์ผ๊ธฐ ๊ฐ์๋ฅผ ๋๋๊ณ 100์ ๊ณฑํ ๋น์จ
- ์ต์ข ์ ์ผ๋ก getDiaryAnalysis ํจ์๋ 3๊ฐ์ ์์๋ฅผ ๋ฆฌํดํจ
- ๋น๊ตฌ์กฐํ ํ ๋น์ ํตํด ํจ์ ํธ์ถ ์, ๊ฐ๊ฐ์ ์์๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋จ
### - ๋ฐ์ดํฐ ์ถ๋ ฅ
```javascript
// App.js
...
์ ์ฒด ์ผ๊ธฐ : {data.length}
๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๊ฐ์ : {goodCount}
๊ธฐ๋ถ ๋์ ์ผ๊ธฐ ๊ฐ์ : {badCount}
๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๋น์จ : {goodRatio}
...
```- ๊ฐ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํ๋ค.
- ์ฝ์์ ๋ณด๋ฉด "์ผ๊ธฐ ๋ถ์ ์์"์ 2๋ฒ ์ถ๋ ฅํ๋๋ฐ ์ด ์ด์ ๋ ์ฒ์์ data์ ์๋ฌด ๊ฒ๋ ์๋ ์ํ์์ 1๋ฒ ํธ์ถ๋๊ณ , API๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ๋ฆฌ๋ ๋ ๋์ด 2๋ฒ์งธ ํธ์ถ์ผ ๋ฐ์ํ๊ธฐ์ 2๋ฒ ์ถ๋ ฅ๋๋ ๊ฒ์ ์ ์ ์์
### - ๋ฌธ์ ์
- ์ผ๊ธฐ์ `๋ด์ฉ์ ์์ `ํ๋ ๊ฒ์ ํ์ฌ ์ฐ๋ฆฌ๊ฐ ์ถ๋ ฅํ๋ `๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๊ฐ์`, `๊ธฐ๋ถ ๋์ ์ผ๊ธฐ ๊ฐ์`, `๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๋น์จ`์ ์ด๋ ํ ์ํฅ๋ ๋ฏธ์น์ง ์๋๋ค.
- ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ `๋ด์ฉ์ ์์ `ํ๋ฉด `๋ฆฌ๋ ๋`๊ฐ ๋ฐ์ํ๊ณ "์ผ๊ธฐ ๋ถ์ ์์"์ด ์ฝ์์ ์ถ๋ ฅ๋๋ฉฐ `์ผ๊ธฐ ๋ถ์ ํจ์๊ฐ ๋ ํธ์ถ`๋ ๊ฒ์ ์ ์ ์๋ค.
- ํ์ฌ๋ ํจ์ ํ๋๊ฐ ํธ์ถ๋์ง๋ง, ์ผ๊ธฐ์ ๊ฐ์๊ฐ ๋ง์์ง๊ฑฐ๋, ๋ ๋ง์ ํจ์๊ฐ ๋ฆฌ๋ ๋ ์, ํธ์ถ๋๋ค๋ฉด ํ๋ก๊ทธ๋จ์ ์ฌ์ฉ์ฑ์ ์ํฅ์ ์ค ์ ์๋ค.
- ๋ฐ๋ผ์ ๋ฆฌ๋ ๋ ๋ ๋๋ง๋ค ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ์๋, `ํน์ ๊ฐ์ด ๋ณํํ ๋๋ง` ํด๋น ํจ์๋ฅผ ์ํํ๋๋ก ํ ์ ์๋ค.
### 9-4. useMemo
- ์์ ๋ฌธ์ ๋ฅผ useMemo๋ฅผ ์ฌ์ฉํ์ฌ ํด๊ฒฐํ ์ ์๋ค.
- useMemo๋ `์ฝ๋ฐฑํจ์`, `dependency ๋ฐฐ์ด`์ ์ธ์๋ก ๋ฐ์, (useEffect์ ์ ์ฌ)```javascript
// App.jsconst getDiaryAnalysis = useMemo(() => {
console.log("์ผ๊ธฐ ๋ถ์ ์์");const goodCount = data.filter((it) => it.emotion >= 3).length;
const badCount = data.length - goodCount;
const goodRatio = (goodCount / data.length) * 100;
return { goodCount, badCount, goodRatio };
}, [data.length]);
```- ์ฝ๋ฐฑํจ์๊ฐ ์คํ๋๊ณ ํด๋น ๋ฆฌํด ๊ฐ์ ๊ณ์ ๊ธฐ์ตํด๋๋ค.
- ๊ทธ๋ฆฌ๊ณ dependency ๋ฐฐ์ด์ ๋ด๊ธด ๊ฐ์ด ๋ณํํ์ง ์์ผ๋ฉด ๋ฆฌํด ๊ฐ์ ๊ทธ๋๋ก ์ฌ์ฉํ๋ค.
- ํ์ง๋ง dependency ๋ฐฐ์ด์ ๋ด๊ธด ๊ฐ์ด ๋ณํํ๋ฉด ์๋ก ์ ๋ฐ์ดํธ๋ ๋ฆฌํด ๊ฐ์ ๋ค์ ์ฌ์ฉํ๊ฒ ๋๋ค.
- ๋ฐ๋ผ์ ๋ถํ์ํ ์ฐ์ฐ์ ๋ง์ ์ ์๋ค.
### - useMemo์ ํ์ (์ค์ ์ฃผ์)
- useMemo๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ฝ๋ฐฑํจ์๊ฐ ๋ฆฌํดํ๋ ๊ฐ์ ๋ฐ๊ธฐ ๋๋ฌธ์ getDiaryAnalysis๋ ํจ์๊ฐ ์๋ ์์๊ฐ ๋จ
```javascript
// App.jsconst { goodCount, badCount, goodRatio } = getDiaryAnalysis;
```- ๋ฐ๋ผ์ `getDiaryAnalysis();`์ ๊ฐ์ด ํจ์๋ก ํธ์ถํ์์ง๋ง useMemo๋ฅผ ์ฌ์ฉํ๋ฉด `getDiaryAnalysis`๋ก ๊ฐ์ ธ์์ผ ํจ
- ํจ์ ํธ์ถ๋ก ๊ฐ์ ธ์ฌ ๊ฒฝ์ฐ, ์๋ฌ ๋ฐ์
![useMemo](README_img/useMemo.gif)
## 10. ์ต์ ํ2 ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ - React.memo
### 10-1. ๋ถํ์ํ ๋ฆฌ๋ ๋
- count์ text๋ผ๋ ์ํ๋ฅผ ๊ฐ์ง App์์ count ๊ฐ์ด ๋ณํํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
![๋ถํ์ํ ๋ฆฌ๋ ๋](README_img/React_memo_background.png)
- `state๊ฐ ๋ณํ`ํ๋ฉด ํด๋น ์ปดํฌ๋ํธ์ ์์ ์ปดํฌ๋ํธ๋ค์ `๋ชจ๋ ๋ฆฌ๋ ๋๊ฐ ๋ฐ์`
- ๋ถํ์ํ ๋ฆฌ๋ ๋๋ฅผ ๋ฐ์์ํค๊ธฐ์ ์ปดํฌ๋ํธ์ ์์ด ๋ง์ ๊ฒฝ์ฐ, `์ฑ๋ฅ ๋นํจ์จ`์ด ๋ฐ์ํ ์ ์๋ค.
### 10-2. ํด๊ฒฐ๋ฐฉ์
- ์์ ์ปดํฌ๋ํธ์ `์กฐ๊ฑด์ ์ง์ `ํ๋ค.
- ํด๋น ์กฐ๊ฑด์ด `์ถฉ์กฑ`๋ ๊ฒฝ์ฐ์๋ง `๋ ๋๋ง์ ์ํ`ํ๋๋ก ํจ![ํด๊ฒฐ๋ฐฉ์](README_img/React_memo_solution.png)
### 10-3. React.memo
- ํจ์ํ ์ปดํฌ๋ํธ์์ `๋์ผํ props`์ `๋ถ๋ชจ ์ปดํฌ๋ํธ`๋ก๋ถํฐ ๋ฐ์์ `๋์ผํ ๋ ๋๋ง`์ด ๋ฐ์ํ ๊ฒฝ์ฐ, ๋ค์ ๋ ๋๋งํ์ง ์๊ณ ๋ง์ง๋ง์ผ๋ก ๋ ๋๋ง๋ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
- props์ ์ํด์๋ง ๋ ๋๋ง์ ์ฌ์ฌ์ฉํ๋ ๊ฒ์ด๋ฉฐ, state๋ฑ์ ๋ณํ ์์๋ ๋ฆฌ๋ ๋๋ง ๋ฐ์ํจ
### - React.memo ํ ์คํธ
- ๋ถ๋ชจ ์ปดํฌ๋ํธ์ธ OptimizeTest๋ฅผ ์์ฑ
- `count`์ `text`์ ๋ ๊ฐ์ ์ํ๋ฅผ ์์ฑ
- ์์ ์ปดํฌ๋ํธ์ธ CountView์ TextView์ ๊ฐ๊ฐ props ์ ๋ฌ```javascript
// OptimizeTest.jsconst OptimizeTest = () => {
const [count, setCount] = useState(1);
const [text, setText] = useState("");return (
count
setCount(count + 1)}>+
text
{
setText(e.target.value);
}}
/>
);
};
```
```javascript
// Optimize.jsimport { useState, useEffect } from "react";
const TextView = ({ text }) => {
useEffect(() => {
console.log(`Update :: text : ${text}`);
});
return{text};
};const CountView = ({ count }) => {
useEffect(() => {
console.log(`Update :: count : ${count}`);
});
return{count};
};
```- `CountView`์ `TextView` ๋ ๊ฐ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ์์ฑ
- ๊ฐ๊ฐ prop์ผ๋ก count์ text๋ฅผ ๋ฐ๋๋ค.
- useEffect๋ฅผ ์ฌ์ฉํ๊ณ dependency ๋ฐฐ์ด์ ๋ฐ์ง ์๊ฒํ์ฌ ์ด๋ค ์์๋ผ๋ Update(๋ฆฌ๋ ๋)๋๋ฉด ์ฝ์์ ์ถ๋ ฅํ๋๋ก ์ค์
![React.memo ์ ์ฉ ์ ](README_img/React_memo_non.png)
<์์ ์ปดํฌ๋ํธ ๋์์ ๋ฆฌ๋ ๋๋์ด ์ฝ์ ์ถ๋ ฅ>
```javascript
// Optimize.jsimport React, { useState, useEffect } from "react";
const TextView = React.memo(({ text }) => {
useEffect(() => {
console.log(`Update :: text : ${text}`);
});
return{text};
});const CountView = React.memo(({ count }) => {
useEffect(() => {
console.log(`Update :: count : ${count}`);
});
return{count};
});
```- React๋ฅผ importํ๊ธฐ
- props๋ฅผ ๋ฐ๋ ์์์ ํจ์ํ ์ปดํฌ๋ํธ ์ ์ฒด๋ฅผ `React.memo()`๋ก ๊ฐ์ธ๊ธฐ
- ์ด๋ ๊ฒ ํ๊ฒ ๋๋ฉด props์ ๊ฐ์ด ๋ฐ๋๋ ๊ฒฝ์ฐ์๋ง ํด๋น ์์ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋ํ๊ฒ ๋๋ค.
![React.memo ์ ์ฉ ํ](README_img/React_memo_using.png)
### - props๋ก ๊ฐ์ฒด๋ฅผ ๋ฐ๋ ๊ฒฝ์ฐ : ๋ฌธ์ ์
```javascript
// OptimizeTest.jsconst OptimizeTest = () => {
const [count, setCount] = useState(1);
const [obj, setObj] = useState({
count: 1,
});return (
Counter A
{
setCount(count);
}}
>
A button
Counter B
{
setObj({ count: obj.count });
}}
>
B button
);
};
```- state๋ก count, obj ๋ฐ์ดํฐ ์ค์
- `setCount(count)`, `setObj({ count: obj.count })`๋ก ๊ธฐ์กด ๋ฐ์ดํฐ์ ๊ฐ์ ๊ฐ์ state๋ก ์ ๋ฌ
```javascript
//OptimizeTest.jsimport React, { useState, useEffect } from "react";
const CounterA = React.memo(({ count }) => {
useEffect(() => {
console.log(`CounterA Update - count : ${count}`);
});
return{count};
});const CounterB = React.memo(({ obj }) => {
useEffect(() => {
console.log(`CounterB Update - count : ${obj.count}`);
});
return{obj.count};
});
```- useMemo๋ฅผ ์ฌ์ฉํ์ฌ ๋ถ๋ชจ ์ปดํฌ๋ํธ OptimizeTest๋ก๋ถํฐ ๋ฐ์ props์ธ count์ obj๊ฐ ๋ณ๊ฒฝ๋๋ฉด useEffect์ ์ํด์ ์ฝ์์ ์ ๋ฐ์ดํธ ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๋๋ก ์ค์ ํ์๋ค.
- CounterA ์ปดํฌ๋ํธ์ ๊ฒฝ์ฐ, `count` ๊ฐ์ด `setCount(count)`์ ์ํด ๋์ผํ ๊ฐ์ ๋ณด๋ด๊ธฐ์ `๋ฆฌ๋ ๋๋์ง ์์`
- ๋ฐ๋ฉด CounterB ์ปดํฌ๋ํธ์ ๊ฒฝ์ฐ, `obj` ๊ฐ์ด `setObj({ count: obj.count })`๋ก ๊ฐ์ ๋ณํ๊ฐ ์์ง๋ง `๋ฆฌ๋ ๋๊ฐ ๋ฐ์`ํ๋ค.
<๊ฐ์ฒด๋ฅผ ๋น๊ตํ๋ ๋ฐฉ๋ฒ>
```javascript
const a = {count: 1};
const b = {count: 1};
```- ์์ ๊ฒฝ์ฐ, `a === b`๋ true์ผ๊น?
- ์ ๋ต์ false
- ๊ฐ์ฒด๋ฅผ ๋น๊ตํ๋ ๊ฒฝ์ฐ, ๊ฐ์ฒด์ `์ฃผ์(ํด์)`๋ฅผ ๋น๊ตํ๊ฒ ๋๋๋ฐ ์ด ์ฃผ์๊ฐ ๋ค๋ฅด๊ธฐ์ ๋ค๋ฅธ ๊ฐ์ฒด๋ก ์ธ์ํ๊ฒ ๋๋ค. (`์์ ๋น๊ต`)
- ๋ฐ๋ผ์ ๋์ผํ props๋ฅผ ๋ฐ์ ๊ฒ๊ฐ์ง๋ง ๋ค๋ฅธ ๊ฐ์ฒด๋ก ํ๋จํ๊ธฐ์ React.memo์์ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋ค.
```javascript
const a = {count: 1};
const b = a;
```- ์ด ๊ฒฝ์ฐ๋ b์์ a ์์ฒด๋ฅผ ์ฐธ์กฐํ์ฌ ๊ฐ์ ๊ฐ์ฒด ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํค๊ธฐ์ `a === b`๊ฐ `true`์ด๋ค.
### - props๋ก ๊ฐ์ฒด๋ฅผ ๋ฐ๋ ๊ฒฝ์ฐ : ํด๊ฒฐ - areEqual() ํจ์
```javascript
// OptimizeTest.jsconst CounterB = ({ obj }) => {
useEffect(() => {
console.log(`CounterB Update - count : ${obj.count}`);
});
return{obj.count};
};
```- ๊ธฐ์กด CounterB ์ปดํฌ๋ํธ์์ React.memo ์ ๊ฑฐ
```javascript
// OptimizeTest.jsconst areEqual = (prevProps, nextProps) => {
if (prevProps.obj.count === nextProps.obj.count) {
return true; // ์ด์ props์ ํ์ฌ props๊ฐ ๊ฐ๋ค -> ๋ฆฌ๋ ๋ ๋ฐ์ ์ ํจ
}
return false; // ์ด์ props์ ํ์ฌ props๊ฐ ๋ค๋ฅด๋ค -> ๋ฆฌ๋ ๋ ๋ฐ์
};// -------------------------------------------------
// ์ถ์ฝ๋ ์ฝ๋
const areEqual = (prevProps, nextProps) => {
return prevProps.obj.count === nextProps.obj.count;
};
```- ๋ ๊ฐ์ ๊ฐ์ฒด ์ธ์๋ฅผ ๋ฐ์ ๊ฐ์ด ๊ฐ์์ง ๋น๊ตํ์ฌ `๊ฐ์ผ๋ฉด true`, `๋ค๋ฅด๋ฉด false`๋ฅผ ๋ฆฌํดํ๋ ํจ์ `areEqual`์ ์์ฑ
```javascript
// OptimizeTest.jsconst MemoizedCounterB = React.memo(CounterB, areEqual);
...
Counter B
{
setObj({ count: obj.count });
}}
>
B button
...
```- React.memo์ ๊ธฐ์กด์ ์ปดํฌ๋ํธ ํจ์ `CounterB`์ ๊ฐ์ ๋น๊ตํ๋ ํจ์ `areEqual`์ ์ธ์๋ก ์ฃผ๋ฉด ์๋ก์ด ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํจ
- ์ด ํจ์๋ฅผ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์ฌ์ฉํ๋ฉด props๋ก ๋ฐ์ ๊ฐ์ฒด์ ์์ ๋น๊ต๋ฅผ ํ์ง์๊ณ , ๋ ๋ฒ์งธ ์ธ์์ areEqual์ด ๋ฆฌํดํ๋ true/false์ ๋ฐ๋ผ ๋ฆฌ๋ ๋๋ฅผ ๊ฒฐ์ ํ๊ฒ ๋๋ค.
## 11. ์ต์ ํ3 ์ปดํฌ๋ํธ & ํจ์ ์ฌ์ฌ์ฉ - useCallback
### 11-1. ์ต์ ํํ ์์ ์ฐพ๊ธฐ
### - React Developer Tools ํ์ฉ
- ํฌ๋กฌ ๋ธ๋ผ์ฐ์ ์ ํ์ฅ ์ฑ์ผ๋ก ๋ฉํ(Meta)์์ ๊ฐ๋ฐํจ
- ๋ธ๋ผ์ฐ์ ์์ ํ์ด์ง๊ฐ React ๊ธฐ๋ฐ์ผ๋ก ์ ์๋์๋์ง ํ์ธํ ์ ์์
- ์ต์ ์์ `Highlight updates when components render`๋ฅผ ํ์ฑํํ๋ฉด ํ์ฌ `๋ฆฌ๋ ๋`๋ง๋๋ ์ปดํฌ๋ํธ๋ฅผ `ํ์ด๋ผ์ดํธ` ํด์ค
![๋ฆฌ์กํธ ๊ฐ๋ฐ ๋๊ตฌ ํ์ด๋ผ์ดํธ ๊ธฐ๋ฅ](README_img/React_highlight.gif)
<์ผ๊ธฐ ๋ด์ฉ์ด ์ ๋ฐ์ดํธ๋ ๋๋ง๋ค DiaryEditor.js ์์ญ์ด ๋ ธ๋์์ผ๋ก ํ์ด๋ผ์ดํธ๋จ>
### - ๋ฆฌ๋ ๋๋ง์ด ์ผ์ด๋๋ ๊ฒฝ์ฐ
- ๋ณธ์ธ์ด ๊ฐ์ง state๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ
- ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒฝ์ฐ
- ์์ ์ด ๋ฐ์ props๊ฐ ๋ณ๊ฒฝ๋๋ ๊ฒฝ์ฐ
### 11-2. useCallback
- ReactHooks ์ค ํ๋
- useCallback์ useMemo์ ๋์ผํ๊ฒ ๊ตฌ์ฑ๋๋ `useMemo`๋ ๋์ผํ `์ฐ์ฐ์ ๊ฒฐ๊ณผ ๊ฐ`์ ๋ฆฌํดํ๋ ๋ฐ๋ฉด, `useCallback`์ `์ฝ๋ฐฑํจ์`๋ฅผ ๋ฆฌํดํ๋ค.
- dependency ๋ฐฐ์ด์ ๊ฐ์ด ๋ณํํ์ง ์์ผ๋ฉด ๋์ผํ ์ฝ๋ฐฑํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋์```javascript
// useCallback ๊ธฐ๋ณธ๊ตฌ์ฑ(์ฝ๋ฐฑํจ์, dependency ๋ฐฐ์ด)const memoizationCallback = useCallback(
() => {
doSometing(a, b);
}, [a, b]
);
```
### 11-3. useCallback ์ฌ์ฉ
- DiaryList์์ ๋ณํ๊ฐ ์์ ๊ฒฝ์ฐ, data ๋ณ์๊ฐ ์ ๋ฐ์ดํธ๋๊ณ data๋ฅผ state๋ก ๊ฐ์ง๋ App.js(๋ถ๋ชจ ์ปดํฌ๋ํธ)๋ ๋ฆฌ๋ ๋๋ง ๋๋ค.
- ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ๋ฆฌ๋ ๋๋ง์ด ์ผ์ด๋๊ธฐ์ ์์ ์ปดํฌ๋ํธ์ธ `DiaryList`์ `DiaryEditor` ๋ชจ๋ ๋ฆฌ๋ ๋๋ง ๋๋ค.
- ํ์ง๋ง data๊ฐ ๋ณํํ๋๋ผ๋ `DiaryEditor`๋ `๋ฆฌ๋ ๋๋ง ๋ ํ์๊ฐ ์๋ค`.
- DiaryEditor๋ ๋ถ๋ชจ App ์ปดํฌ๋ํธ๋ก๋ถํฐ `onCreate` ํจ์๋ฅผ props๋ก ๋ฐ๊ณ ์๋ค.
- ๋ฐ๋ผ์ ๋์ผํ onCreate ํจ์๋ฅผ props๋ก ๋ฐ์ ๋ฆฌ๋ ๋๋ง์ด ์ผ์ด๋์ง ์๋๋ก `onCreate` ํจ์์ `useCallback`์ ์ฌ์ฉํ๊ณ `DiaryEditor`๋ ๋์ผํ props๋ฅผ ๋ฐ์ผ๋ฉด ๋ฆฌ๋ ๋๋ง๋์ง ์๋๋ก `React.memo`๋ฅผ ์ฌ์ฉํ๋ค.```javascript
// DiaryEditor.jsexport default React.memo(DiaryEditor);
``````javascript
// App.jsconst onCreate = useCallback((author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData([newItem, ...data]);
}, []);
```
### 11-4. ์ต์ ํ์ ๋๋ ๋ง
- useCallback์ ์ ์ฉํ ํ์ฌ DiaryEditor์์ ์ผ๊ธฐ๋ฅผ ์์ฑํ๋ฉด, onCreate๊ฐ ์ํ๋๋๋ฐ useCallback์ `dependency`์ `๋น๋ฐฐ์ด`์ ๋ด๊ณ ์๊ธฐ ๋๋ฌธ์ ์ต์ด์ mount๋ ๋์ `data(๋น๋ฐฐ์ด)`๋ง ๊ธฐ์ตํ์ฌ `์์ฑ๋ ์ผ๊ธฐ๋ฅผ ๋น๋ฐฐ์ด์ ๊ณ์ ๋ฃ๊ฒ ๋๋ค`.
- ๋ฐ๋น ์ง ๋ ์ ๋ฌผ๋ถ๊ธฐ์ ๊ฐ์ ํ์ ๋ฐ์
- `dependency ๋ฐฐ์ด`์ `data`๋ฅผ ๋ฃ๊ฒ ๋๋ฉด, data๋ฅผ `์ฐธ์กฐ`๋ฐ์ ๊ธฐ์กด ์ผ๊ธฐ์ ์๋ก ์์ฑ๋ ์ผ๊ธฐ๋ฅผ ์ถ๊ฐํ๊ฒ ๋์ง๋ง `data๊ฐ ๋ณํ`ํ ๋๋ง๋ค `DiaryEditor๊ฐ ๋ฆฌ๋ ๋`๋์ด useCallback์ ์ฌ์ฉํ `์ด์ ์ด ์ฌ๋ผ์ง๋ค`.
### 11-5. ํจ์ํ ์ ๋ฐ์ดํธ
- setState ํจ์์ ํจ์๋ฅผ ์ ๋ฌํ๋ ๊ฒ
```javascript
// App.jsconst onCreate = useCallback((author, content, emotion) => {
const created_date = new Date().getTime();
const newItem = {
author,
content,
emotion,
created_date,
id: dataId.current,
};
dataId.current += 1;
setData((data) => [newItem, ...data]);
}, []);
```- `setData((data) => [newItem, ...data])`๋ฅผ ๋ณด๋ฉด setData ํจ์ ๋ด์ ํจ์๋ฅผ ์ฌ์ฉํ๊ณ data๋ฅผ ์ธ์๋ก ๋ฐ์ ์ ๋ฐ์ดํธ๋ ๋ฐฐ์ด์ ๋ฆฌํดํ๋ค.
- ์ด๋ ๊ฒ ํ๋ฉด dependency ๋ฐฐ์ด์ data๋ฅผ ๊ฐ์ง์ง ์์ data๊ฐ ์์ ๋๋๋ผ๋ onCreate๋ ๋์ผํ ์ฝ๋ฐฑํจ์๋ฅผ ๋ฆฌํดํ์ฌ ๋ฆฌ๋ ๋๊ฐ ๋ฐ์ํ์ง ์์
- ๋์ setData์์ ์ฝ๋ฐฑํจ์ `์ธ์`๋ก `data`๋ฅผ ์ฐธ์กฐํ๊ธฐ์ data๊ฐ `๋น๋ฐฐ์ด๋ก ์ด๊ธฐํ๋์ง ์๋๋ค`.
## 12. ์ต์ ํ4 - React.memo + useCallback
### 12-1. ์ต์ ํ ํ์ ๋ถ๋ถ
- DiaryItem์์ `์ผ๊ธฐ ํ๋`๊ฐ ์ญ์ ๋ ๊ฒฝ์ฐ, `๋ชจ๋ ์ผ๊ธฐ๋ค์ด ๋ฆฌ๋ ๋๋ง`๋๋ ๊ฒ์ ์ ์ ์์
- ์ผ๊ธฐ์ ๊ฐ์๊ฐ ๋ง์ด ์์ ๊ฒฝ์ฐ, ์ฑ๋ฅ์ ์ข์ง ์์
### 12-2. React.memo์ useCallback ์ ์ฉ
```javascript
// DiaryItem.jsconst DiaryItem = ({onEdit, onRemove, author, content, created_date, emotion, id}) => {
...
}export default React.memo(DiaryItem);
```- `React.memo`๋ก `๋์ผํ props`๋ฅผ ๋ฐ์ ๊ฒฝ์ฐ, `๋ฆฌ๋ ๋ ๋ฐ์ ๋ฐฉ์ง`
- ํ์ฌ DiaryItem ์ปดํฌ๋ํธ๋ onEdit, onRemove, author, content, created_date, emotion, id๋ฅผ props๋ก ๋ฐ๊ณ ์์
- author, content, created_date, emotion, id๋ ๋์ผํ ๊ฐ์ props๋ฅผ ๋ฐ๋๋ค๋ ๊ฒ์ React.memo๋ฅผ ํตํด ์ธ์ํ ์ ์์
- ํ์ง๋ง, onEdit, onRemove์ ๊ฒฝ์ฐ, ํจ์์ด๊ธฐ ๋๋ฌธ์ ์์ ๋น๊ต๋ก ๋์ผํ ํจ์์ธ์ง ์๋์ง React.memo์์๋ ํ๋ณ์ด ์ด๋ ค์
- ๋ฐ๋ผ์ `onEdit`๊ณผ `onRemove`์ `useCallback`์ผ๋ก ๋์ผํ ์ฝ๋ฐฑํจ์๋ฅผ ๋ณด๋ด๋ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ด์ผ ํจ
```javascript
// App.jsconst onRemove = useCallback((targetId) => {
setData((data) => data.filter((it) => it.id !== targetId));
}, []);const onEdit = useCallback((targetId, newContent) => {
setData((data) =>
data.map((it) =>
it.id === targetId ? { ...it, content: newContent } : it,
),
);
}, []);
```- App ์ปดํฌ๋ํธ์์ onRemove ํจ์์ onEdit ํจ์์ useCallback ์ฒ๋ฆฌ๋ฅผ ํ๊ณ dependency ๋ฐฐ์ด์ ๋น๋ฐฐ์ด๋ก ์ฒ๋ฆฌ
- useCallback์ ๋๋ ๋ง์ ๋น ์ง์ง ์๊ณ setState์์ ์ต์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ฒํ๊ธฐ ์ํด data๋ฅผ ์ธ์๋ก ๋ฐ๋ ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํจ์ํ ์ ๋ฐ์ดํธ๋ฅผ ์งํ
- ์ด๋ ๊ฒ ํ๋ฉด DiaryItem ํ๋๋ฅผ ์ ๋ฐ์ดํธํ์ฌ๋ ๋ค๋ฅธ DiaryItem๋ค์ด ๋ฆฌ๋ ๋ ๋์ง ์์
![React.memo + useCallback](README_img/React_memo_useCallback.gif)
## 13. ๋ณต์กํ ์ํ ๊ด๋ฆฌ ๋ก์ง ๋ถ๋ฆฌํ๊ธฐ - useReducer
### 13-1. App ์ปดํฌ๋ํธ์ ๋ณต์กํจ
![App์ ์ํ๋ณํํจ์](README_img/React_setStates.png)
- ํ์ฌ App ์ปดํฌ๋ํธ์๋ useState๋ก ์ธํ์ฌ `setState`์ธ ์ํ ๋ณํ ์ฒ๋ฆฌํจ์๊ฐ `์ฝ๋ ์์ ๋ง์ด ํฌํจ`๋์ด์์
- ์ด ์ํ ๋ณํ ํจ์๋ค์ ๋ชจ๋ `data๋ฅผ ์ฌ์ฉ`ํ๊ณ ์๊ธฐ ๋๋ฌธ์ App ์ปดํฌ๋ํธ์์ ์์ฑ๋์์
- App ์ปดํฌ๋ํธ ์ฝ๋๊ฐ ๋ณต์กํ๊ณ ๊ธธ์ด์ง
- ์ํ ๋ณํ ํจ์๊ฐ ๋ง์์ง์๋ก ๋ ๋ณต์กํด์ง
### 13-2. useReducer
- React Hooks ์ค ํ๋
- ์ปดํฌ๋ํธ์์ `์ํ ๋ณํ ๋ก์ง`์ `๋ถ๋ฆฌ`ํ์ฌ ๋ฐ๋ก ๊ด๋ฆฌ
```jsx
const Counter = () => {
const [count, setCount] = useState(0);
const add1 = () => {
setCount(count + 1);
};
const add10 = () => {
setCount(count + 10);
};
const add100 = () => {
setCount(count + 100);
};
return (
{count}
add 1
add 10
add 100
)
};
```
```jsx
// ์ํ ๊ด๋ฆฌ ๋ก์ง ๋ถ๋ฆฌconst reducer = (state, action) => {
switch (action.type) {
case 1:
return state + 1;
case 10:
return state + 10;
case 100:
return state + 100;
default:
return state;
}
}
```- ์ํ ๋ณํ ๋ก์ง์ ๋ฐ๋ก ๋ถ๋ฆฌํ์ฌ `switch-case ๋ฌธ๋ฒ`์ฒ๋ผ ํ์ฉ
- `state` : ํ์ฌ ๊ฐ์ฅ ์ต์ ์ state
- `action` : dispatch ํธ์ถ ์ ์ ๋ฌํ ์ธ์์ธ `action ๊ฐ์ฒด`
```jsx
// useReducer ์ ์ฉconst Counter = () => {
const [count, dispatch] = useReducer(reducer, 1);
return (
{count}
dispatch({ type: 1})}>add 1
dispatch({ type: 10})}>add 10
dispatch({ type: 100})}>add 100
)
};
```- useState์ ๊ฐ์ด `๋น๊ตฌ์กฐํ ํ ๋น` ์ฌ์ฉ
- `count` : 0๋ฒ ์ธ๋ฑ์ค๋ก ์ํ(state)
- `dispatch` : 1๋ฒ ์ธ๋ฑ์ค๋ก ์ํ ๋ณํ๋ฅผ ์ผ์ผํค๋(`raise`) ํจ์
- `useReducer()` : ์ํ ๋ณํ ๊ด๋ฆฌ React Hook
- `reducer` : 0๋ฒ ์ธ๋ฑ์ค๋ก dispatch์์ ์ผ์ด๋ `์ํ๋ณํ action์ ์ฒ๋ฆฌ`ํด์ฃผ๋ ํจ์
- `1` : ์ํ์ธ count์ `์ด๊ธฐ ๊ฐ`
- `dispatch({ type: 1 })` : dispatch ํธ์ถ ์, ์ ๋ฌํ๋ {type: 1}๊ณผ ๊ฐ์ ๊ฐ์ฒด๋ฅผ `action ๊ฐ์ฒด`๋ผ๊ณ ํจ
### 13-3. App ์ปดํฌ๋ํธ์ useReducer ์ ์ฉํ๊ธฐ
```jsx
// App.js// reducer ํจ์ ๋ฐ๋ก ์์ฑ
const reducer = (state, action) => {
switch (action.type) {
// ์ฒ์ Mount ๋ ๋,
case "INIT": {
return action.data; // action์ ๋ด๊ธด data ๊ทธ๋๋ก ๋ฆฌํด
}
// ์์ฑํ ๋,
case "CREATE": {
const created_date = new Date().getTime(); // ์์ฑ์ผ์ ๋ฐ๋ก ์ฒ๋ฆฌ
const newItem = {
...action.data,
created_date,
}; // ๋ฐ์ action์ data์ ์์ฑ์ผ์ ์ถ๊ฐํ ์๋ก์ด ์ผ๊ธฐ newItem
return [newItem, ...state]; // ๊ธฐ์กด ์ผ๊ธฐ state์ newItem ์ถ๊ฐํ์ฌ ๋ฆฌํด
}
// ์ญ์ ํ ๋,
case "REMOVE": {
// ํด๋น id๊ฐ ์๋ ์ผ๊ธฐ๋ง ๋ชจ์์ ๋ฆฌํด
return state.filter((it) => it.id !== action.targetId);
}
// ์์ ํ ๋,
case "EDIT": {
// ํด๋น id์ธ ์ผ๊ธฐ๋ฅผ ์ฐพ์์ content๋ง action์ผ๋ก ๋ฐ์ newContent๋ก ์์ ํ์ฌ ๋ฆฌํด
return state.map((it) =>
it.id === action.targetId ? { ...it, content: action.newContent } : it,
);
}
default:
return state;
}
};
``````jsx
// App.js// useState ๋์ useReducer ์ฌ์ฉ
const [data, dispatch] = useReducer(reducer, []);// ๊ธฐ์กด์ setData๋ฅผ ์ด์ฉํ๋ ๋ชจ๋ ์ํ ๋ณํ ํจ์ ์์ -> dispatch ์ ์ฉ
const getData = async () => {
const res = await fetch(
"https://jsonplaceholder.typicode.com/comments",
).then((res) => res.json());
const initData = res.slice(0, 20).map((it) => {
return {
author: it.email,
content: it.body,
emotion: Math.floor(Math.random() * 5) + 1,
created_date: new Date().getTime(),
id: dataId.current++,
};
});dispatch({ type: "INIT", data: initData });
};const onCreate = useCallback((author, content, emotion) => {
dispatch({
type: "CREATE",
data: { author, content, emotion, id: dataId.current },
});
dataId.current += 1;
}, []);const onRemove = useCallback((targetId) => {
dispatch({ type: "REMOVE", targetId });
}, []);const onEdit = useCallback((targetId, newContent) => {
dispatch({ type: "EDIT", targetId, newContent });
}, []);
```- `dispatch`๋ ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ์ด๋ ํ ์ฒ๋ฆฌ์์ด ๋ฐ๋ ๊ฒ์ด ๊ฐ๋ฅํ๊ธฐ์ ๊ธฐ์กด์ useCallback ๋๋ ๋ง์์ ํจ์ํ ์ ๋ฐ์ดํธ ์ฒ๋ฆฌ์ ๊ฐ์ `๋ณ๋์ ์กฐ์น๋ฅผ ํ์ง ์์๋ ๊ด์ฐฎ์`
## 14. ์ปดํฌ๋ํธ ํธ๋ฆฌ์ ๋ฐ์ดํฐ ๊ณต๊ธํ๊ธฐ - Context
### 14-1. ์ ๋ฌ๋๋ props์ ์ปดํฌ๋ํธ์์ ํ์ฉ๋๋ props
![props ๊ตฌ๋ถ](README_img/React_Context.png)
- App ์ปดํฌ๋ํธ์์ ์์ฑ๋ `onEdit` ํจ์์ `onRemove` ํจ์๋ `DiaryList` ์ปดํฌ๋ํธ์๋ `์ ๋ฌ`๋ง ์ด๋ฃจ์ด์ง๊ณ `DiaryItem` ์ปดํฌ๋ํธ์์ ์ค์ง์ ์ผ๋ก `์ฌ์ฉ`๋จ (๋นํจ์จ์ )
- ์ ๋ฌ๋๋ props๊ฐ ๋ง์์ง๊ฒ ๋๋ฉด ์ค๊ฐ์ ์ด๋ฆ์ ์์ ํ๋ ๋ฑ์ ์์ ์ ํ ๋, ๋ชจ๋ ์์ ํด์ฃผ๋ ๋ฐ๋ณต ์์ ์ ํด์ผํจ
- ์ด๋ฅผ `Props ๋๋ฆด๋ง`์ด๋ผ๊ณ ํจ
### 14-2. Provider, Context
![Provider](README_img/React_Provider.png)
- `Provider ์ปดํฌ๋ํธ`์๊ฒ App ์ปดํฌ๋ํธ์ `๋ชจ๋ ๋ฐ์ดํฐ`๋ฅผ ์ ๋ฌ๋ฐ์
- ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ Provider๋ก๋ถํฐ ํ์ํ props๋ฅผ `์ง์ ์ ๋ฌ` ๋ฐ์
- ์ด์ฒ๋ผ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ Provider์ ์์ ์ปดํฌ๋ํธ๊ฐ ๋์ด props๋ฅผ ์ง์ ์ ๋ฌ๋ฐ์ ์ ์๋ ์์ญ์ `Context`, ๋ฌธ๋งฅ์ด๋ผ๊ณ ํจ
- ๊ฐ์ Context์ ์ํด์์ง ์์ผ๋ฉด prop์ provider๋ก๋ถํฐ ์ง์ ์ ๋ฌ ๋ฐ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅ
### - Context ์์ฑ
```jsx
const MyContext = React.createContext(defaultValue);
```- Context๋ฅผ ์ฝ๊ฒ ์์ฑํ๋๋ก React์ `Context API`๋ฅผ ํ์ฉํ ์ ์์
- `createContext` : Context ์์ฑ ํจ์
### - Context Provider๋ฅผ ํตํ ๋ฐ์ดํฐ ๊ณต๊ธ
```jsx
{/*์ด context์์ ์์นํ ์์ ์ปดํฌ๋ํธ๋ค*/}
```
- `Childern Props` : ์ปดํฌ๋ํธ๋ฅผ ์ปดํฌ๋ํธ๋ก ๊ฐ์ธ ์ปดํฌ๋ํธ๋ฅผ props๋ก ์ ๋ฌ
- ์์ ์ปดํฌ๋ํธ ์์ ์ ํ์ ์์
### 14-3. ์ฝ๋์ ์ ์ฉ
### - data๋ฅผ ๊ด๋ฆฌํ Context ์์ฑ
```jsx
// App.jsexport const DiaryStateContext = React.createContext();
```- ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ importํ ์ ์๋๋ก `export` ํด์ฃผ์ด์ผ ํจ
### - data Context์ Provider ์ปดํฌ๋ํธ๋ก ์์ ์ปดํฌ๋ํธ ๊ฐ์ธ๊ธฐ
```jsx
// App.js
์ ์ฒด ์ผ๊ธฐ : {data.length}
๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๊ฐ์ : {goodCount}
๊ธฐ๋ถ ๋์ ์ผ๊ธฐ ๊ฐ์ : {badCount}
๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๋น์จ : {goodRatio}
```
- DiaryStateContext์ Provider ์ปดํฌ๋ํธ์ value๋ฅผ data๋ก ์ง์
### - DiaryList ์ปดํฌ๋ํธ์์ ํด๋น props ๋ฐ๊ธฐ
```jsx
// DiaryList.jsconst DiaryList = () => {
const diaryList = useContext(DiaryStateContext);
};
```- ๊ธฐ์กด์ props๋ ์ ๊ฑฐํ๊ณ React Hooks์ ํ๋์ธ `useContext`๋ฅผ ํตํด Context์์ diaryList ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
### - ์ํ ๋ณํ ํจ์๋ ๊ด๋ฆฌํ Context ์์ฑ
```jsx
export const DiaryDispatchContext = React.createContext();
```- `data`์ `์ํ ๋ณํ ํจ์`๋ฅผ `ํ๋์ Context`์์ ๊ด๋ฆฌํ ๊ฒฝ์ฐ, ๊ธฐ์กด์ React.memo์ useCallback์ผ๋ก ๋ฆฌ๋ ๋๋ฅผ ๋ฐฉ์งํ `์ต์ ํ๊ฐ ํ๋ ค๋ฒ๋ฆฌ๊ณ ` data๊ฐ ์ ๋ฐ์ดํธ ๋ ๋๋ง๋ค ๋ค์ `๋ฆฌ๋ ๋๊ฐ ๋ฐ์ํจ`
- ๋ฐ๋ผ์ ๋ค๋ฅธ Context์์ ๊ด๋ฆฌํด์ผํจ
### - ์ํ ๋ณํ ํจ์๋ค ํ๋์ ๋ณ์์ ๋ด๊ธฐ
```jsx
// App.jsconst memoizedDispatches = useMemo(() => {
return { onCreate, onRemove, onEdit };
}, []);// ----------------------------------------------
// ์ด๋ ๊ฒ ๋ฌถ์ผ๋ฉด ์ ๋จ
const memoizedDispatches = { onCreate, onRemove, onEdit };
```- ๋จ์ ๊ฐ์ฒด๋ก ๋ฌถ์ด์ Context์ value๋ก ์ ๋ฌํ๋ฉด App ์ปดํฌ๋ํธ๊ฐ ์ ๋ฐ์ดํธ๋ ๋, ์ํ ๋ณํ ํจ์๋ค๋ ์ฌ์์ฑ๋จ
- ๋ฐ๋ผ์ ํจ์์ ์ฐ์ฐ ๊ฒฐ๊ณผ ๊ฐ์ ๊ธฐ์ตํ๋ useMemo๋ฅผ ์ฌ์ฉํด์ผ ํจ
### - ์ํ ๋ณํ ํจ์ Provider ์ปดํฌ๋ํธ ์ ์ฉ
```jsx
// App.js
์ ์ฒด ์ผ๊ธฐ : {data.length}
๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๊ฐ์ : {goodCount}
๊ธฐ๋ถ ๋์ ์ผ๊ธฐ ๊ฐ์ : {badCount}
๊ธฐ๋ถ ์ข์ ์ผ๊ธฐ ๋น์จ : {goodRatio}
```
- ํด๋น Context์ Provider ์ปดํฌ๋ํธ๋ฅผ ์ค์ฒฉํ์ฌ props๋ฅผ ์ ๋ฌ
- value๋ก ์์ ์์ฑํ `memoizedDispatches`๋ฅผ ์ง์
- `onCreate={onCreate}`์ ๊ฐ์ ์ํ ๋ณํ ํจ์ props๋ค ์ ๊ฑฐ
### - Provider์์ props ๋ฐ๊ธฐ
```jsx
// DiaryEditor.js
const DiaryEditor = () => {
const { onCreate } = useContext(DiaryDispatchContext);
};```
- ๊ธฐ์กด props ์ญ์
- useContext๋ก DiaryDispatchContext์์ onCreate ๊ฐ์ ธ์ค๊ธฐ
```jsx
// DiaryItem.jsconst DiaryItem = ({ id, author, content, emotion, created_date }) => {
const {onEdit, onRemove} = useContext(DiaryDispatchContext);
}
```- ๊ธฐ์กด props์์ ์ํ ๋ณํ ํจ์ props ์ญ์
- useContext๋ก DiaryDispatchesContext์์ onEdit, onRemove ๊ฐ์ ธ์ค๊ธฐ