Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jeonggoncho/react-practice-emotion-diary

๐Ÿ“˜ React๋ฅผ ์ด์šฉํ•˜์—ฌ ์ œ์ž‘ํ•œ 'Emotion Diary ํด๋ก ์ฝ”๋”ฉ'์ž…๋‹ˆ๋‹ค.
https://github.com/jeonggoncho/react-practice-emotion-diary

clone-coding javascript jsx react

Last synced: 19 days ago
JSON representation

๐Ÿ“˜ React๋ฅผ ์ด์šฉํ•˜์—ฌ ์ œ์ž‘ํ•œ 'Emotion Diary ํด๋ก ์ฝ”๋”ฉ'์ž…๋‹ˆ๋‹ค.

Awesome Lists containing this project

README

        

# ๊ฐ์„ฑ ์ผ๊ธฐ์žฅ ๋งŒ๋“ค๊ธฐ

๋ณธ ํ”„๋กœ์ ํŠธ๋Š” ["ํ•œ์ž… ํฌ๊ธฐ๋กœ ์ž˜๋ผ ๋จน๋Š” ๋ฆฌ์•กํŠธ ๊ฐ•์˜"](https://www.udemy.com/course/winterlood-react-basic/)์˜ "๊ฐ์„ฑ ์ผ๊ธฐ์žฅ ๋งŒ๋“ค์–ด ๋ณด๊ธฐ"๋ฅผ ํด๋ก ํ•œ ํ”„๋กœ์ ํŠธ๋กœ "๋ฆฌ์•กํŠธ"์˜
๊ธฐ์ดˆ ์ง€์‹์„ ํ•™์Šตํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œํ•˜์˜€์Šต๋‹ˆ๋‹ค.


## ๋ชฉ์ฐจ

1. [ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… - React SPA & CSR](#1-ํŽ˜์ด์ง€-๋ผ์šฐํŒ…---react-spa--csr)
2. [ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… - React Router ๊ธฐ๋ณธ](#2-ํŽ˜์ด์ง€-๋ผ์šฐํŒ…---react-router-๊ธฐ๋ณธ)
3. [ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… - React Router ์‘์šฉ](#3-ํŽ˜์ด์ง€-๋ผ์šฐํŒ…---react-router-์‘์šฉ)
4. [ํ”„๋กœ์ ํŠธ ๊ธฐ์ดˆ๊ณต์‚ฌ 1](#4-ํ”„๋กœ์ ํŠธ-๊ธฐ์ดˆ๊ณต์‚ฌ-1)
5. [ํ”„๋กœ์ ํŠธ ๊ธฐ์ดˆ๊ณต์‚ฌ 2](#5-ํ”„๋กœ์ ํŠธ-๊ธฐ์ดˆ๊ณต์‚ฌ-2)
6. [ํŽ˜์ด์ง€ ๊ตฌํ˜„](#6-ํŽ˜์ด์ง€-๊ตฌํ˜„)
7. [๋ฒ„๊ทธ](#7-๋ฒ„๊ทธ)
8. [LocalStorage](#8-localstorage)
9. [์ตœ์ ํ™”](#9-์ตœ์ ํ™”)
10. [๋ฐฐํฌ ์ค€๋น„ & ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ](#10-๋ฐฐํฌ-์ค€๋น„--ํ”„๋กœ์ ํŠธ-๋นŒ๋“œ)
11. [Firebase ๋ฐฐํฌ](#11-firebase-๋ฐฐํฌ)
12. [Open Graph](#12-open-graph)




## 1. ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… - React SPA & CSR

### 1-1. Page Routing

### - Routing

- ์–ด๋–ค ๋„คํŠธ์›Œํฌ ๋‚ด์—์„œ ํ†ต์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ `๊ฒฝ๋กœ๋ฅผ ์„ ํƒ`ํ•˜๋Š” ์ผ๋ จ์˜ ๊ณผ์ •
- `Router` : ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ๋กœ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ง€์ •ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ

![๋ผ์šฐํŒ…](README_img/React_Router.png)

<๋„คํŠธ์›Œํฌ ํ†ต์‹  ์‹œ, ๋ผ์šฐํ„ฐ>


### - Page Routing์ด๋ž€?

- ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณด๋‚ธ `์š”์ฒญ`์„ ๋ฐ”ํƒ•์œผ๋กœ ์•Œ๋งž์€ `ํŽ˜์ด์ง€(ํ…œํ”Œ๋ฆฟ)`์„ ์›น ์„œ๋ฒ„์—์„œ `์‘๋‹ต`ํ•˜๋Š” ๊ฒƒ
- ์ƒˆ๋กœ์šด ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋ฅผ ์‘๋‹ตํ•˜๊ฒŒ ๋˜๊ณ  ๊ทธ ๊ณผ์ •์—์„œ ๋ธŒ๋ผ์šฐ์ €๋Š” `์ƒˆ๋กœ๊ณ ์นจ(ํ™”๋ฉด๊นœ๋ฐ•์ž„)`์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋จ

![MPA ํŽ˜์ด์ง€ ๋ผ์šฐํŒ…](README_img/React_page_routing.png)

- `MPA(Multi page Application)` : ์œ„์˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด `์—ฌ๋Ÿฌ ๊ฐœ์˜ ํŽ˜์ด์ง€`๋ฅผ ์ค€๋น„ํ•ด๋‘์—ˆ๋‹ค๊ฐ€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ํŽ˜์ด์ง€๋ฅผ ์‘๋‹ตํ•˜๋Š” ๋ฐฉ์‹


### 1-2. SPA(Single page Application)

- React๋Š” MPA์ด ์•„๋‹Œ `SPA(Singlepage Application`์˜ ๋ฐฉ์‹์„ ์‚ฌ์šฉ
- ์–ด๋– ํ•œ ์š”์ฒญ์ด ์ฃผ์–ด์ ธ๋„ `๋‹จ์ผํ•œ ํŽ˜์ด์ง€`๋ฅผ ์‘๋‹ตํ•จ
- ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ๊ณ ์นจ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์•„ ํŽ˜์ด์ง€์˜ ์ด๋™์ด `๋น ๋ฅด๊ณ  ์พŒ์ ํ•จ`

![SPA ํŽ˜์ด์ง€ ๋ผ์šฐํŒ…](README_img/React_page_routing_SPA.png)


### - SPA ์ž‘๋™ ์›๋ฆฌ

![SPA ์ž‘๋™](README_img/React_SPA_action.png)

1. ์ดˆ๊ธฐ์— ์›น ์„œ๋ฒ„์—์„œ `๋‹จ์ผ ํŽ˜์ด์ง€`์™€ ํ•จ๊ป˜ `React app`์„ ์ „๋‹ฌํ•ด์คŒ
2. ์ดํ›„, ์—…๋ฐ์ดํŠธ ์‹œ, ์›น ์„œ๋ฒ„๊ฐ€ ์•„๋‹Œ ๋ธŒ๋ผ์šฐ์ €์˜ `React app`์—์„œ `ํŽ˜์ด์ง€๋ฅผ ์—…๋ฐ์ดํŠธ` ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋น ๋ฆ„(ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์ฒ˜๋ฆฌ)
3. ์„œ๋ฒ„์˜ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ
4. ํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•˜๊ฑฐ๋‚˜, ์ƒˆ๋กœ์šด `๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”`ํ•œ ๊ฒฝ์šฐ์—๋Š” ์ผ๋‹จ ํ™”๋ฉด์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  `๋ฐ์ดํ„ฐ๋งŒ ์„œ๋ฒ„์—์„œ ์‘๋‹ต`๋ฐ›์•„ ์ฑ„์šฐ๊ฒŒ ๋จ


### 1-3. CSR (Client Side Rendering)

- `ํด๋ผ์ด์–ธํŠธ ์ธก`์—์„œ `ํ™”๋ฉด์„ ๋ Œ๋”๋ง`ํ•˜๋Š” ๊ฒƒ




## 2. ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… - React Router ๊ธฐ๋ณธ

### 2-1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

- `npx create-react-app emotion-diary`๋กœ React ์•ฑ ์ƒ์„ฑ


### 2-2. React Router

- React์—์„œ Page Routing์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” `React Router`๊ฐ€ ํ•„์š”ํ•จ
- `React Router` : ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(CSR)์„ ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
- [React Router ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ณต์‹ ์‚ฌ์ดํŠธ](https://reactrouter.com/en/main)


### - React Router ์„ค์น˜

```bash
$ npm install react-router-dom@(๋ฒ„์ „)
```


![react router ์„ค์น˜ ํ™•์ธ](README_img/React_react_router_package.png)


### - Router๋กœ ๊ด€๋ฆฌํ•  ํŽ˜์ด์ง€ ๋ชจ์Œ ํด๋” ๋ฐ ํŽ˜์ด์ง€ ์ƒ์„ฑ

- src/`pages` ํด๋” ์ƒ์„ฑ
- ํด๋” ์•ˆ์— `Home.js`, `New.js`, `Edit.js`, `Diary.js` ํŒŒ์ผ ์ƒ์„ฑ


```jsx
// src/pages/Home.js

const Home = () => {
return (


Home


์ด๊ณณ์€ ํ™ˆ ์ž…๋‹ˆ๋‹ค.



);
};

export default Home;

// ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋“ค๋„ ๋™์ผํ•˜๊ฒŒ ์„ธํŒ…
```

- ๊ฐ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ตฌ์„ฑ


### - App ์ปดํฌ๋„ŒํŠธ์—์„œ Route ๋ถ„๋ฐฐํ•˜๊ธฐ

<๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ์ปดํฌ๋„ŒํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ>

```jsx
// src/App.js

import {BrowserRouter, Route, Routes} from "react-router-dom";

import Home from "./pages/Home";
import New from "./pages/New";
import Edit from "./pages/Edit";
import Diary from "./pages/Diary";
```

- react-route-dom ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ `BrowserRouter`, `Route`, `Routes` ๊ฐ€์ ธ์˜ค๊ธฐ
- pages ํด๋”์—์„œ ์ƒ์„ฑํ•œ ๊ฐ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ


```jsx
// src/App.js

return (


App.js



}/>
}/>



);
```

- `BrowserRouter` : React Router๋ฅผ ์ ์šฉํ•  `์ „์ฒด App` ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ๊ธฐ
- `Routes` : Route ์ ์šฉํ•  `์ปดํฌ๋„ŒํŠธ๋“ค` ๋ฌถ๊ธฐ
- `Route` : ๊ฐ๊ฐ์˜ path(๊ฒฝ๋กœ)์™€ element(์ปดํฌ๋„ŒํŠธ) `๋งคํ•‘`
- ๋”ฐ๋ผ์„œ ๋งคํ•‘๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ํ•ด๋‹น url ๊ฒฝ๋กœ์ธ ๊ฒฝ์šฐ์—๋งŒ ๋ Œ๋”๋ง ๋จ

![๊ธฐ๋ณธ index ํ™”๋ฉด](README_img/React_basic_path.png)


![new ๊ฒฝ๋กœ ํ™”๋ฉด](README_img/React_new_path.png)


### - Base Template

- Routes ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ์‹ธ์ง€ ์•Š์€ `

App.js

`์˜ ๊ฒฝ์šฐ ๊ณ„์† ๋ Œ๋”๋ง๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Œ
- ๋”ฐ๋ผ์„œ Base Template์œผ๋กœ `๊ณ„์† ํ™”๋ฉด์— ์ถœ๋ ฅ`๋˜์–ด์•ผํ•˜๋Š” header, navbar, footer๋Š” `Routes์— ๊ฐ์‹ธ์ง€ ์•Š์Œ`์œผ๋กœ์จ ๊ณ„์† ํ™”๋ฉด์— ์ถœ๋ ฅ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Œ


### - ํ™”๋ฉด ์ด๋™์‹œํ‚ค๋Š” ์š”์†Œ ๋งŒ๋“ค๊ธฐ Link

<๊ธฐ์กด a ํƒœ๊ทธ๋กœ ๋งํฌ ์ž‘์„ฑ>

```jsx
// src/App.js

New๋กœ ์ด๋™
```

- a ํƒœ๊ทธ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋งํฌ๋ฅผ ๋งŒ๋“ค๊ณ  href ์†์„ฑ์œผ๋กœ ๊ฒฝ๋กœ ์—ฐ๊ฒฐ
- ํ•ด๋‹น ๋งํฌ ํด๋ฆญ ์‹œ, ํ•ด๋‹น url๋กœ ์ด๋™์„ ํ•˜์ง€๋งŒ `์ƒˆ๋กœ๊ณ ์นจ`์ด ๋ฐœ์ƒ๋จ(MPA์ฒ˜๋Ÿผ ์ž‘๋™ํ•จ)


- components ํด๋” ๋งŒ๋“ค๊ณ  ํ…Œ์ŠคํŠธ์šฉ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ

```jsx
// src/components/RouterTest.js

import {Link} from "react-router-dom";

const RouteTest = () => {
return (


HOME


DIARY


NEW


EDIT

);
};

export default RouteTest;
```

- `react-router-dom` ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” `Link` ๊ฐ€์ ธ์˜ค๊ธฐ
- Link์˜ `to ์†์„ฑ`์— `๊ฒฝ๋กœ(path)` ๋งคํ•‘ํ•˜๊ธฐ
- ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ณด๋‚ด๊ธฐ


```jsx
// src/App.js

import RouteTest from "./components/RouteTest";


App.js



}/>
}/>
}/>
}/>


```

- ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ App ์ปดํฌ๋„ŒํŠธ์— ๊ฐ€์ ธ์˜ค๊ธฐ
- ์ด๋ ‡๊ฒŒ Link ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด SPA๋กœ ๋™์ž‘ํ•˜๋ฉฐ ํ™”๋ฉด ์ด๋™ ์‹œ, ์ƒˆ๋กœ๊ณ ์นจ์ด ๋˜์ง€ ์•Š์Œ


### 2-3. React Router ์ •๋ฆฌ

- ํ•ด๋‹น ๊ฒฝ๋กœ๋งˆ๋‹ค ๋ Œ๋”๋ง ๋  ํŽ˜์ด์ง€๋ฅผ ๋งคํ•‘ํ•ด์ฃผ๊ฒŒ ๋˜๋ฉด url์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•จ
- ์‹ค์ œ๋กœ๋Š” ์ด๋™ํ•˜๊ธฐ๋ณด๋‹ค๋Š” ์ฃผ์–ด์ง„ `index ๋‹จ์ผ ํŽ˜์ด์ง€`์—์„œ `url`์— ๋”ฐ๋ผ์„œ `์ปดํฌ๋„ŒํŠธ๋“ค์„ ๊ต์ฒด`ํ•ด์ค€๋‹ค๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Œ
- ํ™”๋ฉด ์ „ํ™˜์˜ ์†๋„๊ฐ€ ๋งค์šฐ ๋น ๋ฆ„




## 3. ํŽ˜์ด์ง€ ๋ผ์šฐํŒ… - React Router ์‘์šฉ

### 3-1. ํ•™์Šต๋ชฉํ‘œ

- `useParams` : Path Variable(๊ฒฝ๋กœ ๋ณ€์ˆ˜)
- `useSearchParams` : Query String(url๋กœ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ)
- `useNavigate` : Page Moving(Link๊ฐ€ ์•„๋‹Œ ํ•จ์ˆ˜๋กœ ํŽ˜์ด์ง€ ์ด๋™)


### 3-2. Path Variable & useParams

### - Diary ํŽ˜์ด์ง€

- ๊ฒฝ๋กœ : '/diary'
- ํŠน์ง• : ํŠน์ • ์ผ๊ธฐ์˜ ์ƒ์„ธ ํŽ˜์ด์ง€๋กœ์„œ ์–ด๋–ค ์ผ๊ธฐ๋ฅผ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•  ์ง€ ์ „๋‹ฌ ๋ฐ›์•„์•ผํ•จ
- ex) /diary/1 -> 1๋ฒˆ ์ผ๊ธฐ


### - Path Variable

```jsx
// App.js


App.js



}/>
}/>
}/>
}/>


```

- diary ๊ฒฝ๋กœ ๋’ค์— `:id`๋ฅผ ํฌํ•จ์‹œํ‚ด
- ๊ฐ id ๊ฐ’์— ๋”ฐ๋ผ์„œ diary ๊ฒฝ๋กœ๋ฅผ ๋ถ„๋ฐฐ์‹œํ‚ด


### - useParams

```jsx
// pages/Diary.js

import {useParams} from "react-router-dom";

const Diary = () => {
const {id} = useParams();
console.log(id);
};
```

- React ์ž์ฒด์—์„œ๊ฐ€ ์•„๋‹Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” `์ปค์Šคํ…€ Hooks`์ธ `useParams` ๊ฐ€์ ธ์˜ค๊ธฐ
- id ๊ฐ’์„ useParams๋กœ ์„ค์ •ํ•˜๋ฉด ํ•ด๋‹น id ๊ฐ’์„ Diary ํŽ˜์ด์ง€์—์„œ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Œ


![useParams](README_img/React_useParams.gif)


### 3-3. Query String & useSearchParams

### - Query String

```
- Query ์ „๋‹ฌ ์˜ˆ์‹œ

URL : https://site.com/edit?id=10&mode=dark
```

- `Query` : ์›น ํŽ˜์ด์ง€์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•
- `Query String` : url์—์„œ ๋ฌผ์Œํ‘œ ํ‚ค์›Œ๋“œ('?') ๋’ค์— ์ „๋‹ฌ๋˜๋Š” ๋ฐ์ดํ„ฐ
- `name=value`์˜ ํ˜•ํƒœ๋กœ '&'๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌถ์Œ


### - Query String์€ path ์„ค์ • ์•ˆํ•จ

- ์•ž์„  `Path Variable`์˜ ๊ฒฝ์šฐ, `/diary/:id`์™€ ๊ฐ™์ด ๋ฐ›์„ Path Variable์„ ๊ฒฝ๋กœ์— ์„ค์ •ํ•ด์ฃผ์—ˆ์Œ
- ๋ฐ˜๋ฉด, `Query String`์˜ ๊ฒฝ์šฐ, ๊ฒฝ๋กœ์— ๋ณ„๋„์˜ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•„๋„ ๋จ


### - useSearchParams

```jsx
// pages/Edit.js

import {useSearchParams} from "react-router-dom";

const Edit = () => {
const [searchParams, setSearchParams] = useSearchParams()

const id = searchParams.get('id');
console.log('id : ', id);

const mode = searchParams.get('mode');
console.log('mode : ', mode);

...
{
setSearchParams({who: '์กฐ์ •๊ณค'})
}}>QS ๋ฐ”๊พธ๊ธฐ

};
```

- `useSearchParams` : ์ปค์Šคํ…€ Hooks ์ค‘ ํ•˜๋‚˜๋กœ useState์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋น„๊ตฌ์กฐํ™” ํ• ๋‹น์„ ํ†ตํ•ด `searchParams`์™€ `setSearchParams`๋ฅผ ๋ฐ›์Œ
- `searchParams` : ์ „๋‹ฌ๋ฐ›์€ Query String์ด ๊ฐ์ฒด๋กœ ๋‹ด๊ฒจ ์žˆ์œผ๋ฉฐ, `.get()` ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ
- `setSearchParams` : searchParams `๊ฐ์ฒด๋ฅผ ์—…๋ฐ์ดํŠธ`ํ•˜๋Š” ํ•จ์ˆ˜


![useSearchParams ์˜ˆ์‹œ](README_img/React_useSearchParams.gif)


### 3-4. Page Moving & useNavigate

### - useNavigate

```jsx
// pages/Edit.js

import {useNavigate, useSearchParams} from "react-router-dom";

const Edit = () => {
const navigate = useNavigate();

...
// ๊ฒฝ๋กœ ํŽ˜์ด์ง€๋กœ ์ด๋™
{
navigate('/home')
}}>HOME์œผ๋กœ ๊ฐ€๊ธฐ

// ํ•ด๋‹น ์ˆซ์ž๋งŒํผ ๋’ค๋กœ๊ฐ€๊ธฐ
{
navigate(-1)
}}>๋’ค๋กœ๊ฐ€๊ธฐ

}
```

- `useNavigate` : ์ปค์Šคํ…€ Hooks ์ค‘ ํ•˜๋‚˜๋กœ, `ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜`๋ฅผ ๋ฐ˜ํ™˜ํ•จ, ๋”ฐ๋ผ์„œ ์ด ํ•จ์ˆ˜๋ฅผ navigate๋ผ๋Š” ๋ณ€์ˆ˜์— ํ• ๋‹น
- ๋ฒ„ํŠผ onClick์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜์— navigate ํ•จ์ˆ˜๋ฅผ ๋„ฃ๊ณ  `์ธ์ž๋กœ ๊ฒฝ๋กœ`๋ฅผ ๋ฐ›์œผ๋ฉด ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, ํ•ด๋‹น ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•จ
- ๊ฒฝ๋กœ ๋Œ€์‹  `-(์ˆซ์ž)`๋ฅผ ๋„ฃ๊ฒŒ ๋˜๋ฉด ์ˆซ์ž๋งŒํผ `๋’ค๋กœ๊ฐ€๊ธฐ` ์ˆ˜ํ–‰


![ํŽ˜์ด์ง€ ์ด๋™ ๋ฐ ๋’ค๋กœ๊ฐ€๊ธฐ](README_img/React_useNavigate.gif)




## 4. ํ”„๋กœ์ ํŠธ ๊ธฐ์ดˆ๊ณต์‚ฌ 1

### 4-1. ์„ธํŒ… ํ•ญ๋ชฉ

- ํฐํŠธ ์„ธํŒ… : Google Web Fonts ์ด์šฉ
- ๋ ˆ์ด์•„์›ƒ ์„ธํŒ… : ๋ชจ๋“  ํŽ˜์ด์ง€์— ๋ฐ˜์˜๋˜๋Š” ๋ ˆ์ด์•„์›ƒ ์„ธํŒ…
- ์ด๋ฏธ์ง€ ์—์…‹ ์„ธํŒ… : ๊ฐ์ • ํ‘œํ˜„ ์ด๋ฏธ์ง€๋“ค์„ ํ”„๋กœ์ ํŠธ์—์„œ ๋ถˆ๋Ÿฌ์™€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ™˜๊ฒฝ ์„ธํŒ…
- ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ์„ธํŒ… : ๋ชจ๋“  ํŽ˜์ด์ง€์— ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฒ„ํŠผ, ํ—ค๋” ์ปดํฌ๋„ŒํŠธ ์„ธํŒ…


### 4-2. ํฐํŠธ ์„ธํŒ…

- [Google Web Font](https://fonts.google.com/) ์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉํ•  ํฐํŠธ๋ฅผ ์ฐพ์•„ selectํ•˜๊ธฐ
- `@import...` ๊ตฌ๋ฌธ์„ App.css ํŒŒ์ผ ์ƒ๋‹จ์— ๋ถ™์—ฌ๋„ฃ๊ธฐ
- css๋กœ font-family ์†์„ฑ ์ถ”๊ฐ€

```css
/*src/App.css*/

@import url('https://fonts.googleapis.com/css2?family=Nanum+Gothic&family=Noto+Sans+KR&display=swap');

.App {
padding: 20px;
font-family: 'Nanum Gothic', sans-serif;
font-family: 'Noto Sans KR', sans-serif;
}
```


### 4-3. ๋ ˆ์ด์•„์›ƒ ์„ธํŒ…

- ๋ชจ๋“  ํŽ˜์ด์ง€์— ๋ฐ˜์˜๋˜๋Š” ๋ ˆ์ด์•„์›ƒ ์„ธํŒ…

```css
/*src/App.css*/

@import url('https://fonts.googleapis.com/css2?family=Nanum+Gothic&family=Noto+Sans+KR&display=swap');

body {
background-color: #f6f6f6;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Nanum Gothic', sans-serif;
min-height: 100vh;
margin: 0px;
}

@media (min-width: 650px) {
.App {
width: 640px;
}
}

@media (max-width: 650px) {
.App {
width: 90vw;
}
}

#root {
background-color: white;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
}

.App {
min-height: 100vh;
padding-left: 20px;
padding-right: 20px;
}
```

- ๊ธฐ๋ณธ์ ์ธ ๋ ˆ์ด์•„์›ƒ ์„ธํŒ… ์ง„ํ–‰


### 4-4. ์ด๋ฏธ์ง€ ์—์…‹ ์„ธํŒ…

- ๊ฐ์ • ํ‘œํ˜„ ์ด๋ฏธ์ง€ ์ข…๋ฅ˜ 5๊ฐ€์ง€ (๊ธฐ๋ถ„ ์ตœ๊ณ , ์ข‹์Œ, ๊ทธ๋Ÿญ์ €๋Ÿญ, ๋‚˜์จ, ๋”์ฐํ•จ)
- ํ•ด๋‹น ์ด๋ฏธ์ง€ ํŒŒ์ผ `public/assets`์— ๋‹ด๊ธฐ

| | | | | |
|:-----------------------------------------------------------------:|:-----------------------------------------------------------------:|:-----------------------------------------------------------------:|:-----------------------------------------------------------------:|:-----------------------------------------------------------------:|
| ๊ธฐ๋ถ„ ์ตœ๊ณ  | ์ข‹์Œ | ๊ทธ๋Ÿญ์ €๋Ÿญ | ๋‚˜์จ | ๋”์ฐํ•จ |


```tsx
// src/pages/App.js

...
return (


App.js




}/>
}/>
}/>
}/>



);
```


### - process.env.PUBLIC_URL

- ํ•ด๋‹น URL์€ ์–ด๋””์— ์žˆ๋˜์ง€ ๋ฌด์กฐ๊ฑด `public ๋””๋ ‰ํ† ๋ฆฌ`๋ฅผ ๋‚˜ํƒ€๋ƒ„

```tsx

```

- ๋”ฐ๋ผ์„œ ์œ„ ์ด๋ฏธ์ง€ ์ปดํฌ๋„ŒํŠธ์˜ ํ•ด๋‹น ๊ฒฝ๋กœ๋Š” `public/assets/emotion1.png`๋ฅผ ๊ฐ€๋ฆฌํ‚ด
- ๋งŒ์•ฝ ๊ฒฝ๋กœ๊ฐ€ public์œผ๋กœ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, ์•„๋ž˜์™€ ๊ฐ™์ด ์ดˆ๊ธฐํ™” ์ง„ํ–‰

```tsx
// src/pages/App.js

function App() {
const env = process.env;
env.PUBLIC_URL = env.PUBLIC_URL || "";
...
```


### 4-5. ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ์„ธํŒ…

- ๋ชจ๋“  ํŽ˜์ด์ง€์— ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฒ„ํŠผ, ํ—ค๋” ์ปดํฌ๋„ŒํŠธ ์„ธํŒ…
- UI ์š”์†Œ๊ฐ€ `์–ด๋–ค ๊ธฐ์ค€`์œผ๋กœ `์–ผ๋งˆ๋งŒํผ ๋ณ€ํ™”`ํ•˜๊ฒŒ ๋˜๋Š”๊ฐ€๋ฅผ `ํŒจํ„ดํ™”`ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”


### - ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

![๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ](README_img/button_components.png)

- ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ
- props๋กœ text, type, onClick์„ ๋ฐ›์Œ
- btnType ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด props๋กœ ๋ฐ›์€ type ๊ฐ’์ด 'positive', 'negative' ๋‘˜ ์ค‘ ํ•˜๋‚˜์ด๋ฉด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์ด์ƒํ•œ ๊ฐ’์ด๋ฉด, default๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ
- ๋ฒ„ํŠผ ์ƒ‰์ƒ์€ type์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋ณ€ํ™”์‹œํ‚ค๊ธฐ ์œ„ํ•ด `${}` ์‚ฌ์šฉ
- ์•„๋ฌด๋Ÿฐ type์ด ์ „๋‹ฌ ์•ˆ ๋  ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ `defaultProps`๋กœ type์— default๋ฅผ ๋„ฃ๊ธฐ

```tsx
// src/components/MyButton.js

const MyButton = ({text, type, onClick}) => {
const btnType = ["positive", "negative"].includes(type) ? type : "default";

return (

{text}

)
}

MyButton.defaultProps = {
type: "default",
}

export default MyButton;
```

- App์— ๋ฒ„ํŠผ ์ƒ์„ฑํ•˜๊ณ  Props ์ „๋‹ฌํ•˜๊ธฐ
- onClick์œผ๋กœ alert ๋„์šฐ๊ธฐ

```tsx
// src/pages/App.js

...
alert("๋ฒ„ํŠผ ํด๋ฆญ")}
type={"positive"}/>
alert("๋ฒ„ํŠผ ํด๋ฆญ")}
type={"negative"}/>
alert("๋ฒ„ํŠผ ํด๋ฆญ")}
type={"default"}/>
...
```

- css ์Šคํƒ€์ผ๋ง

```css
/*src/pages/App.css*/

/* MyButton */

.MyButton {
cursor: pointer;
border: none;
border-radius: 5px;
padding-top: 10px;
padding-bottom: 10px;
padding-right: 20px;
padding-left: 20px;
font-size: 18px;
white-space: nowrap;
font-family: 'Nanum Gothic';
}

.MyButton_default {
background-color: #ececec;
color: black;
}

.MyButton_positive {
background-color: #64c964;
color: white;
}

.MyButton_negative {
background-color: #fd565f;
color: white;
}
```


### - ํ—ค๋” ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

![ํ—ค๋” ์ปดํฌ๋„ŒํŠธ](README_img/header_component.png)

- ํ—ค๋” ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ

```tsx
// src/components/MyHeader.js

const MyHeader = ({headText, leftChild, rightChild}) => {
return

{leftChild}

{headText}

{rightChild}


}

export default MyHeader;
```

- App.js์— ์ ์šฉ

```tsx
// src/pages/App.js

...
alert("์™ผ์ชฝ ํด๋ฆญ")}/>}
rightChild={ alert("์˜ค๋ฅธ์ชฝ ํด๋ฆญ")}/>}
/>
...
```

- css ์Šคํƒ€์ผ๋ง

```css
/*src/pages/App.css*/

/* HEADER */

header {
padding-top: 20px;
padding-bottom: 20px;
display: flex;
align-items: center;
border-bottom: 1px solid #e2e2e2;
}

header > div {
display: flex;
}

header .head_text {
width: 50%;
font-size: 25px;
justify-content: center;
}

header .head_btn_left {
width: 25%;
justify-content: start;
}

header .head_btn_right {
width: 25%;
justify-content: end;
}

header button {
font-family: 'Nanum Gothic', sans-serif;
}
```




## 5. ํ”„๋กœ์ ํŠธ ๊ธฐ์ดˆ๊ณต์‚ฌ 2

### 5-1. ์„ธํŒ… ํ•ญ๋ชฉ

- ์ƒํƒœ ๊ด€๋ฆฌ ์„ธํŒ…ํ•˜๊ธฐ : ํ”„๋กœ์ ํŠธ ์ „๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋  ์ผ๊ธฐ ๋ฐ์ดํ„ฐ State ๊ด€๋ฆฌ ๋กœ์ง ์ž‘์„ฑํ•˜๊ธฐ
- ํ”„๋กœ์ ํŠธ State Context ์„ธํŒ…ํ•˜๊ธฐ : ์ผ๊ธฐ ๋ฐ์ดํ„ฐ State๋ฅผ ๊ณต๊ธ‰ํ•  Context๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Provider๋กœ ๊ณต๊ธ‰ํ•˜๊ธฐ
- ํ”„๋กœ์ ํŠธ Dispatch Context ์„ธํŒ…ํ•˜๊ธฐ : ์ผ๊ธฐ ๋ฐ์ดํ„ฐ State์˜ Dispatch ํ•จ์ˆ˜๋“ค์„ ๊ณต๊ธ‰ํ•  Context๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Provider๋กœ ๊ณต๊ธ‰ํ•˜๊ธฐ


### 5-2. ์ƒํƒœ ๊ด€๋ฆฌ ์„ธํŒ…ํ•˜๊ธฐ

![๊ตฌ์กฐ](README_img/project_structure.png)

- App ์ปดํฌ๋„ŒํŠธ๊ฐ€ routes๋กœ 4๊ฐœ์˜ ํŽ˜์ด์ง€๋ฅผ ์ž์‹์š”์†Œ๋กœ ๊ฐ€์ง
- URL์— ๋”ฐ๋ผ์„œ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์คŒ
- `/` : Home
- `/new` : New
- `/edit` : Edit
- `/diary` : Diary


### 5-3. ์ƒํƒœ ๊ด€๋ฆฌ ์„ธํŒ…ํ•˜๊ธฐ

- `์ผ๊ธฐ ๋ฐ์ดํ„ฐ`๋Š” `์ƒ์„ฑ`, `์‚ญ์ œ`, `์ˆ˜์ •`์˜ ์ƒํƒœ ๋ณ€ํ™” ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•จ
- data(์ผ๊ธฐ)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์—ฌ๋Ÿฌ ์ƒํƒœ ๋ณ€ํ™” ํ•จ์ˆ˜๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด `useReducer` ์‚ฌ์šฉ


```javascript
// src/pages/App.js

import React, {useReducer, useRef} from "react";

...
function App() {
const [data, dispatch] = useReducer(reducer, []);
...
}
```


- reducer ํ•จ์ˆ˜๋กœ ๋ถ„๊ธฐ์ฒ˜๋ฆฌํ•˜๊ธฐ
- dispatch๋ฅผ ํ˜ธ์ถœํ•œ action ๊ฐ์ฒด์˜ ํƒ€์ž…์ด `INIT`์ผ ๊ฒฝ์šฐ, ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
- ์ƒ์„ฑ์ธ `CREATE`์ผ ๊ฒฝ์šฐ, newItem์„ newState ๋ฐฐ์—ด์— ๋‹ด์•„ ์ƒ์„ฑ
- ์‚ญ์ œ์ธ `REMOVE`์ผ ๊ฒฝ์šฐ, ์ตœ์‹  ์ƒํƒœ state์—์„œ action ๊ฐ์ฒด์˜ targetId์™€ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋งŒ ๋ชจ์€ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด newState๋ฅผ ์ƒ์„ฑ
- ์ˆ˜์ •์ธ `EDIT`์˜ ๊ฒฝ์šฐ, ์ตœ์‹  state์—์„œ action ๊ฐ์ฒด์˜ id์™€ ๊ฐ™์„ ๊ฒฝ์šฐ, action์˜ data๋ฅผ ๋ฎ์–ด์”Œ์šฐ๊ณ , ๋‹ค๋ฅด๋ฉด ๊ทธ๋Œ€๋กœ์ธ ๋ฐฐ์—ด newState๋ฅผ ์ƒ์„ฑ
- ์ตœ์ข…์ ์œผ๋กœ newState ๋ฐ˜ํ™˜

```javascript
// src/pages/App.js

...
const reducer = (state, action) => {
let newState = [];
switch (action.type) {
case 'INIT': {
return action.data;
}
case 'CREATE': {
const newItem = {
...action.data
};
newState = [newItem, ...state];
break;
}
case 'REMOVE': {
newState = state.filter((it) => it.id !== action.targetId);
break;
}
case 'EDIT': {
newState = state.map((it) =>
it.id === action.data.id ? {...action.data} : it
);
break;
}
default:
return state;
}
return newState;
};
...
```


```javascript
// src/pages/App.js

...
function App() {

const [data, dispatch] = useReducer(reducer, []);

const dataId = useRef(0);

// CREATE
const onCreate = (date, content, emotion) => {
dispatch({
type: "CREATE",
data: {
id: dataId.current,
date: new Date(date).getTime(),
content,
emotion,
}
});
dataId.current += 1;
};

// REMOVE
const onRemove = (targetId) => {
dispatch({
type: "REMOVE",
targetId,
});
};

// EDIT
const onEdit = (targetId, date, content, emotion) => {
dispatch({
type: "EDIT",
data: {
id: targetId,
date: new Date(date).getTime(),
content,
emotion,
}
});
};
...
```

- dispatch ํ˜ธ์ถœ ํ•จ์ˆ˜ ์ƒ์„ฑํ•˜๊ธฐ


### 5-4. ํ”„๋กœ์ ํŠธ State Context ์„ธํŒ…ํ•˜๊ธฐ

- context ์ƒ์„ฑ ๋ฐ Provider ์ปดํฌ๋„ŒํŠธ๋กœ ๊ณต๊ธ‰ํ•˜๊ธฐ

```javascript
// src/pages/App.js

...
export const DiaryStateContext = React.createContext();
...




App.js



}/>
}/>
}/>
}/>



```


### 5-5. ํ”„๋กœ์ ํŠธ Dispatch Context ์„ธํŒ…ํ•˜๊ธฐ

- context ์ƒ์„ฑ ๋ฐ Provider ์ปดํฌ๋„ŒํŠธ๋กœ ๊ณต๊ธ‰ํ•˜๊ธฐ

```javascript
// src/pages/App.js

...
export const DiaryDispatchContext = React.createContext();
...






App.js



}/>
}/>
}/>
}/>





```




## 6. ํŽ˜์ด์ง€ ๊ตฌํ˜„

### 6-1. ํ™ˆ(/)

- ํ—ค๋”(์›” ๋ณ€๊ฒฝ), ํ•„ํ„ฐ๋ง(์ผ๊ธฐ ์ž‘์„ฑ ํฌํ•จ), ์ผ๊ธฐ(๋‚ ์งœ, ๋‚ด์šฉ, ๊ฐ์ •, ์ˆ˜์ •ํ•˜๊ธฐ) ๋ฆฌ์ŠคํŠธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰จ


### - ํ—ค๋”

- 3๋‹จ๊ณ„๋กœ ๊ฐœ๋ฐœ ์ง„ํ–‰
1. ๊ฐ€์šด๋ฐ ์›” ํ‘œ์‹œ
2. ์™ผ์ชฝ ๋ฒ„ํŠผ
3. ์˜ค๋ฅธ์ชฝ ๋ฒ„ํŠผ




## 7. ๋ฒ„๊ทธ




## 8. LocalStorage

- ์ผ๊ธฐ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ƒˆ๋กœ๊ณ ์นจ ์‹œ, ๋”๋ฏธ ๋ฐ์ดํ„ฐ ์™ธ์— ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ƒ์„ฑ๋œ ์ผ๊ธฐ ๋ฐ์ดํ„ฐ๋Š” ์‚ฌ๋ผ์ง
- ์ƒ์„ฑ๋œ ์ผ๊ธฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ์ƒ์—์„œ `ํœ˜๋ฐœ์„ฑ`์ด๊ธฐ ๋•Œ๋ฌธ
- ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ์ผ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด ์ค„ ํ•„์š”๊ฐ€ ์žˆ์Œ
- Web์—์„œ ์ œ๊ณตํ•˜๋Š” `Web Storage API`๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Œ

[MDN - Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)


### 8-1. Web Storage API

- `ํ‚ค/๊ฐ’`์˜ ์Œ์„ ์ฟ ํ‚ค๋ณด๋‹ค ์ง๊ด€์ ์œผ๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ• ์ œ๊ณต
- `sessionStorage`์™€ `localStorage` ๋ฐฉ์‹์„ ์ œ๊ณตํ•จ


### - sessionStorage

- ํŽ˜์ด์ง€ ์„ธ์…˜์ด ์œ ์ง€๋˜๋Š” ๋™์•ˆ(`๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—ด๋ฆฐ ๋™์•ˆ`) ๋…๋ฆฝ์ ์ธ ๊ณต๊ฐ„์„ ์ œ๊ณตํ•จ
- ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ์ „์†กํ•˜์ง€ ์•Š์Œ
- ์ €์žฅ๊ณต๊ฐ„์ด ์ฟ ํ‚ค๋ณด๋‹ค ํผ(์ตœ๋Œ€ 5MB)


### - localStorage

- sessionStorage์™€ ๋น„์Šทํ•˜๋‚˜ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋‹ซ์•˜๋‹ค๊ฐ€ `๋‹ค์‹œ ์ ‘์†ํ•ด๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ์Œ` (๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ง€๋จ)
- ์œ ํšจ๊ธฐ๊ฐ„ ์—†์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•จ
- ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ ๋˜๋Š” ๋กœ์ปฌ ์ €์žฅ ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์›Œ์•ผ ์‚ฌ๋ผ์ง

1. ๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๊ธฐ - setItem(key, value)

```js
// setItem ์˜ˆ์‹œ

localStorage.setItem("item1", 10);
localStorage.setItem("item2", "20");
localStorage.setItem("item3", JSON.stringify({value: 30}));

// ๊ฐ’์œผ๋กœ ์ˆซ์ž, ๋ฌธ์ž์—ด ์ €์žฅ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ,
// ๊ฐ์ฒด๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ด์„ํ•˜์ง€ ๋ชปํ•˜๊ธฐ์— JSON.stringify()๋ฅผ ํ†ตํ•ด ์ง๋ ฌํ™”(๊ฐ์ฒด๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜)๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ•จ
// JavaScript์—์„œ setItem์„ ์ง€์›Œ๋„ localStorage์— ๋“ค์–ด๊ฐ„ ๋ฐ์ดํ„ฐ๋Š” ์ง€์šฐ๊ธฐ ์ „๊นŒ์ง€ ์œ ์ง€๋จ
```

2. ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ - getItem(key)

```js
// getItem ์˜ˆ์‹œ

const item1 = localStorage.getItem("item1");
const item2 = localStorage.getItem("item2");
const item3 = localStorage.getItem("item3");

// ํ•ด๋‹น ๋ฐ์ดํ„ฐ ํ™•์ธ
console.log({item1, item2, item3});

// ๊ฐ’์„ ํ™•์ธํ•ด๋ณด๋ฉด ์ˆซ์ž ํƒ€์ž…์œผ๋กœ ์ €์žฅํ•œ ๊ฐ’์ด ๋ฌธ์ž์—ด๋กœ ์ถœ๋ ฅ๋จ
// ์ฆ‰, localStorage์˜ ๊ฐ’์€ ๋ชจ๋‘ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜๋˜์–ด ์ €์žฅ๋จ
// ๋”ฐ๋ผ์„œ ์ˆซ์ž์˜ ๊ฒฝ์šฐ, parseInt() ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์–ด์•ผ ํ•จ
// ๊ฐ์ฒด๋Š” JSON.parse()๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋กœ ๋ณต์›ํ•ด์ฃผ์–ด์•ผ ํ•จ

const item1 = parseInt(localStorage.getItem("item1"));
const item2 = localStorage.getItem("item2");
const item3 = JSON.parse(localStorage.getItem("item3"));
```



## 9. ์ตœ์ ํ™”

- ๋‚ญ๋น„๋˜๋Š” ์—ฐ์‚ฐ์„ ์ฐพ์•„ ์ตœ์ ํ™” ํ•˜๊ธฐ
- ๋‚ญ๋น„๋˜๋Š” ์—ฐ์‚ฐ์ฐพ๊ธฐ
- ์ •์  ๋ถ„์„ ๋ฐฉ๋ฒ• : ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ธฐ
- ๋™์  ๋ถ„์„ ๋ฐฉ๋ฒ• : ๋„๊ตฌ์˜ ๋„์›€์„ ๋ฐ›์•„ ๋‚ญ๋น„๋˜๋Š” ๋ถ€๋ถ„ ์ฐพ๊ธฐ

### 9-1. ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”

### - Home์˜ ํ•„ํ„ฐ ๋ฐ ์ผ๊ธฐ ์ž‘์„ฑ

- ์›”์„ ์ด๋™ํ•  ๊ฒฝ์šฐ, ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋” ๋ฐœ์ƒ
- Home์˜ ์›”์ด ๋ฐ”๋€Œ๋ฉด, ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ DiaryList๊ฐ€ ์˜ํ–ฅ์„ ๋ฐ›๊ณ  DiaryList์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ ControlMenu๊ฐ€ ์˜ํ–ฅ์„ ๋ฐ›์Œ
- React.memo ์‚ฌ์šฉ

```js
// src/components/DiaryList.js

// ControlMenu ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐ’์ด ๋ฐ”๋€” ๊ฒฝ์šฐ, ๋ฆฌ๋ Œ๋”๊ฐ€ ๋˜๋„๋ก React.memo()๋กœ ๊ฐ์‹ธ๊ธฐ

import React, {useState} from "react";

...
const ControlMenu = React.memo(({value, onChange, optionList}) => {
return (
onChange(e.target.value)}
>
{optionList.map((it, idx) => (

{it.name}

))}

)
});
...
```


### - Home์˜ diaryList

- ์ตœ์‹  ์ˆœ, ์˜ค๋ž˜๋œ ์ˆœ ๋“ฑ์˜ ํ•„ํ„ฐ ๋ณ€ํ™”์— ๋”ฐ๋ผ์„œ diaryItem๋“ค์˜ ์œ„์น˜๋งŒ ๋ณ€ํ•˜๋ฉด ์ข‹์€๋ฐ ๋ชจ๋“  ํ•ญ๋ชฉ์ด ๋ฆฌ๋ Œ๋”๋ง ๋จ
- ๋ชจ๋“  ๊ฐ์ •, ์ข‹์€ ๊ฐ์ •, ๋‚˜์œ ๊ฐ์ •์œผ๋กœ ํ•„ํ„ฐ ํ•  ๋•Œ, ํ•ญ๋ชฉ์ด ๋ฐ”๋€Œ์ง€ ์•Š์•„๋„ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒ ํ•จ
- ์ผ๊ธฐ ํ•ญ๋ชฉ์ด ๋งŽ์•„์งˆ ์ˆ˜๋ก ์‚ฌ์ดํŠธ์˜ ๊ฒฝํ—˜์„ฑ์ด ๋‚˜๋น ์ง
- DiaryItem์— React.memo() ์‚ฌ์šฉ

```js
// src/components/DiaryItem.js

import React from "react";

...
export default React.memo(DiaryItem);
```


### Edit์˜ DiaryEditor

- ์˜ค๋Š˜์˜ ์ผ๊ธฐ์— ์ผ๊ธฐ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜๋ฉด onChange๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋™์•ˆ, ์˜ค๋Š˜์˜ ๊ฐ์ •์˜ ๊ฐ์ • ํ•ญ๋ชฉ๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚จ
- DiaryEditor ์ปดํฌ๋„ŒํŠธ์—์„œ ์ผ๊ธฐ ๋‚ด์šฉ์ด ๋ณ€ํ•˜๋ฉด ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ EmotionItem ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒ
- ๋”ฐ๋ผ์„œ EmotionItem ์ปดํฌ๋„ŒํŠธ์— React.memo() ์‚ฌ์šฉ

```js
// src/components/EmotionItem.js

import React from "react";

const EmotionItem = ({
emotion_id,
emotion_img,
emotion_descript,
onClick,
isSelected
}) => {
...
};

...
export default React.memo(EmotionItem);
```

- ํ•˜์ง€๋งŒ, ๊ณ„์† ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜๋Š”๋ฐ ์ด ์ด์œ ๋Š” EmotionItem์ด Props๋กœ onClick์„ ์ „๋‹ฌ๋ฐ›๋Š”๋ฐ ํ•ด๋‹น onClick์œผ๋กœ ์ „๋‹ฌ๋ฐ›๋Š” ํ•จ์ˆ˜๊ฐ€ useState์˜ ์ƒํƒœ ๋ณ€ํ™” ํ•จ์ˆ˜, useCallback์œผ๋กœ ๋ฌถ์€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ์‹œ, ๋‹ค์‹œ ์ƒ์„ฑ๋˜๋ฏ€๋กœ React.memo()๋ฅผ ์ ์šฉํ•œ ๊ฐ•ํ™”๋œ ์ปดํฌ๋„ŒํŠธ์ผ์ง€๋ผ๋„ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œํ‚ด
- ๋”ฐ๋ผ์„œ onClick์— ์ „๋‹ฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ useCallback()์œผ๋กœ ๋ฌถ์–ด์ฃผ๊ธฐ

```js
// src/components/DiaryEditor.js

import {useCallback, useContext, useEffect, useRef, useState} from "react";

...
const handleClickEmote = useCallback((emotion) => {
setEmotion(emotion);
}, []);
...

์˜ค๋Š˜์˜ ๊ฐ์ •



{emotionList.map((it) => (

))}

...
```




## 10. ๋ฐฐํฌ ์ค€๋น„ & ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ

### 10-1. ๊ธฐ๋ณธ title ํƒœ๊ทธ ์ˆ˜์ •

- ๋ธŒ๋ผ์šฐ์ €์˜ ํƒญ์„ ๋ณด๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ React App์œผ๋กœ ํ‘œ์‹œ๋จ
- ํ•ด๋‹น ๋‚ด์šฉ์€ head ํƒœ๊ทธ ์•ˆ์˜ title ํƒœ๊ทธ์˜ ๋ฌธ์ž๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์œผ๋กœ ํŽ˜์ด์ง€์— ๋งž๊ฒŒ ์ถœ๋ ฅ๋  ํ•„์š”๊ฐ€ ์žˆ์Œ

```html

...
๊ฐ์ • ์ผ๊ธฐ์žฅ

```


### 10-2. description ์ˆ˜์ •

- ์•ฑ, ์„œ๋น„์Šค์˜ ์„ค๋ช…์„ ๋‚˜ํƒ€๋‚ด๋Š” description meta ํƒœ๊ทธ ๋‚ด์šฉ ์ˆ˜์ •

```html

...

...

```


### 10-3. lang ์ˆ˜์ •

- ์–ธ์–ด๋„ ํ•œ๊ตญ์–ด๋กœ ์„ค์ •

```html

...

```


### 10-4. ํŽ˜์ด์ง€๋งˆ๋‹ค title ๋ณ€ํ™˜

- useEffect๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€ ๋งˆ์šดํŠธ ์‹œ, ํŽ˜์ด์ง€ ์ด๋ฆ„ ๋ณ€๊ฒฝ ์ง„ํ–‰
- DOM์˜ getElementsByTagName ๋ฌธ๋ฒ•์œผ๋กœ title ํƒœ๊ทธ ๋ชจ๋‘ ์„ ํƒ
- ๋ฐฐ์—ด์ด ๋ฐ˜ํ™˜๋˜๊ธฐ์— ์ฒซ๋ฒˆ์งธ ์š”์†Œ ์„ ํƒ
- ํ•ด๋‹น ์š”์†Œ์˜ innerHTML์„ ๋ณ€๊ฒฝํ•ด์ฃผ๊ธฐ
- ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋“ค๋„ ๋™์ผํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ˆ˜์ •

```js
// src/pages/Diary.js

...
useEffect(() => {
const titleElement = document.getElementsByTagName("title")[0];
titleElement.innerHTML = `๊ฐ์ • ์ผ๊ธฐ์žฅ - ${id}๋ฒˆ ์ผ๊ธฐ`;
}, []);
...
```


### 10-5. favicon.ico ๋ณ€๊ฒฝ

- ํƒญ์—์„œ ํ‘œ์‹œ๋˜๋Š” ์•„์ด์ฝ˜ ๋ณ€๊ฒฝ
- public ํด๋” ์•ˆ์˜ favicon.ico ํŒŒ์ผ์„ ๋™์ผํ•œ ์ด๋ฆ„์˜ ๋กœ๊ณ ๋กœ ๊ต์ฒดํ•˜๊ธฐ


### 10-6. ๋นŒ๋“œ

- ์†Œ์Šค์ฝ”๋“œ์˜ ์ค„๋ฐ”๊ฟˆ, ๋„์›Œ์“ฐ๊ธฐ ๋“ฑ์€ ๋ชจ๋‘ ์šฉ๋Ÿ‰์„ ์ฐจ์ง€ํ•˜๊ธฐ์— ์ด๋ฅผ ์••์ถ•ํ•˜์—ฌ ๋ฐฐํฌํ•˜๊ธฐ ์ตœ์ ์˜ ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ธฐ
- ์ด๋ฅผ ๋นŒ๋“œ๋ผ๊ณ  ํ•จ
- package.json ํŒŒ์ผ์˜ scripts์˜ build๋ฅผ ํ†ตํ•ด ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

```bash
$ npm run build
```


### - ๋กœ์ปฌ์— ๋ฐฐํฌํ•ด๋ณด๊ธฐ

- serve ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก serve ํŒจํ‚ค์ง€ ์„ค์น˜
- serve -s build ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๋กœ์ปฌ์— ๋ฐฐํฌ
- ๋กœ์ปฌ ๋ฐฐํฌ๋ฅผ ํ•˜๋Š” ์ด์œ ๋Š” ์‹ค์ œ ์„œ๋น„์Šค ๋ฐฐํฌ ์‹œ, ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ํ…Œ์ŠคํŠธ ํ•ด๋ณด๊ธฐ ์œ„ํ•จ

```bash
$ npm install -g serve

$ serve -s build
```


### - ์ผ๊ธฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ

- ์ผ๊ธฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ์ƒํƒœ์—์„œ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ, ์—๋Ÿฌ ๋ฐœ์ƒ
- App ์ปดํฌ๋„ŒํŠธ์—์„œ diaryList๊ฐ€ ๋นˆ๋ฐฐ์—ด์ผ ๊ฒฝ์šฐ, truthy๊ฐ€ ๋˜๋Š”๋ฐ ์ด๋•Œ, ์ฒซ๋ฒˆ์งธ ์š”์†Œ์— ์ ‘๊ทผํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ์Œ
- ๋”ฐ๋ผ์„œ ์กฐ๊ฑด๋ฌธ์„ ํ†ตํ•ด diaryList์˜ ๊ธธ์ด๊ฐ€ 1 ์ด์ƒ์ผ ๋•Œ, ํ•ด๋‹น ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์ˆ˜์ •

```js
// src/App.js

...
useEffect(() => {
const localData = localStorage.getItem("diary");
if (localData) {
const diaryList = JSON.parse(localData).sort((a, b) => parseInt(b.id) - parseInt(a.id));

// ์กฐ๊ฑด๋ฌธ ์ถ”๊ฐ€
if (diaryList.length >= 1) {
dataId.current = parseInt(diaryList[0].id) + 1;
dispatch({type: "INIT", data: diaryList});
}
}
}, []);
...
```


### - title ์—๋Ÿฌ

- ์ผ๊ธฐ ์ž‘์„ฑ ํ›„, Home์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ์‹œ, ํƒญ์˜ title์ด ์ผ๊ธฐ ์ž‘์„ฑ์œผ๋กœ ๋จธ๋ฌผ๋Ÿฌ ์žˆ์Œ
- ์ผ๊ธฐ ์‚ญ์ œ ํ›„, Home์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ์‹œ, ํƒญ์˜ title์ด ์ผ๊ธฐ ์ˆ˜์ •์œผ๋กœ ๋จธ๋ฌผ๋Ÿฌ ์žˆ์Œ
- ์ด๋Š” Home ์ปดํฌ๋„ŒํŠธ๋Š” useEffect์™€ DOM ๋ฌธ๋ฒ•์„ ์ด์šฉํ•˜์—ฌ title์„ ๋ฐ”๊ฟ”์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•จ

```js
// src/pages/Home.js

...
useEffect(() => {
const titleElement = document.getElementsByTagName("title")[0];
titleElement.innerHTML = `๊ฐ์ • ์ผ๊ธฐ์žฅ`;
}, []);
...
```




## 11. Firebase ๋ฐฐํฌ

- ๋ฐฐํฌ๋Š” ์„œ๋ฒ„ ์œ ์ง€ ๋น„์šฉ, ํด๋ผ์šฐ๋“œ ๋Œ€์—ฌ ๋น„์šฉ, ๋ฐฉํ™”๋ฒฝ ๋“ฑ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ณ ๋ คํ•ด์•ผ ํ•  ์š”์†Œ๊ฐ€ ๋งŽ์Œ
- Firebase๋Š” ๊ตฌ๊ธ€์—์„œ ์ œ๊ณตํ•˜๋ฉฐ ์‰ฝ๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ฐฐํฌ ์†”๋ฃจ์…˜์ž„
- [Firebase ๊ณต์‹ ์‚ฌ์ดํŠธ](https://firebase.google.com/?hl=ko)


### 11-1. Firebase ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

### - ํ™ˆํŽ˜์ด์ง€

![ํ™ˆํŽ˜์ด์ง€](README_img/firebase_home.png)

- ์‹œ์ž‘ํ•˜๊ธฐ ํด๋ฆญ


### - ์ฝ˜์†” ํŽ˜์ด์ง€

![์ฝ˜์†” ํŽ˜์ด์ง€](README_img/firebase_console.png)

- ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ ํด๋ฆญ


### - ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ 3๋‹จ๊ณ„

![ํ”„๋กœ์ ํŠธ ์ด๋ฆ„](README_img/firebase_name.png)

- ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ์ง€์ •ํ•˜๊ธฐ
- ํ•ด๋‹น ์ด๋ฆ„์€ ์—ฌ๋Ÿฌ ์‚ฌ์ดํŠธ๋ฅผ ํฌ๊ด„ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋„๋ฉ”์ธ ์ด๋ฆ„์ด๋ž‘ ๋ณ„๊ฐœ์ž„


![๊ตฌ๊ธ€ ๋ถ„์„](README_img/firebase_analytics.png)

- ํŠธ๋ž˜ํ”ฝ ๋ถ„์„ ๋“ฑ ๊ตฌ๊ธ€์— ์ •๋ณด ์ œ๊ณต ์œ ๋ฌด ๊ฒฐ์ •


![ํ”„๋กœ์ ํŠธ ์ค€๋น„ ์™„๋ฃŒ](README_img/firebase_prepare_done.png)

- ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ๋จ


### - ํ˜ธ์ŠคํŒ…

![์ฝ˜์†” ํŽ˜์ด์ง€](README_img/firebase_console_re.png)

- ํ”„๋กœ์ ํŠธ ์ฝ˜์†” ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜ ๋จ


![ํ˜ธ์ŠคํŒ… ๋ฉ”๋‰ด](README_img/firebase_hosting.png)

- ๋นŒ๋“œ ๋ฉ”๋‰ด์˜ ํ˜ธ์ŠคํŒ… ํด๋ฆญ


![ํ˜ธ์ŠคํŒ… ํŽ˜์ด์ง€](README_img/firebase_hosting_page.png)

- ํ˜ธ์ŠคํŒ… ํŽ˜์ด์ง€๋กœ ์ด๋™
- ํ˜ธ์ŠคํŒ… ์‹œ์ž‘ํ•˜๊ธฐ ํด๋ฆญ


![firebase cli](README_img/firebase_cli.png)

- Firebase CLI ์„ค์น˜ํ•˜๊ธฐ


![firebase login init](README_img/firebase_login_init.png)

- ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ firebase login ๋ช…๋ น์–ด ์ˆ˜ํ–‰


![ํ—ˆ์šฉ](README_img/firebase_allow.png)

- ์˜ค๋ฅ˜ ์ˆ˜์ง‘ ์—ฌ๋ถ€ ๊ฒฐ์ •


![๋กœ๊ทธ์ธ ์™„๋ฃŒ](README_img/firebase_login_done.png)

- ๋กœ๊ทธ์ธ ์™„๋ฃŒ




### - ๋ฐฐํฌ ์„ธํŒ…

![ํ˜ธ์ŠคํŒ…์œผ๋กœ ์„ธํŒ…](README_img/firebase_setting1.png)

- ํ˜ธ์ŠคํŒ…์œผ๋กœ ๋ฐฐํฌ ์ง„ํ–‰


![์˜ต์…˜](README_img/firebase_setting2.png)

- ํ˜„์žฌ ๋งŒ๋“ค์–ด์ง„ ํ”„๋กœ์ ํŠธ, ์ƒˆ ํ”„๋กœ์ ํŠธ ๋“ฑ ์˜ต์…˜ ์„ ํƒ


![ํ”„๋กœ์ ํŠธ ์„ ํƒ](README_img/firebase_setting3.png)

- ํ•ด๋‹น ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋ฐฐํฌํ•  Firebase ํ”„๋กœ์ ํŠธ ์„ ํƒ


![์—ฌ๋Ÿฌ ์„ธํŒ… ์„ ํƒ](README_img/firebase_setting4.png)

- ํ˜ธ์ŠคํŒ…์— ๊ด€๋ จ๋œ ์—ฌ๋Ÿฌ ์„ธํŒ… ์„ค์ •ํ•˜๊ธฐ


![firebase ํŒŒ์ผ ์ƒ์„ฑ](README_img/firebase_new_files.png)

- ์„ธํŒ…์„ ์™„๋ฃŒ ์‹œ, `.firebaserc`์™€ `firebase.json` ํŒŒ์ผ์ด ์ƒ์„ฑ๋จ


![๋ฐฐํฌ](README_img/firebase_deploy.png)

- ๋ฐ”๋กœ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ฝ˜์†”๋กœ ์ด๋™


![๋„๋ฉ”์ธ ์ถ”๊ฐ€](README_img/firebase_add_new_site.png)

- ์ฝ˜์†” ํŽ˜์ด์ง€ ์•„๋ž˜์˜ ๋‹ค๋ฅธ ์‚ฌ์ดํŠธ ์ถ”๊ฐ€ ํด๋ฆญ


![๋„๋ฉ”์ธ ์ด๋ฆ„ ์ž‘์„ฑ](README_img/firebase_write_domain.png)

- ์‚ฌ์šฉํ•  ๋„๋ฉ”์ธ ์ด๋ฆ„ ์ž‘์„ฑ


![๋„๋ฉ”์ธ ์ด๋ฆ„ ํŒŒ์ผ์— ์ž‘์„ฑ](README_img/firebase_add_domain.png)

- firebase.json ํŒŒ์ผ์—์„œ hosting ์˜ต์…˜์— `"site": "์ž‘์„ฑํ•œ ๋„๋ฉ”์ธ"` ์ถ”๊ฐ€ํ•˜๊ธฐ


```bash
$ npm run build
```

- ๋นŒ๋“œ ๋‹ค์‹œ ์ง„ํ–‰


```bash
$ firebase deploy
```

- ๋ฐฐํฌ ์ง„ํ–‰


![์™„๋ฃŒ](README_img/firebase_done.png)

- ๋ฐฐํฌ ์™„๋ฃŒ๋จ




## 12. Open Graph

- Open Graph์˜ ์ •๋ณด๋Š” ํ•ด๋‹น ๋งํฌ๋ฅผ ๊ณต์œ ํ•  ๋•Œ, `์ธ๋„ค์ผ, ์‚ฌ์ดํŠธ ์ด๋ฆ„, ๊ฐ„๋‹จํ•œ ์„ค๋ช…`์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•จ
- `property="og:<์ œ๊ณตํ•  ์ •๋ณด>"` ์™€ `content="์ด๋ฏธ์ง€ ์ฃผ์†Œ ๋ฐ ๋‚ด์šฉ"`์˜ ์†์„ฑ์„ ๊ฐ€์ง„ meta ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ

```html

...



...

```