20_Oct_2021 ๐Ÿฐ ์—˜๋ฆฌ์Šค AI ํŠธ๋ž™ TIL: ์ƒํƒœ๊ด€๋ฆฌ

์ƒํƒœ ๊ด€๋ฆฌ

- ์ƒํƒœ ๊ด€๋ฆฌ ๊ธฐ์ˆ ์ด๋ž€ ์•ฑ ์ƒ์—์„œ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ ๋“ฑ์— ์ €์žฅํ•˜๊ณ  ํ•˜๋‚˜ ์ด์ƒ์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

  • ํ•œ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ์˜ ์ƒํƒœ, ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ ์ƒํƒœ, ์ „์ฒด ์•ฑ์˜ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋ชจ๋‘ ํฌํ•จํ•œ๋‹ค.

MPA์™€ SPA์—์„œ์˜ ์ƒํƒœ ๊ด€๋ฆฌ

  • MPA์—์„œ๋Š” ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ด์šฉํ•ด ํŽ˜์ด์ฆˆ๋ฅผ ๋ Œ๋”๋งํ•˜๋ฏ€๋กœ, ํด๋ผ์ด์–ธํŠธ์˜ ๋ฐ์ดํ„ฐ์™€ ์„œ๋ฒ„์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํฐ ์ฐจ์ด๋ฅผ ๊ฐ€์ง€์ง€ ์•Š๋Š”๋‹ค.
  • SPA์—์„œ๋Š” ์ž์ฒด์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ , ์„œ๋ฒ„์™€์˜ ๋™๊ธฐํ™”๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๊ทธ ์™ธ ๋ฐ์ดํ„ฐ๋Š” Client๋งŒ์œผ๋กœ ๋ฐ์ดํ„ฐ ์œ ์ง€.

์ƒํƒœ ๊ด€๋ฆฌ ๊ธฐ์ˆ ์˜ ๋„์ž…

  • ์ƒํƒœ๊ฐ€ ๋งŽ์ง€ ์•Š๊ฑฐ๋‚˜, ์œ ์ €์™€์˜ ์ธํ„ฐ๋ ‰์…˜์ด ๋งŽ์ง€ ์•Š๋‹ค๋ฉด ๋งค ์ž‘์—… ์‹œ ์„œ๋ฒ„์™€ ๋™๊ธฐํ™”ํ•˜๋”๋ผ๋„ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ
  • ์•ฑ์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์ ์  ๋งŽ์•„์ง€๊ณ , ์œ ์ €์™€์˜ ์ธํ„ฐ๋ ‰์…˜ ์‹œ ์ž„์‹œ๋กœ ์ €์žฅํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์•„์ง€๋Š” ๊ฒฝ์šฐ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ๊ณ ๋ ค.
  • ํ”„๋ก ํŠธ์—”๋“œ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋ฐฑ์—”๋“œ์™€์˜ ๋ฐ์ดํ„ฐ ํ†ต์‹ ๋„ ์ถฉ๋ถ„ํžˆ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.
    ex) GraphQL

์ƒํƒœ ๊ด€๋ฆฌ ๊ธฐ์ˆ ์˜ ์žฅ์ :

  • ๋†’์€ ํ’ˆ์งˆ์˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์œ ๋ฆฌํ•˜๋‹ค.
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”, ๋„คํŠธ์›Œํฌ ์ตœ์ ํ™” ๋“ฑ์— ์œ ๋ฆฌํ•˜๋‹ค.
  • ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์˜ ๊ณ ๋„ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
    ex) localStorage๋ฅผ ํ™œ์šฉํ•œ persist state

์ƒํƒœ ๊ด€๋ฆฌ ๊ธฐ์ˆ ์˜ ๋‹จ์ 

  • Boilerplate ๋ฌธ์ œ.
  • ํŒŒ์•…ํ•ด์•ผ ํ•  ๋กœ์ง๊ณผ ๋ ˆ์ด์–ด๊ฐ€ ๋งŽ์•„์ง.
  • ์ž˜๋ชป ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์•ฑ์˜ ๋ณต์žก๋„ ๋งŒ์„ ๋†’์ด๊ฑฐ๋‚˜, ์„ฑ๋Šฅ์„ ์•…ํ™”.
    ex) global state์˜ ์ž˜๋ชป๋œ ํ™œ์šฉ์‹œ ์•ฑ ์ „์ฒด ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ.

์ƒํƒœ ๊ด€๋ฆฌ ๊ธฐ์ˆ ์ด ํ•ด๊ฒฐํ•˜๋Š” ๋ฌธ์ œ

๋ฐ์ดํ„ฐ ์บ์‹ฑ๊ณผ ์žฌํ™œ์šฉ

  • SPA์—์„œ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์‹œ๋งˆ๋‹ค ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋”ฉํ•œ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ธก๋ฉด์—์„œ MPA๋ฅผ ํฌ๊ฒŒ ๋„˜์–ด์„œ๊ธฐ ํž˜๋“ค์–ด์ง„๋‹ค.
  • ์˜คํžˆ๋ ค ๋„คํŠธ์›Œํฌ ์š”์ฒญ ์ˆ˜๊ฐ€ ๋งŽ์•„์ ธ ๋” ๋Š๋ฆด ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ๋ณ€๊ฒฝ์ด ์žฆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด, ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹ฑํ•˜๊ณ  ์žฌํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ณ€๊ฒฝ์ด ์žฆ๋‹ค๋ฉด, ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ ์‹œ์ ์„ ํŒŒ์•…ํ•ด ์ตœ์ ํ™”ํ•ด์•ผ ํ•œ๋‹ค.
    ex) ์ผ์ • ์‹œ๊ฐ„๋งˆ๋‹ค ์„œ๋ฒ„์— ์ €์žฅ, ํƒ€์ดํ•‘ 5์ดˆ ํ›„ ์„œ๋ฒ„์— ์ €์žฅ. (๋ณ€๊ฒฝ์ด ์žฆ๋‹ค)

Prop Drilling

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

Flux Pattern

  • 2014๋…„ Facebook์—์„œ ์ œ์•ˆํ•œ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด.
  • ์ผ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ์™€ UI๋ฐ˜์˜์„ ๋‹จ์ˆœํ™” ํ•˜์˜€๋‹ค.
  • React์˜ UI ํŒจํ„ด์ธ ํ•ฉ์„ฑ ์ปดํฌ๋„ŒํŠธ์™€ ์–ด์šธ๋ฆฌ๋„๋ก ์„ค๊ณ„๋จ
  • redux, react-redux ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์„ ํ–‰ ํŒจํ„ด์ด๋‹ค.

unidirectional data flow

Store(Data Source)๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด View๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๋“ฑ ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ํ๋ฅด๋Š” ๊ฐœ๋…์ด๋‹ค.

Flux ํŒจํ„ด๊ณผ MVC ํŒจํ„ด ๋น„๊ต

MVC

  • MVC ํŒจํ„ด์—์„œ๋Š” View์˜ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์—ฐ์‡„์ ์ธ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ผ์–ด๋‚˜ ์•ฑ์ด ์ปค์งˆ์ˆ˜๋ก ์—…๋ฐ์ดํŠธ์˜ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ธฐ ํž˜๋“ค๋‹ค.
  • View์—์„œ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์—ฐ์‡„์ ์ธ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ผ์–ด๋‚œ๋‹ค.
  • ํŠน์ • ์œ ์ €์˜ ์ธํ„ฐ๋ ‰์…˜์ด ์—ฌ๋Ÿฌ UI ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์˜ํ–ฅ์„ ์ค„ ๋•Œ, ์•ฑ์˜ ๋ณต์žก๋„์™€ ์—…๋ฐ์ดํŠธ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๊ธฐ ํž˜๋“ค๋‹ค.

MVC ํŒจํ„ด์˜ Model์€ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ณ , View๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ํ™”๋ฉด๋‹จ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ•˜๋‚˜์˜ Model์—์„œ ๋‹ค์ˆ˜์˜ View๋“ค์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ํ˜•ํƒœ์ด๋‹ค. ์—ฌ๊ธฐ์—์„œ MVC ํŒจํ„ด์€ Bidirectional(์–‘๋ฐฉํ–ฅ) ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ฐ–๋Š” ๊ตฌ์กฐ๋ฅผ ํ•˜๊ณ  ์žˆ๋‹ค. ์ฆ‰, ์„œ๋กœ ์—ฐ๊ฒฐ๋œ ์ƒํƒœ์ธ ๊ฒฝ์šฐ View๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๋ฉด Model ๋˜ํ•œ ์—…๋ฐ์ดํŠธ ๋œ๋‹ค.

Flux

  • ๋ฐ˜๋ฉด Flux๋Š” ํ•˜๋‚˜์˜ Action์ด ํ•˜๋‚˜์˜ Update๋งŒ์„ ๋งŒ๋“ค๋„๋ก ํ•œ๋‹ค.
  • ํ•˜๋‚˜์˜ ์œ ์ € ์ธํ„ฐ๋ ‰์…˜ ๋‹น ํ•˜๋‚˜์˜ Update๋งŒ์„ ๋งŒ๋“ค๋„๋ก ํ•œ๋‹ค.
  • Data์™€ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ ํ๋ฅด๋ฏ€๋กœ UI์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ์˜ˆ์ธกํ•˜๊ธฐ ์‰ฝ๋‹ค.

Store(๋ฐ์ดํ„ฐ ์†Œ์Šค) -> ๋‹ค์ˆ˜์˜ View. View๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜์–ด๋„ Store๋Š” ์—…๋ฐ์ดํŠธ ๋˜์ง€ ์•Š๋Š” unidirectional data flow

Flux์˜ ๊ตฌ์กฐ

  • Action -> Dispatcher -> Store -> View ์ˆœ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ํ๋ฅธ๋‹ค.

    Action: View๊ฐ€ Store๋ฅผ ์—…๋ฐ์ดํŠธ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์ƒ์„ฑํ•œ ๊ฒƒ. Reducer์— ๋„˜๊ฒจ์ง„ ํ›„, Store๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

  1. Store๋Š” ๋ฏธ๋ฆฌ Dispatcher์— callback์„ ๋“ฑ๋กํ•ด, ์ž์‹ ์ด ์ฒ˜๋ฆฌํ•  Action์„ ์ •์˜ํ•œ๋‹ค.

    callback: ์ด๋ฒคํŠธ์— ๋”ฐ๋ผ ์‹คํ–‰๋  ํ•จ์ˆ˜

  2. Action Creator๋Š” Action์„ ์ƒ์„ฑํ•˜์—ฌ Dispatcher๋กœ ๋ณด๋‚ธ๋‹ค.
  3. Dispatcher๋Š” Action์„ Store๋ฅผ ๋„˜๊ธด๋‹ค.
  4. Store๋Š” Action์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ๊ด€๋ จ View๋กœ ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.
  5. View๋Š” ๊ทธ์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ใ…‚๋ฐ›์•„์™€ ์ƒˆ๋กœ์šด UI๋ฅผ ๋งŒ๋“ ๋‹ค.
  6. ์œ ์ € ์ธํ„ฐ๋ ‰์…˜์ด ๋ฐœ์ƒํ•˜๋ฉด View๋Š” Action์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

React Hooks์„ ํ†ตํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

์ƒํƒœ ๊ด€๋ฆฌ์— ์‚ฌ์šฉ๋˜๋Š” Hooks

  • ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด React๊ฐ€ ์ œ๊ณตํ•˜๋Š” Hook ๋งŒ์œผ๋กœ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.
  • ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์— ์ƒํƒœ๋ฅผ ๋‘๊ณ , ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ๋ฐ์ดํ„ฐ์™€ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌ.

useState

  • ๋‹จ์ˆœํ•œ ํ•˜๋‚˜์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ์— ์ ํ•ฉ
  • const [state, seState] = useState(initState | initFn)
  • state๊ฐ€ ๋ฐ”๋€Œ๋ฉด, state๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”ํ•œ๋‹ค.
  • useEffect์™€ ํ•จ๊ป˜, state์— ๋ฐ˜์‘ํ•˜๋Š” Hook์„ ๊ตฌ์ถ•ํ•œ๋‹ค.

useRef

  • ์ƒํƒœ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด, ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์™€ ์ƒ๊ด€์—†์ด, ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ๋ฆฌ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ๋ฅผ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฆ‰, ์ƒํƒœ๊ฐ€ UI์˜ ๋ณ€๊ฒฝ๊ณผ ๊ด€๊ณ„์—†์„ ๋•Œ ์‚ฌ์šฉ.

    setTimout์˜ timerId๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ ๋“ฑ์ด ํ•ด๋‹น

  • uncontrolled component์˜ ์ƒํƒœ๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๋“ฑ ๋ฆฌ๋ Œ๋”๋ง์„ ์ตœ์†Œํ™”ํ•˜๋Š” ์ƒํƒœ ๊ด€๋ฆฌ์— ์‚ฌ์šฉ๋œ๋‹ค.

    Dynamic Form์˜ ์˜ˆ์‹œ๋ฅผ ๋“ค ์ˆ˜ ์žˆ๋‹ค.

useContext

  • ์ปดํฌ๋„ŒํŠธ์™€ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•  ๋•Œ ์‚ฌ์šฉ.
  • ๋ถ€๋ถ„์ ์ธ ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์ƒํƒœ ๊ด€๋ฆฌ, ์ „์ฒด ์•ฑ์˜ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋ชจ๋‘ ๊ตฌํ˜„ํ•œ๋‹ค.
  • Context Provider ์•ˆ์—์„œ ๋ Œ๋”๋ง ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š”, useContext๋ฅผ ์ด์šฉํ•ด ๊นŠ์ด nested ๋œ ์ปดํฌ๋„ŒํŠธ๋ผ๋„ ๋ฐ”๋กœ context value๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • context vlaue๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ชจ๋‘ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค.

useReducer

  • useState๋ณด๋‹ค ๋ณต์žกํ•œ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด flux pattern์— ๊ธฐ๋ฐ˜ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.
  • const [state, dispatch] = useReducer(reducer, initState)
  • nested state ๋“ฑ ๋ณต์žกํ•œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ƒํƒœ๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜, ์–ด๋–ค ์ƒํƒœ์— ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ฒ˜๋ฆฌ๋ฅผ ์ ์šฉํ•  ๋–„ ์œ ์šฉํ•˜๋‹ค.
  • ์ƒํƒœ ๋ณต์žกํ•˜๋‹ค๋ฉด, useState์— ๊ด€ํ•œ callback์„ ๋‚ด๋ ค์ฃผ๋Š” ๊ฒƒ๋ณด๋‹ค dispatch๋ฅผ prop์œผ๋กœ ๋‚ด๋ ค ๋ฆฌ๋ Œ๋”๋ง์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋œ๋‹ค.

useState๋ฅผ ํ™œ์šฉํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

  • ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ state์™€ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค. ๊ทธ state๋‚˜ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ prop์œผ๋กœ ๋‚ด๋ ค์ฃผ๋Š” ํŒจํ„ด์ด๋‹ค.
  • state๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ์ค‘๊ฐ„์— state๋ฅผ ๋„˜๊ธฐ๊ธฐ๋งŒ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค๋„ ๋ชจ๋‘ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค.
  • ์ƒํƒœ์™€ ์ƒํƒœ์— ๋Œ€ํ•œ ๋ณ€ํ™”๊ฐ€ ๋‹จ์ˆœํ•˜๊ฑฐ๋‚˜, ์ƒ๋Œ€์ ์œผ๋กœ ์†Œ๊ทœ๋ชจ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋‹ค.

TodoApp

function TodoApp() {
	const [todos, setTodos] = useState([]);
  	const [filter, setFilter] = useState('all');
  	const [globalId, setGlobalId] = useState(3000);
  
  	const toggleTodo = (id) => {
    		setTodos((todos)=>{
            	  todos.map((todo)=>{
                  	todo.id === id ? {...todo, completed: !todo.completed}
                  }: todo)
            })
    	}
     	const deleteTodo = (id) => {
        	setTodos((todos)=> todos.filter((todo)=> todo.id !== id));
        };
      	const addTodo = (title => {
        	setTodos((todos)=> [{title, id: globalId + 1}, ... todos]);
          	setGlobalId((id)=> id+1);
          
         return <TodosPage
          	state ={{todos, filter}}
            	toggleTodo={toggleTodo}
    		addTodo={addTodo}
  		deleleTodo={deleteTodo}
  		changeFilter={setFilter}
  		/>
        })
}

TodosPage

function TodosPage({state, addTodo, deleteTodo, toggleTodo, changeFilter}){
	const filterTodos = state.todos.filter((todo)=>{
    		const {filter} = state;
      		return (
            		filter === 'all' ||
              		(filter === 'completed' && todo.completed) ||
              		(filter === 'todo' && !todo.completed)
            );
    })
    
    return (
    	<div>
      		<h3>TodosPage</h3>
      		<TodoForm onSubmit={addTodo}/>
		<TodoFilter filter={state.filter} 
		changeFilter={changeFilter}/>
            	<ToDoList
		 todos={filteredTodos}
		 toggleTodo={toggleTodo}
		 deleteTodo={deleteTodo}
		 />
      	</div>
    );
}

TodoForm

function TodoForm({onSubmit}) {
  const [title, setTitle] = useState('');
  return (
    <form
      onSubmit={(e) =>{
    	e.preventDefault();
        onSubmit(title);
        setTitle('');
      }}
     >
      <label htmlFor ='todo-title'>Title</label>
      <input id='todo-title' type='text' 
	name='todo-title' onchange={(e)=>
    	setTitle(e.target.value)} value={title} />
      <button type='submit'>Make</button>
      </form>
    	);
}

todoList

function TodoList({todos, toggleTodo, deleteTodo}) {
  return (
  	<ul>
    	  {todos.map(({title, completed, id}) => (
  	    <li onClick{()=> toggleTodo(id)}>
    	    <h5>{title}</h5>
	    	<div>
    		  {completed ? ์ฒดํฌ์•„์ด์ฝ˜: ์“ฐ๊ธฐ ์•„์ด์ฝ˜}
       		  <button onClick={()=> 
		  deleteTodo(id)}>Delete</button>
 	    	</div>
	    </li>
  	))} 
    	</ul>
  );
}

TodoFilter

function TodoFilter({filter, changeFilter}) {
  return (
    <div>
    	<label htmlFor='filter'>Filter</label>
    	<select
    	  onChange{(e)=> changeFilter(e.target.value)}
	  id='filter'
	  name='filter'
	 >
          {filterList.map((filterText)=> (
            <option selected={filter === filterText} value={filterText}>
            </option>
          ))};
         </select>
    </div>
  );
}

useContext๋ฅผ ํ™œ์šฉํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

  • context API๋ฅผ ํ™œ์šฉ. Provider ๋‹จ์—์„œ ์ƒํƒœ๋ฅผ ์ •์˜ํ•˜๊ณ , ์ง์ ‘ ์ƒํƒœ์™€ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ useContext๋ฅผ ์ด์šฉํ•ด ๋ฐ”๋กœ ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด
  • useReducer์™€ ํ•จ๊ป˜, ๋ณต์žกํ•œ ์ƒํƒœ์™€ ์ƒํƒœ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ ๋กœ์ง์„
    ๋‘ ๊ฐœ ์ด์ƒ์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ™œ์šฉํ•˜๋„๋ก ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • state๋Š” ํ•„์š”ํ•œ ๊ณณ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๋ถˆํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•˜๋ฉฐ, Prop Drilling(Plumbing)์„ ๋ฐฉ์ง€ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ˜๋‹ค.

TodoContext

const TodoContext = createContext(null);

const initialState ={
	todos: [],
  	filter: 'all',
  	globalId: 3000,
};

function useTodoContext(){
  const context = useContext(TodoContext);
  if (!context){
    throw new Error('Use TodoContext inside Provider.');
  }
  return context;
}

function TodoContextProvider({children}){
  const values = useTodoState();
  return <TodoContext.Provider
  value={values}>{children}</TodoContext.Provider>;
}

function reducer(state, action) {
  switch(action.type) {
    case 'change.filter':
      return {...state, filter:
             action.payload.filter};
    case 'init.todos':
      return {...state, todos:
             action.payload.todos};
    case 'add.todo': {
      return {...state, todos:
             [{title: action.payload.title,
              id: state.globalId + 1}],
             globalId: state.globalId + 1};
    		}
    case 'delete.todo': {
      return {...state, todo:
             state.todos.filter((todo)=> todo.id !==
                              action.payload.id)};
    	}
    case 'toggle.todo': {
      return {...state, todos: state.todos.map((t)
               => t.id === action.payload.id ?
               {...t, completed: !t.compeleted}: t)};
    	}
      deafault: return state;
    }
}

function  useTodoState() {
  const [state, dispatch] = useReducer(reducer, initialState);
  const toggleTodo = useCallback((id)=> dispatch({type: 'toggle.todo', payload: {id} }, []);
  const deleteTodo = useCallback((id)=> dispatch({type: 'delete.todo', payload: {id} }, []);
  const addTodo = useCallback((title)=> dispatch({type: 'add.todo', payload: {title} }, []);
  const changeFilter = useCallback((filter)=> dispatch({type: 'change.filter', payload: {title} }, []);
  const initializeTodos = useCallback((todos)=> dispatch({type: 'init.todos', payload: {todos} }, []);
 
  return {state, toggleTodo, deleteTodo, addTodo, changeFilter, initializeTodos};
}

todoApp

function TodoApp() {
  return (
  	<TodoContextProvider>
    	 <TodosPage/>
        </TodoContextProvider>
  );
}

TodosPage

function TodosPage() {
  const {initializeTodos} = useTodoContext();
  
  // ์ฒ˜์Œ ๋งˆ์šดํŠธ ๋˜์—ˆ์„ ๋•Œ, todo๋ฅผ ๋ฐ›์•„์™€ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.
  useEffect(()=>{
    console.log('useEffect');
    fetchTodos().then(initializeTodos);
  }, [initializeTodos]);
  
  return (
  	<div>
    	  <TodoForm/>
    	  <TodoFilter/>
    	  <TodoList/>
        </div>
  );
}

TodoForm

function TodoForm(){
  const {addTodo} = useContext();
  const [title, setTitle] = useState('');
  
  return (
    <form
      onSubmit={(e)=>{
       e.preventDefault();
       addTodo(title);
       setTitle('');
      }}
    >
      <label htmlFor='todo-title>Title</label>
      <input 
	id='todo-title'
	type='text'
	name='todo-title'
	onChange={(e)=> setTitle(e.target.value)}
    	value={title}/>
      <button type='submit'>Make</button>
    </form>
  );
}

TodoList

function TodoList() {
  const {state, toggleTodo, deleleTodo} = useTodoContext();
  const { todos, filter } = state;
  
  const filteredTodos = todos.filter((todo)=>{
    return (
      filter === 'all' ||
      (filter === 'completed' && todo.completed) ||
      (filter === 'todo' && !todo.completed)
    );
  })
  
  return (
    <ul>
     {filteredTodos.map(({title, completed, id}) => (
       <li key={id} onClick={()=> toggleTodo(id)}>
         <h5>{title}</h5>
	 <div>
       	  {completed ? ์ฒดํฌ๋ฐ•์Šค์•„์ด์ฝ˜ :ํŽœ ์•„์ด์ฝ˜}
	   <button onClick={()=> deleteTodo(id)}>Delete</button>
    	 </div>
     ))}
     </ul>
  )
}

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ