๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Frontend Dev/Output

[Mini Project] ํ”„๋ก ํŠธ์—”๋“œ ๊ณผ์ œํ…Œ์ŠคํŠธ๋กœ ๋งŒ๋‚œ Note-App ๊ตฌํ˜„ ๊ณผ์ • ์ •๋ฆฌ (1)

๋ฐ˜์‘ํ˜•

 

[Mini Project] Note-App

๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป Github Repository https://github.com/sw2377/note-app

 

 

์ž‘์—… ๊ธฐ๊ฐ„ : 2024. 01. 09 ~ 2024. 01. 16

ํ”„๋ก ํŠธ์—”๋“œ ์ฑ„์šฉ ๊ณผ์ œ๋กœ ์ˆ˜ํ–‰ํ•œ Note-App ๊ตฌํ˜„ ๊ณผ์ • (๊ตฌํ˜„ ์ค‘ ์—๋Ÿฌ์‚ฌํ•ญ & ๊ณ ๋ฏผํ•œ ๋ถ€๋ถ„)์„ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

 

๐Ÿ”— ์‹œ๋ฆฌ์ฆˆ ์ด์–ด๋ณด๊ธฐ

ํ˜„์žฌ๊ธ€ : Note-App ๊ตฌํ˜„ ๊ณผ์ • ์ •๋ฆฌ (1)

Note-App ๊ตฌํ˜„ ๊ณผ์ • ์ •๋ฆฌ (2)

Note-App ๊ตฌํ˜„ ๊ณผ์ • ์ •๋ฆฌ (3)

 

๐Ÿ“– ๋ชฉ์ฐจ

1. ๊ธฐ์ˆ  ์Šคํƒ์˜ ์„ ํƒ (TypeScript, Styled-components๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ )
2. ๋ ˆ์ด์•„์›ƒ ๊ณ ๋ฏผํ•˜๊ธฐ
3. Vite๋กœ React + TypeScript ์กฐํ•ฉ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑ ํ›„,main.tsx ํŒŒ์ผ์˜ document.getElementById('root') ๋ถ€๋ถ„์˜ ์—๋Ÿฌ
4. ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ ˆ์ด์•„์›ƒ์„ ์™„์ „ํžˆ ๋จผ์ € ์ž‘์—…
5. zustand๋กœ notebooks ์ƒํƒœ๊ด€๋ฆฌ ๋„์ž…
6. LocalStorage์— ์–ด๋– ํ•œ ํ˜•์‹์œผ๋กœ notebooks๋ฅผ ์ €์žฅํ• ๊นŒ?

๐Ÿค FE ๊ธฐ์ˆ  ์Šคํƒ
- React + Typscript (Vite)
- Styled Components
- Zustand
- Lexical Editor

 

1. ๊ธฐ์ˆ  ์Šคํƒ์˜ ์„ ํƒ

์ •ํ•ด์ง„ ๊ธฐ์ˆ  ์Šคํƒ์€ React ์˜€์œผ๋ฉฐ, React๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›นํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜๋ผ๋Š” ๊ฒƒ์ด ์š”๊ตฌ์‚ฌํ•ญ์ด์—ˆ๋‹ค. TypeScript์˜ ๋„์ž…๊ณผ ์Šคํƒ€์ผ๋ง์„ ์–ด๋–ป๊ฒŒ ํ• ์ง€ ์ •ํ•ด์•ผ ํ–ˆ๋Š”๋ฐ, ์ด ๋ถ€๋ถ„์€ ๋น ๋ฅด๊ฒŒ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

 

๐Ÿ“Œ TypeScript๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š”?

 ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ •์ ํƒ€์ž… ๊ฒ€์‚ฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์ด๋Š” ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ์˜ค๋ฅ˜๋ฅผ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ ํƒ€์ž…์„ ๋ช…์‹œํ•จ์œผ๋กœ์จ ์ฝ”๋“œ์˜ ์˜๋„ ๋˜ํ•œ ๋ช…ํ™•ํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— ํ˜‘์—…์‹œ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๋„ ๋†’์—ฌ์ค€๋‹ค. ๋˜ํ•œ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ์ž๋™์™„์„ฑ ๋“ฑ์˜ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ๊ฐœ๋ฐœ์˜ ํšจ์œจ์„ฑ๋„ ๋†’์—ฌ์ฃผ๋ฉฐ, Interface, Generic๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

 ํŠนํžˆ ์ด๋ฒˆ์— ์ž„์‹œ๋กœ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๋น ๋ฅด๊ฒŒ ํ•ด๋ณด๊ธฐ ์œ„ํ•ด JavaScript๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์—ˆ๋Š”๋ฐ, ํƒ€์ž…์˜ ๋ช…์‹œ๊ฐ€ ๋˜์–ด์žˆ์ง€ ์•Š์•„ ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ํƒ€์ž…์„ ์ฐพ์•„๋ณด๋Š” ์ผ๋„ ๋ช‡ ๋ฒˆ ์žˆ์—ˆ๊ณ , ์ž๋™์™„์„ฑ ๊ธฐ๋Šฅ ๋˜ํ•œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆํŽธํ•จ์„ ์กฐ๊ธˆ ๋Š๋ผ๊ธฐ๋„ ํ–ˆ์—ˆ๋‹ค.

 ์•„์ง์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋Šฅ์ˆ™ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜์ง€๋Š” ๋ชปํ•ด ์ ์šฉํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์ด ์ข€ ๋” ๊ฑธ๋ฆฌ๊ณ , ํ•™์Šต๊ณผ ํ™œ์šฉ์ด ๋” ํ•„์š”ํ•˜์ง€๋งŒ ์ต์ˆ™ํ•ด์ง„๋‹ค๋ฉด ๋ถ„๋ช… ๋””๋ฒ„๊น…๋„ ์‰ฌ์›Œ์ง€๊ณ , ๊ฐœ๋ฐœ์ž ๊ฒฝํ—˜์˜ ํ–ฅ์ƒ๋„ ๋Š๋‚„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๊ณ  ์•ž์œผ๋กœ๋„ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

 

๐Ÿ“Œ Styled-components๋ฅผ ์‚ฌ์šฉํ•œ ์ด์œ ๋Š”?

 Styled components๋Š” ํŠน์ • ์Šคํƒ€์ผ์ด ์ฒจ๋ถ€๋œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํŒจํ‚ค์ง€๋กœ JavaScript ํŒŒ์ผ์— CSS ์Šคํƒ€์ผ์„ ์ž‘์„ฑํ•˜๋Š” CSS-in-JS์˜ ์Šคํƒ€์ผ ์ •์˜ ๋ฐฉ์‹์ด๋‹ค. ์ด๋Š” ํ•ด๋‹น ์Šคํƒ€์ผ์ด ์‚ฌ์šฉ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์—๋งŒ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ณ , ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—๋Š” ์ „ํ˜€ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. ๋˜ํ•œ Styled components๋Š” JavaScript์˜ ํ™˜๊ฒฝ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, props๋ฅผ ํ™œ์šฉํ•œ ์กฐ๊ฑด๋ถ€ ์Šคํƒ€์ผ๋ง ๋“ฑ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ๊ณ ์œ ํ•œ className์ด ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— className ์ค‘๋ณต์— ๋Œ€ํ•œ ๊ฑฑ์ •์„ ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

 ์‹ค์ œ๋กœ Styled components๋ฅผ ์‚ฌ์šฉํ•œ style ์ž‘์„ฑ์‹œ className์˜ ์ค‘๋ณต์„ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ, ํ•˜๋‚˜์˜ ํŒŒ์ผ ์•ˆ์—์„œ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•˜์—ฌ ์Šคํƒ€์ผ ์ˆ˜์ •์‹œ ์–ด๋–ค ํŒŒ์ผ์— ํ•ด๋‹น ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜์–ด ์žˆ๋Š”์ง€ ์ฐพ์ง€ ์•Š์•„๋„ ๋˜๋Š” ์žฅ์ ์ด ์žˆ์—ˆ๋‹ค. ์ฆ‰, ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํŽธ๋ฆฌํ•œ ๊ฒƒ์ด๋‹ค.

 ์ง€์›ํ•œ ํšŒ์‚ฌ์˜ ๊ธฐ์ˆ ์Šคํƒ์— Styled-components์™€ Tailwind๊ฐ€ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ๋„ ์žˆ์—ˆ๋‹ค. ํ”„๋กœ์ ํŠธ๊ฐ€ ํฌ์ง€ ์•Š๊ณ , ๋น„๊ต์  ๊ฐ€๋ฒผ์šด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๋ผ CSS-in-JS์˜ ๋ฐฉ์‹๋„ ์ ํ•ฉํ•  ๊ฒƒ์ด๋ผ ํŒ๋‹จ๋˜์—ˆ๊ณ , ์ด์ „์— Styled-components๋ฅผ ์‚ฌ์šฉํ•œ ์ ์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ต์ˆ™ํ•˜๋‹ค๋Š” ์ด์œ ๋„ ์žˆ์—ˆ๋‹ค. ์‚ฌ์‹ค ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—๋Š” ๊ฐ„๋‹จํ•œ ์Šคํƒ€์ผ๋ง ์ •๋„๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— Styled-components๋ฅผ ๊ธฐ๋Šฅ์„ ์ตœ๋Œ€ํ•œ์œผ๋กœ ํ™œ์šฉํ•˜์ง€๋Š” ๋ชปํ•œ๊ฒƒ ๊ฐ™์ด ์กฐ๊ธˆ ์•„์‰ฝ๊ธฐ๋Š” ํ–ˆ์ง€๋งŒ.

 


 

2. ๋ ˆ์ด์•„์›ƒ ๊ณ ๋ฏผํ•˜๊ธฐ

์ฒ˜์Œ ๊ณผ์ œ๋ฅผ ๋ฐ›์•˜์„ ๋•Œ ๋‘ ๋Ž์Šค์— ๊ฑธ์ณ์ ธ ์žˆ๋Š” ๋ ˆ์ด์•„์›ƒ์„ ์–ด๋–ค์‹์œผ๋กœ ์งœ์•ผํ• ์ง€ ๊ณ ๋ฏผ์„ ๋งŽ์ด ํ–ˆ์—ˆ๋‹ค. 

๊ณ ๋ฏผ๋งŒ ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ์ง„๋„๊ฐ€ ์•ˆ๋‚˜๊ฐ€์„œ ๋…ธํŠธ์— ๋ ˆ์ด์•„์›ƒ ๊ตฌ์„ฑ์„ ์ •๋ฆฌํ•ด๋ณด๊ณ , ์šฐ์„ ์€ ์—‰๋ง์œผ๋กœ๋ผ๋„(?) ํ‹€์„ ์žก์•„๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

๋ณธ๊ฒฉ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์ „์— ์ƒ๊ฐํ–ˆ๋˜ ๋ ˆ์ด์•„์›ƒ

 

3. Vite๋กœ React + TypeScript ์กฐํ•ฉ์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑ ํ›„,
main.tsx ํŒŒ์ผ์˜ document.getElementById('root') ๋ถ€๋ถ„์˜ ์—๋Ÿฌ

// Before
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

// โฌ‡๏ธŽ ๋ณ€๊ฒฝ // 

// After
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

 

 

4. ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ ˆ์ด์•„์›ƒ์„ ์™„์ „ํžˆ ๋จผ์ € ์ž‘์—…

๋ ˆ์ด์•„์›ƒ๊ณผ ๋ผ์šฐํŒ…์ด ์›ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ž˜ ๋˜์ง€๊ฐ€ ์•Š์•„์„œ ๊ธฐ์กด์— ์ž‘์—…ํ•˜๋˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ž ๊น ๋†“๊ณ , ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์ต์ˆ™ํ•œ React + JS, Pure CSS ์กฐํ•ฉ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๋ ˆ์ด์•„์›ƒ์„ ๋จผ์ € ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค. 

์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋กœ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค์–ด๋ณธ ๋ ˆ์ด์•„์›ƒ ๋ฐ ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌ

 

5. zustand๋กœ notebooks ์ƒํƒœ๊ด€๋ฆฌ ๋„์ž…

์•ฑ ๋‚ด์—์„œ Notebook ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” Props Drilling์ด ๋งŽ์ด ์ผ์–ด๋‚˜์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณต์žกํ•ด ๋ณด์˜€๊ธฐ ๋•Œ๋ฌธ์— ์ƒํƒœ๊ด€๋ฆฌ ํˆด์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

๐Ÿ“Œ Zustand๋ฅผ ์ „์—ญ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์‚ฌ์šฉํ•œ ์ด์œ 

 Zustand๋Š” ๋…์ผ์–ด๋กœ “์ƒํƒœ”๋ผ๋Š” ๋œป์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ๊ฐ„๊ฒฐํ•œ Flux ์›์น™์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘๊ณ  ๋น ๋ฅด๊ฒŒ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

๋‚ด๊ฐ€ ์œ ์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด๋ดค๋˜ Redux๋Š” ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ, ์ฆ‰ ์–ด๋–ค ์ผ์„ ํ•˜๊ธฐ ์œ„ํ•ด ๊ผญ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋งŽ์ด ์š”๊ตฌ๋œ๋‹ค. Redux Toolkit์„ ํ†ตํ•ด ์„ค์ •์ด ์กฐ๊ธˆ ๋” ๊ฐ„ํŽธํ•ด์ง€๊ธฐ๋Š” ํ–ˆ์ง€๋งŒ, ๋‹ค๋ฅธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋Œ€๋น„ ์—ฌ์ „ํžˆ ์„ค์ •์ด ๋ณต์žกํ•˜๋ฉฐ ๋Ÿฌ๋‹ ์ปค๋ธŒ๊ฐ€ ๊ธด ํŽธ์ด๋‹ค.

 ๋ฐ˜๋ฉด Zustand๋Š” Redux์ฒ˜๋Ÿผ ๋งŽ์€ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค. Zustand๋Š” ์ƒํƒœ์™€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์•ก์…˜์„ ์ •์˜ํ•˜๊ณ , ๋ฆฌํ„ด ๋ฐ›์€ hook์„ ์–ด๋Š ์ปดํฌ๋„ŒํŠธ์—์„œ๋‚˜ import ํ•˜์—ฌ ์›ํ•˜๋Š” ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Zustand๋Š” ์‚ฌ์šฉ๋ฒ•์ด ๊ฐ„๋‹จํ•˜์—ฌ ๋Ÿฌ๋‹ ์ปค๋ธŒ๊ฐ€ ๋น„๊ต์  ๋‚ฎ์€ ํŽธ์ด๋‹ค.

๋˜ํ•œ ์ด๋ฒˆ note-app์€ API ์—†์ด ํ”„๋ก ํŠธ ๋‹จ์—์„œ๋งŒ ๊ตฌํ˜„ํ•˜๋ผ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— localStorage๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ๋Š”๋ฐ, Zustand๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด ์ค‘ storage๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ์—ˆ๋‹ค.

 ์ง€์›ํ•œ ํšŒ์‚ฌ์˜ ๊ธฐ์ˆ ์Šคํƒ์— ํฌํ•จ๋˜์–ด ์žˆ๋˜ Recoil๊ณผ Zustand ์ค‘ ๋ฌด์—‡์„ ์‚ฌ์šฉํ• ์ง€ ๊ณ ๋ฏผ์„ ์ž ๊น ํ–ˆ์—ˆ๋Š”๋ฐ, ๋น ๋ฅด๊ฒŒ ๊ฒฐ๊ณผ๋ฌผ์„ ๋‚ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด๋‹ค ์‚ฌ์šฉํ•˜๊ธฐ ๊ฐ„ํŽธํ•˜๊ณ  ํŽธ๋ฆฌํ•œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ œ๊ณตํ•˜๋Š” Zustand๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

 Zustand๋Š” ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ดค์ง€๋งŒ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์ด ๊ณต์‹๋ฌธ์„œ์—์„œ๋„ ํ•œ ํŽ˜์ด์ง€ ๋ฐ–์— ๋˜์ง€ ์•Š๊ณ , ๊ต‰์žฅํžˆ ๊ฐ„๋‹จํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋ ต์ง€ ์•Š๊ฒŒ ๊ธˆ๋ฐฉ ์ตํ˜€์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ Zustand์˜ ๋ฏธ๋“ค์›จ์–ด์ธ persist๋ฅผ ํ†ตํ•ด localStorage์— ์ „์—ญ์ƒํƒœ๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์ž๋™์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์ž ๊น, Flux ํŒจํ„ด์ด๋ž€?

Flux๋Š” ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ๊ธฐ๋ฐ˜์œผ๋กœ Action์„ ๋งŒ๋“ค๊ณ  Action์„ Dispatcher์— ์ „๋‹ฌํ•˜์—ฌ Store(Model)์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•œ ๋’ค View์— ๋ฐ˜์˜ํ•˜๋Š” ๋‹จ๋ฐฉํ–ฅ์˜ ํ๋ฆ„์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“œ๋Š” ์•„ํ‚คํ…์ฒ˜์ด๋‹ค.

 


5-1) Zustand ๊ฐ„๋‹จํžˆ ํ•™์Šต

 

Zustand Documentation

Zustand is a small, fast and scalable bearbones state-management solution, it has a comfy api based on hooks

docs.pmnd.rs

๊ณต์‹๋ฌธ์„œ์™€ ์œ ํŠœ๋ธŒ ์ผ๋ถ€ ์ž๋ฃŒ๋ฅผ ํ†ตํ•ด Zustand๋ฅผ ๊ฐ„๋‹จํžˆ ํ•™์Šตํ–ˆ๋‹ค.

์‚ฌ์šฉํ•˜๊ธฐ ๋„ˆ๋ฌด ๊ฐ„๋‹จํ•˜๊ณ  ์‰ฌ์›Œ์„œ ๋†€๋ผ์› ๋˜ Zustand. ์™œ Redux ๋Œ€์‹  Zustand์™€ ๊ฐ™์€ ์ƒํƒœ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ ๊ฒƒ ๊ฐ™์•˜๋‹ค.

 

5-2) Zustnad๋ฅผ ์ ์šฉํ•˜์—ฌ Props Drilling ์œผ๋กœ ๋ณต์žกํ•ด์ง„ ์ปดํฌ๋„ŒํŠธ ๋‹จ์ˆœํ™”ํ•˜๊ธฐ

// Before
// notebooks๊ฐ€ ์–ด๋””์—์„œ๋‚˜ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค notebooks๋ฅผ props๋กœ ๋‚ด๋ ค์ค˜์•ผ๋งŒ ํ–ˆ๋‹ค.
const notebooks = [...]
const Root = () => {
  return (
    <div className="app">
      <Sidebar notebooks={notebooks} />
      { notebooks.length > 0 && (
        <NoteContainer notebooks={notebooks} /> 
      ) }
    </div>
  )
}

// โฌ‡๏ธŽ ๋ณ€๊ฒฝ //

// After
// notebooks๋ฅผ store์—์„œ ๋ถˆ๋Ÿฌ์™€ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
import { useStore } from "../store/notebooks";

const Root = () => {
  const { notebooks } = useStore();

  return (
    <div className="app">
      <Sidebar/>
      { notebooks.length > 0 && (
        <NoteContainer />
      ) }
    </div>
  )
}

 

5-3) Zustand์˜ middleware persist๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ storage์— ์ƒํƒœ ์ €์žฅํ•˜๊ธฐ

๐Ÿ”— ๊ณต์‹๋ฌธ์„œ: https://docs.pmnd.rs/zustand/integrations/persisting-store-data

import { create } from "zustand";
import { persist } from 'zustand/middleware'

export const useStore = create(
  persist(
    (set) => ({
      notebooks: [],
      createNotebook: (name) => set((prev) => ({
        notebooks: [
          ...prev.notebooks,
          {
            id: new Date().getMilliseconds(),
            name,
            notelist: []
          }
        ]
      })),
  { name: "note-storage" }
  )
);

 

→ ์œ„์™€ ๊ฐ™์ด persist๋กœ ๊ฐ์‹ธ์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค. persist๋กœ ๊ฐ์‹ธ์ง„ ์ƒํƒœ์˜ ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธฐ๋ฉด localStorage์—๋„ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋œ๋‹ค. ์ฆ‰, notebook๋˜๋Š” note๋ฅผ ์ถ”๊ฐ€ํ–ˆ์„ ๋•Œ localStorage๋„ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋œ notebooks๋ฅผ ๋ฐ˜์˜ํ•œ๋‹ค.

 

์ฐธ๊ณ ๋กœ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜์—ฌ ํƒ€์ž…์„ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

import { create } from "zustand";
import { persist } from "zustand/middleware";

import { NoteType, NotebookType } from "../type/notebookTypes";

interface StoreType {
  notebooks: NotebookType[];
  createNotebook: (name: string) => void;
}

export const useStore = create<StoreType>()(
  persist(
    set => ({
      notebooks: [],
      createNotebook: name =>
        set(prev => ({
          notebooks: [
            {
              id: Date.now(),
              name,
              notelist: [],
            },
            ...prev.notebooks,
          ],
        })),
    }),
    {
      name: "NOTE_APP_STORAGE",
    },
  ),
);

 

6. LocalStorage์— ์–ด๋– ํ•œ ํ˜•์‹์œผ๋กœ notebooks๋ฅผ ์ €์žฅํ• ๊นŒ?

์ฒ˜์Œ์—๋Š” ์ž‘์—…์˜ ํŽธ์˜๋ฅผ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด notebook๊ณผ ํ•ด๋‹น notebook์˜ notelist๋ฅผ ๋”ฐ๋กœ ์ €์žฅํ•œ ๋”๋ฏธ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ํ–ˆ์—ˆ๋‹ค.

const notebooks = [
  { id: 1, name: "notebook1" },
  { id: 2, name: "notebook2" },
]

const noteList = {
  "notebook1" : [
    { id: 1, title: "new note1", content: "this is a new note", date: new Date() },
  ],
  "notebook2" : [
    { id: 2, title: "new note2", content: "this is a new note", date: new Date() },
    { id: 3, title: "new note3", content: "this is a new note", date: new Date() },
  ]
}

 

๊ทธ๋Ÿฐ๋ฐ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๊ฐ€ “notebooks”๋ผ๋Š” ํ•œ ๊ณณ์— ์ €์žฅ์ด ๋˜์–ด ์žˆ์–ด์•ผ ์ƒ์„ฑ๊ณผ ์ˆ˜์ • ๋“ฑ์˜ ์ž‘์—…์ด ํŽธํ•  ๊ฒƒ ๊ฐ™์•„ notebooks ๋ฐฐ์—ด์— notebook์„ ๋„ฃ๊ณ , notebook์— notelist์˜ ๋ฐฐ์—ด์„ ๋„ฃ๋Š” ํ˜•์‹์œผ๋กœ ์ž‘์—…ํ•˜์˜€๋‹ค.

 

const notebooks = [
  { 
    id: 1, 
    name: "notebook1", 
    notelist: [
      { id: 11, title: "new note1", content: "this is a new note", date: new Date() },
    ] 
  },
  { 
    id: 2, 
    name: "notebook2", 
    notelist: [
      { id: 21, title: "new note2", content: "this is a new note", date: new Date() },
      { id: 31, title: "new note3", content: "this is a new note", date: new Date() },
    ]
  },
]

 

 

โญ๏ธ

 

โฌ‡๏ธ ๊ธ€์ด ๊ธธ์–ด์ ธ์„œ ๋‚˜๋ˆ„์–ด ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๊ธ€ ์ž…๋‹ˆ๋‹ค. โฌ‡๏ธ 

 

[Mini Project] ํ”„๋ก ํŠธ์—”๋“œ ๊ณผ์ œํ…Œ์ŠคํŠธ๋กœ ๋งŒ๋‚œ Note-App ๊ตฌํ˜„ ๊ณผ์ • ์ •๋ฆฌ (2)

[Mini Project] Note-App ๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป Github Repository https://github.com/sw2377/note-app ์ž‘์—… ๊ธฐ๊ฐ„ : 2024. 01. 09 ~ 2024. 01. 16 ํ”„๋ก ํŠธ์—”๋“œ ์ฑ„์šฉ ๊ณผ์ œ๋กœ ์ˆ˜ํ–‰ํ•œ Note-App ๊ตฌํ˜„ ๊ณผ์ • (๊ตฌํ˜„ ์ค‘ ์—๋Ÿฌ์‚ฌํ•ญ & ๊ณ ๋ฏผํ•œ ๋ถ€๋ถ„)์„ ์ •

fay-story.com

๋ฐ˜์‘ํ˜•