Redux: RFC: Redux ์Šคํƒ€ํ„ฐ ํ‚คํŠธ

์— ๋งŒ๋“  2018๋…„ 03์›” 01์ผ  ยท  56์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: reduxjs/redux

#2858 ๋ฐ ์ด์ „ #2295์˜ ์ฃผ์„์— ๋”ฐ๋ฅด๋ฉด Redux ์ฝ”์–ด + ๋ช‡ ๊ฐ€์ง€ ์ผ๋ฐ˜์ ์ธ ๋ฏธ๋“ค์›จ์–ด + ์Šคํ† ์–ด ์ธํ•ธ์„œ + ๊ธฐํƒ€ ๋„๊ตฌ๋กœ ๊ตฌ์„ฑ๋œ ์‚ฌ์ „ ๊ตฌ์„ฑ๋œ ์Šคํƒ€ํ„ฐ ํ‚คํŠธ๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ์˜ˆ์•ฝํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ–ˆ๊ณ (๊ทธ๋ ‡์ง€๋งŒ ์•„์ง ๊ฒฐ์ •ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค) ๊ฑฐ๊ธฐ์—์„œ ๊ณต๋ฐฑ์„ ์ฑ„์šฐ๊ธฐ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ ํŒจํ‚ค์ง€์˜ ๋‚ด์šฉ์„ ์—ฌ๊ธฐ์— ์œ ์ง€ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๋‹ค๋ฅธ repo๋‚˜ ๋‹ค๋ฅธ ๊ฒƒ์„ ์„ค์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ์กฐ์‚ฌํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ํ˜„์žฌ ๊ธด๊ธ‰ํ•œ ํ•„์š”์„ฑ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

์Šคํƒ€ํ„ฐ ํŒจํ‚ค์ง€์— ํฌํ•จ๋˜๋Š” ํ•ญ๋ชฉ์— ๋Œ€ํ•ด ๋‚ด ์ƒ๊ฐ์— ์งง์€ ๋ชฉ๋ก์—๋Š” ์ƒ์šฉ๊ตฌ ์ถ•์†Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ํ•˜๋‚˜(๋˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ตฌ์ถ•ํ•œ ๊ฒƒ), ์ฝํฌ ๋ฐ ์‚ฌ๊ฐ€์™€ ๊ฐ™์€ ์ธ๊ธฐ ์žˆ๋Š” ๋ฏธ๋“ค์›จ์–ด, ์œ ์šฉํ•œ ๊ฐœ๋ฐœ ๋„๊ตฌ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. React ๊ด€๋ จ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ํ•˜์œ„ ์ง‘ํ•ฉ ํŒจํ‚ค์ง€๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค, ๊ทธ๊ฒƒ์ด ํ•„์š”ํ•œ์ง€ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค (RT ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค!).

CLI ๋„๊ตฌ์™€ ๊ทธ ๋ชจ๋“  ๊ฒƒ์„ ์‚ฌ์šฉํ•˜์—ฌ Create React App๊ณผ ๊ฐ™์€ ๊ฒฝํ—˜์„ ๊ตฌ์ถ•ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋” ๋‚˜์€ ๋””๋ฒ„๊น…๊ณผ ์ ์€ ์ฝ”๋“œ๋กœ ํ›Œ๋ฅญํ•œ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

discussion ecosystem feedback wanted

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๊ทธ๋ž˜์„œ, ์–ด... ๋‚ด๊ฐ€ ๊ฐ€์„œ ํ•œ ๊ฐ€์ง€:

์ด์ „ ์Šค๋‹ˆํŽซ๊ณผ ๋‹ค๋ฅธ ์ :

  • enhancers ๊ตฌ์„ฑ ์˜ต์…˜์„ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‚˜๋Š” ์ถ”๊ฐ€ selectorator ์˜์กด์„ฑ์„ ๋‚ด ๋ณด๋‚ธ createSelector

์ƒ๊ฐ?

๋ชจ๋“  56 ๋Œ“๊ธ€

์˜ˆ ์˜ˆ ์˜ˆ! ๋‚˜๋Š” ์ด๊ฒƒ์— ๋Œ€ํ•ด ๋งŽ์€ ์˜๊ฒฌ๊ณผ ์†Œ๋ง์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค! ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ž์ œํ•˜๊ณ  ํ† ๋ก ์ด ๋จผ์ € ์ง„ํ–‰๋˜๋„๋ก ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‹คํ˜„ ๊ฐ€๋Šฅ์„ฑ์ด ํ™•์‹คํ•˜์ง€ ์•Š์€ ์œ„์‹œ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์ค‘ ํ•˜๋‚˜๋Š” createReduxStore() ํ•จ์ˆ˜์— ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜๋Š” ๊ฐ์†๊ธฐ์šฉ ๋‚ด์žฅ HMR์ž…๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„ Webpack์— ๋Œ€ํ•œ ์ง€์‹๊ณผ ๋ช‡ ๊ฐ€์ง€ ์‹คํ—˜์— ๋”ฐ๋ฅด๋ฉด Webpack module.hot.accept() ์ฝœ๋ฐฑ์—์„œ ๋ฆฌ๋“€์„œ ํŒŒ์ผ์— ๋Œ€ํ•œ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๊ฒฝ๋กœ๊ฐ€ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด ์‹คํ˜„ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. Parcel ๋˜๋Š” ๋‹ค๋ฅธ ๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋ชจ๋“  ์‚ฌ๋žŒ์˜ ์ดˆ๊ธฐ ํ”ผ๋“œ๋ฐฑ ์š”์ฒญ:

  • ์ด ํŒจํ‚ค์ง€๋กœ ์ถฉ์กฑ์‹œํ‚ค๊ณ  ์‹ถ์€ ํ•ต์‹ฌ ์š”๊ตฌ ์‚ฌํ•ญ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?
  • ์ด๋Ÿฌํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค _๊ธฐ์กด_ ํŒจํ‚ค์ง€๋ฅผ ์ œ์•ˆํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?
  • ์ตœ์ข… ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•ด ์ผ์„ ๋‹จ์ˆœํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋Ÿฌํ•œ ํŒจํ‚ค์ง€๋ฅผ ๊ฐ€์žฅ ์ž˜ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๊ฐ€๋Šฅํ•œ ๋ณ€ํ˜•์ด ์„œ๋กœ _๊ฒฉ๋ฆฌ_๋˜๋Š” 3๊ฐ€์ง€ ํ•ต์‹ฌ ํฌ์ธํŠธ๋ฅผ ์ง€์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. ์ฒซ ๋ฒˆ์งธ ์š”์ . ๊ฐ์†๊ธฐ ๊ด€๋ฆฌ์ž
    1.0 ๋ฐ”๋‹๋ผ(์—ฌ๊ธฐ๊ฐ€ ๋งž๋‚˜์š”?)
    1.1 Immer.js
    1.2 ์ฝ”์–ด๋•์Šค
    1.3 100500 ๋” ๋งŽ์€ ํŒจํ‚ค์ง€

  2. ๋‘ ๋ฒˆ์งธ ํฌ์ธํŠธ. ๋ฏธ๋“ค์›จ์–ด
    2.0 ๋ฐ”๋‹๋ผ(์‚ฌ์‹ค์ธ๊ฐ€์š”?)
    2.1 ์‚ฌ๊ฐ€
    2.2 ์—ํ”ฝ
    2.3 100500๊ฐœ ์ด์ƒ์˜ ํŒจํ‚ค์ง€.

  3. ์„ธ ๋ฒˆ์งธ ํฌ์ธํŠธ. ๋ฐ˜์‘ ํ†ตํ•ฉ
    3.0. ๋ฐ”๋‹๋ผ(์—ฌ๊ธฐ์„œ ๊ทธ๋Ÿฐ๊ฐ€์š”?)
    3.1. ๋ถ€๋ถ„๊ณต๊ฐ„
    3.2 ์žฌ์ž‘์„ฑ
    3.3 100500๊ฐœ ์ด์ƒ์˜ ํŒจํ‚ค์ง€.

4์œ„, ๋ณด๋„ˆ์Šค, ํฌ์ธํŠธ
4.0 _boilerplate_์—์„œ ์ด ๋ชจ๋“  ๊ฒƒ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.

"์„ ํƒํ•œ ๊ฒƒ"์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ €๋Š” ์‚ฌ๊ฐ€์™€ ์—ํ”ฝ์„ ๋ชจ๋‘ ์ข‹์•„ํ•˜๊ณ  ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ผ๋ถ€ "์š”๋ฆฌ ์˜์ˆ˜์ฆ"๊ณผ ์กฐํ•ฉ์— ๋Œ€ํ•œ ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ์ œ๊ณตํ• 

์˜ˆ๋ฅผ ๋“ค์–ด ๋Œ€๋ถ€๋ถ„์˜ redux ์‚ฌ์šฉ์ž๋Š” ๋ฏธ๋“ค์›จ์–ด์— ๋Œ€ํ•ด __only__ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ฆฌ๋“€์„œ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•˜๋Š” ์ž‘์€ ๋ถ€๋ถ„(immutable.js์— ๋Œ€ํ•ด ๋งŽ์ด _๋“ค์Œ_), ๋ช‡ ๊ฐ€์ง€๋งŒ React ํ†ตํ•ฉ์„ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

์Šคํƒ€ํ„ฐ ํ‚คํŠธ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์™„์ „ํ•˜๊ณ  _ํ’๋ถ€ํ•œ_ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ์Šคํƒ€ํ„ฐ ํ‚คํŠธ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Twitter์—์„œ setup/boilerplate์˜ ๋ฌธ์ œ์ ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์š”์ฒญํ–ˆ์Šต๋‹ˆ๋‹ค. https://twitter.com/acemarke/status/969040835508604929

ํŠธ์œ„ํ„ฐ์—์„œ ๋‚ด ์‘๋‹ต ๋ณต์‚ฌ:

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

์ด๋ฅผ ์œ„ํ•ด create-redux-app ์Šคํƒ€์ผ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์€ - ๊ทธ๋Ÿฌ๋‚˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ณด๋‹ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋” ๋งŽ์ด ์„ค๊ณ„๋จ - ํŠนํžˆ Redux ์œ ์ง€ ๊ด€๋ฆฌ์ž๊ฐ€ ๊ถŒ์žฅํ•˜๋Š” ๋ชจ๋ฒ” ์‚ฌ๋ก€ ๋ฐ ๊ถŒ์žฅ ์‚ฌ๋ก€๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ณด์ฆํ•˜๋Š” ์ €์žฅ์†Œ๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์€ ๋ช…ํ™•์„ฑ์„ ๊ฐ•ํ™”ํ•˜๊ณ  ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ๋จผ ๊ธธ์„ ๊ฐˆ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ ์ ˆํ•œ Redux ์‚ฌ์šฉ์—.

์šฐ๋ฆฌ๋Š” Reactiflux์—์„œ ์ด๊ฒƒ์— ๋Œ€ํ•ด ์•ฝ๊ฐ„ ์ด์•ผ๊ธฐํ–ˆ๊ณ  ์ด ์Šค๋ ˆ๋“œ์— ํฌํ•จ๋˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ํ†ตํ•ฉ์„ ๋ณต์‚ฌํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ค‘ ์ผ๋ถ€๋Š” ์ด๋ฏธ @timdorr์— ๋‚˜์—ดํ•œ ์š”์ ๊ณผ ๊ฒน์นฉ๋‹ˆ๋‹ค(๊ทธ๋ž˜์„œ ์—ด๊ด‘์ ์ธ ํ™•์ธ์ž…๋‹ˆ๋‹ค).

  1. ๋” ๋‚˜์€ ๊ธฐ๋ณธ๊ฐ’, ์ฆ‰ ๋ฏธ๋“ค์›จ์–ด, redux ๊ฐœ๋ฐœ ๋„๊ตฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋” ์‰ฌ์šด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ createStore ์ฃผ๋ณ€ ๋ž˜ํผ
  2. ๋‚ด์žฅ ๋ถˆ๋ณ€ ๋„์šฐ๋ฏธ(immer?)
  3. createReducer ๋‚ด์žฅ(ํ‘œ์ค€ "๋ฆฌ๋“€์„œ ์กฐํšŒ ํ…Œ์ด๋ธ”" ์œ ํ‹ธ๋ฆฌํ‹ฐ)
  4. ๋‚ด์žฅ ์žฌ์„ ํƒ
  5. FSA๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” ์•ก์…˜ ํฌ๋ฆฌ์—์ดํ„ฐ
  6. ๋น„๋™๊ธฐ ์ž‘์—… ์ง€์›/๋ถ€์ž‘์šฉ ์ง€์›(ํ”Œ๋Ÿญ์Šค, ์‚ฌ๊ฐ€, ๋ฌด์—‡?)

Henrik Joreteg์˜ redux-bundler์™€ ๊ฐ™์ด ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒƒ์„ ๊ณต์‹์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

https://github.com/HenrikJoreteg/redux-bundler

๊ฐ„๊ฒฐํ•˜๊ณ  (์ข‹์€ ๋ฐฉ์‹์œผ๋กœ) ๋…๋‹จ์ ์ด๋ฉฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „๋ฐ˜์—์„œ redux ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์—ฌ์ „ํžˆ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ํ›Œ๋ฅญํ•œ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๋งคํ˜น์ ์œผ๋กœ ๋ณด์ด๋Š” ํ”„๋กœ์ ํŠธ์ด๋ฉฐ, ๋” ์ž์„ธํžˆ ์กฐ์‚ฌํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์ด ์ž‘์—…์œผ๋กœ ๋‚ด๊ฐ€ ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚ฌ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋‚ด๊ฐ€ ์ •๋ง๋กœ ๋‹ฌ์„ฑํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๋ช‡ ์ค„์˜ ์ฝ”๋“œ์™€ ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ๊ตฌ์„ฑ ์„ ํƒ์œผ๋กœ Redux ์ €์žฅ์†Œ ์„ค์ •("zero-config JS" ํƒœ๊ทธ๋ผ์ธ Webpack ๋ฐ Parcel์— ํ•ด๋‹นํ•˜๋Š” Redux๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Œ).
  • CRA์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๊ตฌ์„ฑ ํ•ญ๋ชฉ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆจ๊ฒจ์ ธ ์žˆ์œผ๋ฉฐ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ข‹์€ ๊ธฐ๋ณธ๊ฐ’์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‹ ์ค‘ํ•˜๊ฒŒ ์„ ํƒํ•œ ํŒจํ‚ค์ง€๋ฅผ ํฌํ•จํ•˜์—ฌ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ณ  "๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค". ์˜ˆ๋ฅผ ๋“ค์–ด Immer๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๋ฆฌ๋“€์„œ ๋กœ์ง ์ž‘์„ฑ์„ ๋Œ€ํญ ๋‹จ์ˆœํ™”ํ•˜๊ณ  dev์—์„œ ์ƒํƒœ๋ฅผ ๊ณ ์ •์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ํ›Œ๋ฅญํ•œ ์„ ํƒ์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. (๋‚ด๊ฐ€ ์œ ์ผํ•˜๊ฒŒ ์ฃผ์ €ํ•˜๋Š” ๊ฒƒ์€ "๋ณ€ํ˜•"ํ•˜๋Š” ์ฝ”๋“œ์˜ ๋ชจ์–‘์ด ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.)

์ด ๋…ธ๋ ฅ์˜ ๋ฒ”์œ„๋ฅผ ์ƒ๋‹นํžˆ ์ข๊ฒŒ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ง€๋‚œ ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ Reactiflux์—์„œ @hswolff ๋ฐ @nickmccurdy ์™€ ์ฑ„ํŒ…์„ ํ–ˆ์œผ๋ฉฐ ํ† ๋ก ์€ ํ›Œ๋ฅญํ–ˆ์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ ์ƒํ™ฉ์„ ์ง€๋‚˜์น˜๊ฒŒ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” 12๊ฐ€์ง€ ์ค‘๋ณต ์‚ฌ์šฉ ์‚ฌ๋ก€์™€ ์•„์ด๋””์–ด๋ฅผ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์œ ์šฉํ•˜๊ณ  ๊ฐ€์น˜ ์žˆ๊ณ  ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ _๋ฌด์–ธ๊ฐ€_๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ๊ณต์œ ํ•˜์ง€ ์•Š๊ณ  ์กฐํ•ฉํ•˜๊ณ  ์‹ถ๊ณ , ๋‹ค๋ฅธ ๋…ผ์˜๋Š” ์˜†์œผ๋กœ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค(์˜ˆ: Redux "๋ฒˆ๋“ค" ๋˜๋Š” "๋ชจ๋“ˆ"์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ API ๋“ฑ).

API์˜ ์ดˆ๊ธฐ ์ƒ์šฉ๊ตฌ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด ์œ„์— ๊ฒŒ์‹œํ•œ ๋‚ด ๋ชฉ๋ก์˜ 1-3์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

// #1
export function createStore({
    // If an object is given it's passed to combineReducers
    // otherwise it's just used directly.
    reducer: Object<String, Function> | Function,
    // Middleware is built-in and accepts an array of middleware functions.
    middleware: Array<MiddlewareFunction>,
    // Built-in support for devtools.
    // Defaults to NODE_ENV !== production
    devTools: boolean,
    // Same as current createStore.
    preloadedState,
    // Same as current createStore.
    enhancer,
}) {};

// Re-exported for easy usage.
export function combineReducers() {}

// #2
// Re-export immer as the built-in immutability helper.
import immer from 'immer';
export const createNextState = immer;

// #3
// Export an already written version of createReducer .
// Same thing as https://redux.js.org/recipes/structuring-reducers/refactoring-reducers-example#reducing-boilerplate
export function createReducer() {}

๊ทธ๋ž˜, ๋‚œ ๊ทธ๊ฑธ ํŒŒํ—ค์ณ. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ๋ชฉ๋ก์˜ ๋งจ ์•ž์— ํ•ญ์ƒ thunk() ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ง์ ‘ ์ œ๊ณตํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์—ฌ๊ธฐ์—์„œ ์ธํ•ธ์„œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ถ”๊ฐ€ ํ•  ๊ฒƒ applyMiddleware() ์–ด๋–ค ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์žˆ๋‹ค๋ฉด ์ž๋™์œผ๋กœํ•˜๊ณ , ์‚ฌ์šฉ composeWithDevtools ์—์„œ ๊ธฐ๋Šฅ์„ redux-devtools-extension ์˜ ๊ฒฝ์šฐ devTools : true . ์ด "์‰ฌ์šด ์„ค์ •"์„ ์œ„ํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ์—์„œ "์ธํ•ธ์„œ"๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ•ํ™”์ œ ์ œ๊ฑฐ์— +1.

์ฝํฌ๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜๋Š” ์‹œ์ ์—์„œ ๊ธฐ๋ณธ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ๊ธฐ๋Šฅ์„ ๋‚ด๋ณด๋‚ด์„œ ์ผ€์ดํฌ๋„ ๋จน๊ณ  ๋จน์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

export function createDefaultMiddleware(...additional) {
    return [thunk(), ...additional],
}

// Within a user's application
createStore({
    // By default it's just createDefaultMiddleware()
    // However if you want to add any other middleware you can.
    middleware: createDefaultMiddleware(any, other, middleware)
})

๋„ค, ๋Œ€๋žต์ ์œผ๋กœ ์ œ๊ฐ€ ์ƒ๊ฐํ•˜๊ณ  ์žˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด ์ฃผ์š” ๊ด€์‹ฌ์‚ฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ thunk ์˜ ์ž์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š”์ง€ ์•Œ์•„๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ์˜ค๋Š˜ ์ €๋…์— ์ด๊ฒƒ์ €๊ฒƒ ์ข€ ๊ฐ€์ง€๊ณ  ๋†€๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋‹ค์Œ์€ ์ฒซ ๋ฒˆ์งธ ์ปท์ž…๋‹ˆ๋‹ค. ์ƒ๊ฐ?

// configureStore.js
import {createStore, compose, applyMiddleware, combineReducers} from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import createNextState from "immer";

import isPlainObject from "./isPlainObject";

import thunk from "redux-thunk";


const IS_DEVELOPMENT = process.env.NODE_ENV !== "production";

export function createDefaultMiddleware(...additional) {
    return [thunk, ...additional];
}

export function configureStore(options = {}) {
    const {
        reducer,
        middleware = createDefaultMiddleware(),
        devTools = IS_DEVELOPMENT,
        preloadedState,
    } = options;

    let rootReducer;

    if(typeof reducer === "function") {
        rootReducer = reducer;
    }
    else if(isPlainObject(reducer)) {
        rootReducer = combineReducers(reducer);
    }
    else {
        throw new Error("Reducer argument must be a function or an object of functions that can be passed to combineReducers");
    }

    const middlewareEnhancer = applyMiddleware(...middleware);

    const storeEnhancers = [middlewareEnhancer];

    let finalCompose = devTools ? composeWithDevTools : compose;

    const composedEnhancer = finalCompose(...storeEnhancers);

    const store = createStore(
        rootReducer,
        preloadedState,
        composedEnhancer
    );

    return store;
}

export function createReducer(initialState, actionsMap) {
    return function(state = initialState, action) {
        const {type, payload} = action;

        return createNextState(state, draft => {
            const caseReducer = actionsMap[type];

            if(caseReducer) {
                return caseReducer(draft, payload);
            }

            return draft;
        });
    }
}
export {createNextState, combineReducers};

๊ทธ๋ฆฌ๊ณ  ํ•„์ˆ˜ ์˜ˆ์ œ ํ• ์ผ ์•ฑ:

import {configureStore, createReducer} from "./configureStore";

const ADD_TODO = "ADD_TODO";
const TOGGLE_TODO = "TOGGLE_TODO";
const SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER";

function setVisibility(state, newFilterType) {
    return newFilterType;
}

const visibilityReducer = createReducer("SHOW_ALL", {
    [SET_VISIBILITY_FILTER] : setVisibility
});


function addTodo(state, newTodo) {
    state.push({...newTodo, completed : false});
}

function toggleTodo(state, payload) {
    const {index} = payload;

    const todo = state[index];
    todo.completed = !todo.completed;
}

const todosReducer = createReducer([], {
    [ADD_TODO] : addTodo,
    [TOGGLE_TODO] : toggleTodo
});


const preloadedState = {
    todos: [{
        text: 'Eat food',
        completed: true
    }, {
        text: 'Exercise',
        completed: false
    }],
    visibilityFilter : 'SHOW_COMPLETED'
};


const store = configureStore({
    reducer : {
        todos : todosReducer,
        visibilityFilter : visibilityReducer,
    },
    preloadedState,
});

const exercise1 = store.getState().todos[1];

store.dispatch({type : "TOGGLE_TODO", payload : {index : 1}});

const exercise2 = store.getState().todos[1];

console.log("Same object: ", exercise1 === exercise2);

store.dispatch({type : "SET_VISIBILITY_FILTER", payload : "SHOW_COMPLETED"});

console.log(store.getState());

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ด ์Šค๋ ˆ๋“œ์—์„œ ๋…ผ์˜๋˜๋Š” lib์˜ ๊ฒฌ๊ณ ํ•œ ์‹œ์ž‘์ด์ž ํ›Œ๋ฅญํ•œ ์‹œ์ž‘์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์œ„ ์˜ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ ๋ชฉ๋ก์—์„œ ํฌ์ธํŠธ

์ด๊ฒƒ ์œ„์— ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒƒ์€ ํ›Œ๋ฅญํ•  ๊ฒƒ์ด์ง€๋งŒ ์ด๊ฒƒ์€ ๊ฒฌ๊ณ ํ•œ ํ† ๋Œ€์ž…๋‹ˆ๋‹ค.

๊ตฌํ˜„์— ์ง‘์ค‘ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” DX์— ์ง‘์ค‘ํ–ˆ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๋ณด๋‚ด๊ธฐ/API๋Š” ๋ฌด์—‡์ด๋ฉฐ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๊นŒ? ๊ตฌ์„ฑ๋งŒ ์‚ดํŽด๋ณด๊ณ  ์‚ฌ๋žŒ๋“ค์ด ์ƒ์ ์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์‚ดํŽด๋ณผ ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ธฐํšŒ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ž„์˜์˜ ์•„์ด๋””์–ด:

๋” ์‰ฌ์šด ๊ฐ์†๊ธฐ/์•ก์…˜ ์Œ:

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

import { createStore, createActionPack, combineActionPacks } from 'redux/starter'

const counter = createActionPack({
  increment: counter => counter + 1,
  decrement: counter => counter - 1,
  set: (_, value) => value
}, 0)

const posts = createActionPack({
  load: async () => await fetch('/posts'),
  create: async (state, text) => state.push(await fetch('/posts/new', { body: { text } }))
}, [])

const store = createStore(
  combineActionPacks(
    counter,
    posts
  )
)

store.dispatch(counter.increment())
store.dispatch(counter.set(42))

await store.dispatch(posts.load())
await store.dispatch(posts.create('First!'))

์‹ฑ๊ธ€ํ†ค ์ €์žฅ

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋“  ํŒ๋งค๋˜์ง€๋Š” ์•Š์ง€๋งŒ React Router์— ํžˆ์Šคํ† ๋ฆฌ ์‹ฑ๊ธ€ํ†ค์ด ์žˆ์—ˆ๋˜ ์‹œ์ ˆ์„ ์ƒ๊ฐํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์™„๋ฒฝํ•˜์ง€๋Š” ์•Š์•˜์ง€๋งŒ ์–ด๋Š ์ •๋„ ๊ฐ€์น˜๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.

import { store, configureStore } from 'redux/starter'

configureStore(reducer, middleware, initialState)

store.dispatch(someAction())
store.getState()

import ๋ฅผ ํ†ตํ•œ ๊ตฌ์„ฑ ๊ฐ€๋Šฅ

๋‚˜๋Š” ๋งˆ์ง€๋ง‰์„ ์œ„ํ•ด ๋‚˜์˜ ์—‰๋šฑํ•œ ๊ฒƒ์„ ์•„๋‚„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€์„œ ๊ตฌ์„ฑ์„ ํ—ˆ์šฉํ•œ๋‹ค๋ฉด ์–ด๋–จ๊นŒ์š”? ์ง€์ €๋ถ„ํ•˜๊ฑฐ๋‚˜ ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ๋ฐฐํ›„์—์„œ ์ „์—ญ ์ „๋‹ฌ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ ์ดˆ์ ์€ ์šฐ๋ฆฌ ํŽธ์ด ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž์ž…๋‹ˆ๋‹ค.

import { createStore } from 'redux/starter'
import 'redux/starter/devtools'
import 'redux/starter/logger'
import 'redux/starter/immutable'

// OR!

import { createStore } from 'redux/starter/developer'
import { createStore } from 'redux/starter/production'

๋‘ ๋ฒˆ์งธ ์˜ˆ์—์„œ๋Š” ๋นŒ๋“œ ๋„๊ตฌ์—์„œ ํ•ด๋‹น ํŒจํ‚ค์ง€์— ๋ณ„์นญ์„ ์ง€์ •ํ•˜๊ฑฐ๋‚˜ React๊ฐ€ ํ•˜๋Š”

์ข‹์•„, ์ง€๊ธˆ์€ ๊ทธ๊ฒŒ ๋‚˜๋ถ€ํ„ฐ์•ผ. ๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ ์ €๋Š” ์‚ฌ์šฉ์ž๋ฅผ ๋จผ์ € ์ƒ๊ฐํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์šฐ๋ฆฌ๊ฐ€ ํ•ด์•ผ ํ•˜๋Š” ๋”๋Ÿฝ๊ณ  ์ถ”์•…ํ•œ ํ•ต์€ ๋ฌด์—‡์ด๋“  ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. ์š”์ ์€ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๊ฐ€ ์ ๊ณ  ๊ตฌ์„ฑ์ด ๋œ ํ•„์š”ํ•œ ํ›Œ๋ฅญํ•œ DX์ž…๋‹ˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ ์ด๋Ÿฌํ•œ ์•„์ด๋””์–ด๋ฅผ ์‚ฌ๋ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐํ˜€์ง„ ๊ฒƒ์ด ๋ฌด์—‡์ด๋“  ๋‚˜์˜ ์œ ์ผํ•œ ์†Œ์›์€ ๋” ๋‚ฎ์€ ์ˆ˜์ค€์˜ ํ‘œ์ค€ Redux API์— ๋Œ€ํ•œ ์šฐ์•„ํ•œ ์•ก์„ธ์Šค์ž…๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์ด ๋งž์ถคํ™”ํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์ถœํ•  ์ˆ˜ ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๊ฒฝ๋กœ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๋ฉฐ์น ์ด๋“  1๋…„์ด๋“  2๋…„์ด๋“  ๊ฐ„์— ๊ฐ„๋‹จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Lock-in์€ ๋‚ด๊ฐ€ ๋ณธ ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค(Jumpstate๋ฅผ ์‚ฌ์šฉํ•œ ๋‚ด ์ž์‹ ์˜ ์‹œ๋„์™€ Rematch์™€ ๊ฐ™์€ ์ตœ์‹  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ). ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ํ”ผํ•  ์ˆ˜์žˆ์„์ˆ˜๋ก ์‚ฌ๋žŒ๋“ค์˜ ์ž…์— ๋” ์ข‹์€ ๋ง›์„ ๊ฐ–๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค :)

์˜ˆ, "์•ก์…˜ ํŒฉ"๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฝค ๋งŽ์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณด์•˜๊ณ (์ ์–ด๋„ "๋ฆฌ๋“€์„œ ์ž‘์„ฑ, ํ‚ค๋ฅผ ์•ก์…˜ ์œ ํ˜• ๋ฐ ์•ก์…˜ ์ƒ์„ฑ๊ธฐ๋กœ ์ „ํ™˜" ์ธก๋ฉด์—์„œ) ๋˜์ง€๊ธฐ๋ฅผ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€ ํ•ญ๋ชฉ์œผ๋กœ - ์•„์ง ๊ฑฐ๊ธฐ์— ๋„๋‹ฌํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. "combineActionPack" ์ธก๋ฉด์ด ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์–ด์ ฏ๋ฐค @hswolff ์™€ ์ถ”๊ฐ€ ํ† ๋ก ์„ ์„ ํƒ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€/๋‹ค์‹œ ๋‚ด๋ณด๋‚ด๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

import createSelector from 'selectorator';

// selector created with single method call
const getBarBaz = createSelector(['foo.bar', 'baz'], (bar, baz) => {
  return `${bar} ${baz}`;
});

const selectTodos = state => state.todos ์™€ ๊ฐ™์ด ์†์œผ๋กœ ์“ด "๊ฐ„๋‹จํ•œ ์ž…๋ ฅ" ์„ ํƒ์ž๋ฅผ ๋งŽ์ด ์ค„์ด๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

์„ ํƒ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ด์ ์ด ์žˆ๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋˜ํ•œ ๊ทธ๊ฒƒ์— ์˜ํ•ด ํ™œ์„ฑํ™”๋˜๋Š” ์ƒํƒœ ํ˜•ํƒœ์— ๋Œ€ํ•œ ์˜๊ฒฌ์„ ์ œ๊ณตํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ package.json ์— ์„ค์น˜ํ•ด์•ผ ํ•˜๋Š” ํ•ญ๋ชฉ์ด ํ•˜๋‚˜ ์ค„์–ด๋“ค๊ธฐ ๋•Œ๋ฌธ์— ์ ์–ด๋„ ๋ถ€๋ถ„์ ์œผ๋กœ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

์„ ํƒ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํ›Œ๋ฅญํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ด€๋ จ์„ฑ: apollo-boost ์Šคํƒ€ํ„ฐ ํ‚คํŠธ ์— ๋Œ€ํ•œ ์ตœ๊ทผ ๊ธฐ์‚ฌ

mapStateToProps ๊ฐ€ Array ์ผ ๋•Œ reselect (๋˜๋Š” ์œ ์‚ฌํ•œ ์„ ํƒ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก react-redux ์˜ connect ์˜ API๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์„ ํƒ์ž์˜ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•˜๊ณ  ํ˜ธํ™˜์„ฑ์„ ์†์ƒ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋งค์šฐ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ React ๋˜๋Š” react-redux ์™€ ๋ฐ˜๋“œ์‹œ ๊ฒฐํ•ฉ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋Š” ์•Š์ง€๋งŒ react-redux ์˜ต์…˜์„ ํ†ตํ•ด ํ™•์žฅ์„ฑ์„ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ react-redux ์Šคํƒ€ํ„ฐ ํ‚คํŠธ๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” ๋‹ค๋ฅธ ํŒจํ‚ค์ง€๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋„ค, ์ €๋Š” ์ด ์ดˆ๊ธฐ MVP๋ฅผ ์œ„ํ•ด React-Redux๋กœ ์•„๋ฌด ๊ฒƒ๋„ ํ•  ์ƒ๊ฐ์ด ์—†์Šต๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ Redux ์ˆ˜์ค€์—์„œ ์„ ํƒ๊ธฐ์— ๋Œ€ํ•œ ์ถ”์ƒํ™”๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์ด๊ฒƒ์ด react-redux ์œ„์—์„œ๋งŒ ํšจ๊ณผ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ? ๋ฌธ์ œ๋Š” ์ด๊ฒƒ์ด react-redux์™€ ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜์ง€ ์•Š๋‹ค๋ฉด ์ง€๊ธˆ์€ ์„ ํƒ๊ธฐ๋ฅผ ๋ฒ„๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์Šคํ† ์–ด API๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํ•œ react-redux์™€ ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ API๋ฅผ ๊นจ๋ ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ DX๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” react-redux ์™ธ๋ถ€์—์„œ ์„ ํƒ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์—์„œ Redux ์ž์ฒด๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

@timdorr ๋” ์‰ฌ์šด ๋ฆฌ๋“€์„œ/์•ก์…˜ ์Œ์œผ๋กœ ํ‘œํ˜„๋œ ์š•๊ตฌ์— +1. ๋˜ํ•œ ๋ฆฌ๋“€์„œ, ์•ก์…˜ ์ƒ์„ฑ๊ธฐ ๋ฐ ์„ ํƒ๊ธฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋” ์ฆ๊ฒ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ๊ฒฝํ—˜์ด ๋˜๋„๋ก redux ๋ชจ๋“ˆ(์ผ๋ช… ์˜ค๋ฆฌ ํŒจํ„ด)์„ ๋” ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ๋žŒ๋“ค์„ ํ–‰๋ณตํ•˜๊ฒŒ ํ•˜๋Š” ๊ทธ ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ๊ธฐ ์œ„ํ•ด์„œ๋Š” ์กฐ๊ธˆ ๋” ๋งŽ์€ ์ž์ „๊ฑฐ ์ดํƒˆ์ด ํ•„์š”ํ•˜๋ฉฐ ํ›„์† ๋ฒ„์ „์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ํ’€์–ด ์ฃผ๋‹ค.

@markerikson ์ด ๊ทธ์˜ ์ฝ”๋ฉ˜ํŠธ์—์„œ ์ž‘์„ฑํ•œ ๊ฒƒ์€ ์ด๋ฏธ ์ƒ์šฉ๊ตฌ๋ฅผ ์ƒ๋‹นํžˆ ์ค„์ด๊ณ  ํ›Œ๋ฅญํ•œ ์ดˆ๊ธฐ ์‹œํ—˜ํŒ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ํ›„, ์šฐ๋ฆฌ๋Š” ๋ฒˆ๊ฑฐ๋กœ์šด Redux์˜ ๋‹ค๋ฅธ ์ธก๋ฉด(redux/action/selector ์ƒ์„ฑ)์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ๋ฏธ๋ฆฌ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์–ด๋–ค ํ˜•ํƒœ๋กœ๋“  ๊ฐ€์‹œ์ ์ธ ์ง„์ „์„ ์ด๋ฃจ๋Š” ๋ฐ ๋ฐฉํ•ด๊ฐ€ ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ถฉ๋ถ„ํžˆ ์ค€๋น„๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋ฉด ์ €์žฅ์†Œ๋ฅผ ์‹œ์ž‘ํ•˜์—ฌ ์˜ˆ์ œ ์ฝ”๋“œ๋กœ ์ด ์„ค์ •์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์•ฝ๊ฐ„์˜ dogfood(๋˜๋Š” monorepo์˜ ๊ธฐ๋Šฅ ๋ถ„๊ธฐ์— ์žˆ๋Š” ํŒจํ‚ค์ง€๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. PR์„ ํ†ตํ•ด ํ˜‘์—…).

ํ™•์‹ ํ•˜๋Š”. ์˜ค๋Š˜์€ ์กฐ๊ธˆ ์žˆ๋‹ค๊ฐ€ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์ €์žฅ์†Œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ˜„์žฌ ๊ฒƒ ์•ˆ์— ์กด์žฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฌด์—‡์ด๋“  ๋จผ์ € ํ™๋ณดํ•˜์„ธ์š”.

์ด๊ฒƒ์€ ๋ณ„๋„์˜ ๊ฐ€์ ธ์˜ค๊ธฐ๋กœ ๊ธฐ๋ณธ redux ํŒจํ‚ค์ง€์˜ ์ผ๋ถ€์—ฌ์•ผ ํ•ฉ๋‹ˆ๊นŒ(์™„์ „ํžˆ ์—ญํ˜ธํ™˜ ๊ฐ€๋Šฅ) ์•„๋‹ˆ๋ฉด ์—ฌ๋Ÿฌ ํŒจํ‚ค์ง€๊ฐ€ ์žˆ๋Š” ๋‹จ์ผ ์ €์žฅ์†Œ๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๊นŒ? ์ „์ž๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์ด ๋„ˆ๋ฌด ์ปค์ง€๋ฉด ์ƒˆ ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ํ•ญ์ƒ ๋‹ค๋ฅธ ํŒŒ์ผ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ณ„๋„์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ/ํŒจํ‚ค์ง€๋กœ ์‹œ์ž‘ํ•˜์—ฌ ์‹ค์ œ๋กœ ๊ฐ€์ง€๊ณ  ๋†€๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋‚˜์ค‘์— ๋‹ค์‹œ ๋ณ‘ํ•ฉํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฌ์šฐ๋ฏ€๋กœ ์ด ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ๊ธฐ์กด ๋„๊ตฌ ์„ค์ •์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์‰ฌ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด React๋ฅผ ํŠน์ •ํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๊ตฌ๋ฌธ ์ง€์›์ด ํ•„์š”ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

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

๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, ๋ชจ๋…ธ๋ ˆํฌ๋กœ ๊ฐˆ ํ•„์š”๋Š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ํ•˜๋‚˜ ์ด์ƒ์˜ ๋นŒ๋“œ๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ํŠน์ • ํŒจํ‚ค์ง€ ๋นŒ๋“œ์šฉ ํด๋”๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ๋กœ์„œ๋Š” ์ž์ฒด package.json์ด ์žˆ๋Š” starter ํด๋”๋งŒ ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

redux ์ž์ฒด์— ๋กœ์ปฌ ์ฝ”๋“œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ด ํŒจํ‚ค์ง€๋ฅผ ๊ธฐ๋ณธ redux ํŒจํ‚ค์ง€์— ๋กœ์ปฌ๋กœ ์œ ์ง€ํ•˜๋Š” ์•„์ด๋””์–ด๊ฐ€ ์ข‹์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ํ‘œํ˜„๋œ ์•„์ด๋””์–ด๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€ ์ข…์†์„ฑ์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋” ์ฃผ์ €ํ•ฉ๋‹ˆ๋‹ค.

immer, thunk/promise/saga ๋ฐ reselect์™€ ๊ฐ™์€ ์ผ๋ถ€ ์™ธ๋ถ€ ์ข…์†์„ฑ์€ ์—ฌ๊ธฐ์—์„œ ์šฐ๋ฆฌ์˜ ๋ชฉํ‘œ์— ๋งค์šฐ ์ค‘์š”ํ•˜๊ณ  ์‹œ์ž‘์ž๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ ์ข…์†์„ฑ์ด ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ธฐ๋ณธ redux ํŒจํ‚ค์ง€ ์ž์ฒด์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์›ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. .

๊ทธ๋ž˜์„œ, ์–ด... ๋‚ด๊ฐ€ ๊ฐ€์„œ ํ•œ ๊ฐ€์ง€:

์ด์ „ ์Šค๋‹ˆํŽซ๊ณผ ๋‹ค๋ฅธ ์ :

  • enhancers ๊ตฌ์„ฑ ์˜ต์…˜์„ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋‚˜๋Š” ์ถ”๊ฐ€ selectorator ์˜์กด์„ฑ์„ ๋‚ด ๋ณด๋‚ธ createSelector

์ƒ๊ฐ?

์ง€๋‚œ ๋ช‡ ์ฃผ ๋™์•ˆ Redux(๋ฐ React)๋ฅผ ๋ฐฐ์šด ์‚ฌ๋žŒ์œผ๋กœ์„œ ์ตœ์ข… ์ œํ’ˆ์„ ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‚ด ์ฝ”๋“œ์—์„œ ์ •๋ฆฌํ•  ๋‚ด์šฉ์ด ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์„ ๊ฒƒ์ด๋ผ๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ˜ ๐Ÿ‘

๋”ฐ๋ผ์„œ redux ์‚ฌ์šฉ์„ ๋‹จ์ˆœํ™”ํ•˜๋Š” ๊ฒƒ์ด ๊ธด ์•„์ด๋””์–ด๋ผ๋ฉด 2๊ฐœ์˜ ์ˆœ๊ฐ„์„ ์ œ์•ˆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๋ง ๊ทธ๋Œ€๋กœ expect(mapStateToProps(state)).toEqual(mapStateToProps(state)) ์— ๋‚ด์žฅ ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์ด ๋” ๋‚˜์€ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๋„๋ก ๊ฒฉ๋ คํ•˜๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๋Ÿฐ ํ…Œ์ŠคํŠธ๋ฅผ ๋ณธ ์ ์ด ์—†์ง€๋งŒ _์ˆœ์ˆ˜ํ•˜์ง€ ์•Š์€_ mapStateToProps๋Š” redux ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

  2. memoize-state ์™€ ๊ฐ™์€ ์ฑ„ํƒ์„ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค. ์„ ํƒ์ž๋ฅผ ์œ„ํ•œ Immer.js์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์š”์  - ๊ณ ๊ฐ์€ ์„ ํ˜ธํ•˜๋Š” ํ˜•์‹์œผ๋กœ mapStateToProps ๋˜๋Š” ์„ ํƒ๊ธฐ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๋ฉ”๋ชจํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ธ์Šคํ„ด์Šค ๊ฐ„์˜ "๊ณตํ†ต" ๋ฉ”๋ชจ๋ฅผ ์žƒ์ง€ ์•Š๊ณ  "์—ฌ๋Ÿฌ ๊ตฌ์„ฑ ์š”์†Œ ์ธ์Šคํ„ด์Šค์— ๊ฑธ์ณ ์†Œํ’ˆ๊ณผ ์„ ํƒ๊ธฐ ๊ณต์œ "๋กœ ์ด ์žฌ์„ ํƒ ๋ฌธ์ œ ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด์šฉ์„ ๋‘ ๋ฒˆ ๋ฉ”๋ชจ ํ•˜์‹ญ์‹œ์˜ค.

  1. ์ˆœ์ˆ˜ ์„ ํƒ์ž๋ฅผ ์ถ”์ฒœํ•œ๋‹ค๋Š” ์ ์—์„œ ๋‹น์‹ ์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”๋ฅผ ์•Œ์ง€๋งŒ ์ˆœ์ˆ˜ํ•˜์ง€ ์•Š์€ ๋ฉฑ๋“ฑ ์„ ํƒ์ž๋Š” ์—ฌ์ „ํžˆ ๊ทธ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ์ œ์•ˆ์ด ๋” ๋งˆ์Œ์— ๋“ญ๋‹ˆ๋‹ค.
  2. ๋ฉ‹์žˆ๊ฒŒ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” immer ์šฉ ๊ฐ์†๊ธฐ๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์„ ํƒ๊ธฐ๋ฅผ ๋ž˜ํ•‘ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งž์Šต๋‹ˆ๊นŒ?

๊ทธ๋ž˜๋„ ์œ„์—์„œ ๋…ผ์˜ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ด๊ฒƒ์€ React์™€ ๋ฌด๊ด€ํ•œ ์Šคํƒ€ํ„ฐ ํŒจํ‚ค์ง€์— ์žˆ๋Š” ๊ฒƒ๋ณด๋‹ค react-redux์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ •๋ง ๋ฉ‹์ง€๋‹ค!

ํ•˜์ง€๋งŒ ํ•œ ๊ฐ€์ง€ ์š”์ฒญ/ํ”ผ๋“œ๋ฐฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. Immer๊ฐ€ ์•”์‹œ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

Immer๋Š” ํ›Œ๋ฅญ ํ•ฉ๋‹ˆ๋‹ค. ์ œ๊ฐ€ ๊ฐ€์žฅ ์ข‹์•„ํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ๋•Œ๋„( produce(baseState, draftState => ... ), ๊ทธ๊ฒƒ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ๋‚ด ์ฝ”๋“œ๋ฅผ ๋ณผ ๋•Œ ๊ฑฑ์ •์ด ๋˜๊ณ , ๋‚ด๊ฐ€ Immer ์ดˆ๋Šฅ๋ ฅ ๋•Œ๋ฌธ์— ์ด ๋ณ€๊ฒฝ ์ž‘์—…๋งŒ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

API๊ฐ€ ์ง€๊ธˆ ๋ณด์ด๋Š” ๋ฐฉ์‹์œผ๋กœ ์ƒˆ ์‚ฌ์šฉ์ž๊ฐ€ redux ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์„ ์ƒ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๋“ค์ด ์ด ์Šคํƒ€ํ„ฐ ํ‚คํŠธ ์—†์ด Redux๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์˜คํ•ด๊ฐ€ ๊ทธ๋“ค์„ ์‹ฌํ•˜๊ฒŒ ๋ฌผ๋ฆด ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

createNextState ๋Š” ์ด๋ฏธ ๋‚ด๋ณด๋‚ด๊ธฐ์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

import {createReducer, createNextState} from "@acemarke/redux-starter-kit";

function addTodo(state, newTodo) {
    return createNextState(state, draftState => {
        draftState.push({...newTodo, completed : false});
    });
}

( createNextState ์šฉ๋„๋ฅผ ์ž˜๋ชป ์ดํ•ดํ–ˆ๋‹ค๋ฉด ์‚ฌ๊ณผ๋“œ๋ฆฝ๋‹ˆ๋‹ค! ์ „ํ˜€ ํŒŒํ—ค์น˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค)

https://github.com/markerikson/redux-starter-kit/issues/5๋ฅผ ์ฐธ์กฐ

API๊ฐ€ ์ง€๊ธˆ ๋ณด์ด๋Š” ๋ฐฉ์‹์œผ๋กœ ์ƒˆ ์‚ฌ์šฉ์ž๊ฐ€ redux ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์„ ์ƒ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๋“ค์ด ์ด ์Šคํƒ€ํ„ฐ ํ‚คํŠธ ์—†์ด Redux๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์˜คํ•ด๊ฐ€ ๊ทธ๋“ค์„ ์‹ฌํ•˜๊ฒŒ ๋ฌผ๋ฆด ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ œ ์ƒ๊ฐ์—๋Š” https://github.com/reactjs/redux/issues/2858 ๋•Œ๋ฌธ์— ์ด ํŒจํ‚ค์ง€๊ฐ€ ์™„์„ฑ๋˜์—ˆ์„ ๋•Œ Redux๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค

createNextState๋Š” ์ด๋ฏธ ๋‚ด๋ณด๋‚ด๊ธฐ์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋‚˜์œ ์ƒ๊ฐ์€ ์•„๋‹ˆ์ง€๋งŒ, ํŠนํžˆ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  immer ์ถ”๊ฐ€๊ฐ€ ์—ญํ˜ธํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์—์„œ ์•”์‹œ์ ์ธ ๊ฒƒ์„ ๋” ์„ ํ˜ธํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

libs immer.js libs ์™€ ๊ฐ™์ด store ์š”๊ตฌ ์‚ฌํ•ญ์„ ์กด์ค‘ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ๋ชจ๋‘ ์ €์žฅ์†Œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ newState ์ธ์Šคํ„ด์Šค๋ฅผ previousState ๋Œ€ํ•ด ( === ) ํ™•์ธํ•˜๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ›จ์”ฌ ๋” ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค์ธ ๊ฒฝ์šฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ์‹์œผ๋กœ ๋ชจ๋“  ์‚ฌ๋žŒ์ด store ์˜ ๊ทœ์น™๊ณผ ์š”๊ตฌ ์‚ฌํ•ญ์„ ๋ฐฐ์šฐ๋„๋ก ๊ฐ•์ œ๋ฉ๋‹ˆ๋‹ค.

์ถ”์‹ : ์ €๋Š” immer ์˜ ์ด์ ์„ ์ง„์ •์œผ๋กœ ์ดํ•ดํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์ „ํ˜€ ๋ฐ˜๋Œ€ํ•  ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๋Š” ์‚ฌ๋žŒ๋“ค์ด ๋งˆ์นจ๋‚ด ๊ฐœ๋…์„ ์ดํ•ดํ•˜๋Š” ์‹œ์ ์— ๋„๋‹ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํ˜ผ๋ž€์˜ ๊ฑฐ๋Œ€ํ•œ ๊ทผ์›์„ ์ƒ์„ฑํ•˜๊ณ  ๊ธฐ์ˆ ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฒค์น˜๋งˆํ‚นํ•˜๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋Š์Šจํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋ณต์žก์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐœ์ธ์ ์ธ ์ œ์•ˆ:

logger , devTools ๋ฐ Thunk ๋Š” Starter ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•˜๋ฉฐ ๋‚˜๋จธ์ง€๋Š” ๊ฐœ์ธ์ ์ธ ์„ ํƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ ๋ชจ๋‘๋Š” ์ €์žฅ์†Œ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ newState ์ธ์Šคํ„ด์Šค๋ฅผ previousState ( === )์— ๋Œ€ํ•ด ํ™•์ธํ•˜๊ณ  ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค์ธ ๊ฒฝ์šฐ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ›จ์”ฌ ๋” ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.

๋™์ž‘์— ๋”ฐ๋ผ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ๋™์ผํ•œ ์ €์žฅ์†Œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ˆ์•ฝํ•˜๊ณ  ์ž˜๋ชป๋œ ๊ธ์ • ์ฐจ์ด๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋”๋ผ๋„ Redux๋Š” ํ•ต์‹ฌ ๊ฒ€์ฆ์ž๋ฅผ ์›ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉฐ Immer๋Š” ๊ฐœ์ธ์ ์œผ๋กœ ์ด์— ๋Œ€ํ•œ ๊ดœ์ฐฎ์€ ๋ณดํ˜ธ ์žฅ์น˜๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด Redux๋ฅผ ๋ฐฐ์šฐ๋Š” ๋ชฉ์ ์„ ์–ด๋Š ์ •๋„ ๋ฌด๋„ˆ๋œจ๋ฆฌ๋Š” ๊ฒƒ์— ๋™์˜ํ•˜์ง€๋งŒ ์ˆœ์ˆ˜ํ•œ FP๋ฅผ ๋ฐฐ์šฐ๊ณ  ์‹ถ์ง€ ์•Š๊ฑฐ๋‚˜ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋Š” Redux๋ฅผ ๋ฐฐ์šฐ๋Š” ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋ฅผ ๋ด…๋‹ˆ๋‹ค.

๋ฒค์น˜๋งˆํ‚นํ•˜๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋Š์Šจํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋ณต์žก์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์•„๋Š” ํ•œ, Immer๊ฐ€ Proxy๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์€ ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋งŒ ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ผ๋ฐ˜์ ์ธ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ์—…๋ฐ์ดํŠธ ๋ฆฌ๋“€์„œ์—๋Š” ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ •๋„๋ฉด ๊ดœ์ฐฎ์€ ๋ฐธ๋Ÿฐ์Šค๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋กœ๊ฑฐ, devTools ๋ฐ Thunk๋Š” Starter์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ถ”๊ฐ€๋˜์–ด์•ผ ํ•˜๋ฉฐ ๋‚˜๋จธ์ง€๋Š” ๊ฐœ์ธ์ ์ธ ์„ ํƒ์— ๋” ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

์ œ๊ฑฐ๋ฅผ ๊ณ ๋ คํ•˜๊ณ  ์‹ถ์€ ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค(๊ฐœ์ธ์ ์œผ๋กœ ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๋ณด์™„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์— ๋น„ํ•ด ๋กœ๊ฑฐ์˜ ํฐ ์ด์ ์„ ๋ณด์ง€๋Š” ๋ชปํ•จ), https://github.com/markerikson/ ์ฐธ์กฐ redux-starter-kit/issues/19. ๊ทธ๋Ÿฌ๋‚˜ Immer ๋ฐ Thunk์˜ ํ˜„์žฌ ์‚ฌ์šฉ๊ณผ ๊ฐ™์ด ๋‹ค์‹œ ํ˜ธํ™˜๋˜๋Š” ๊ฒฝ์šฐ ๋” ๋งŽ์€ ๊ฒƒ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์— ๋ฐ˜๋Œ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋™์ž‘์— ๋”ฐ๋ผ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ๋™์ผํ•œ ์ €์žฅ์†Œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ˆ์•ฝํ•˜๊ณ  ์ž˜๋ชป๋œ ๊ธ์ • ์ฐจ์ด๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋”๋ผ๋„ Redux๋Š” ํ•ต์‹ฌ ๊ฒ€์ฆ์ž๋ฅผ ์›ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉฐ Immer๋Š” ๊ฐœ์ธ์ ์œผ๋กœ ์ด์— ๋Œ€ํ•œ ๊ดœ์ฐฎ์€ ๋ณดํ˜ธ ์žฅ์น˜๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด Redux๋ฅผ ๋ฐฐ์šฐ๋Š” ๋ชฉ์ ์„ ์–ด๋Š ์ •๋„ ๋ฌด๋„ˆ๋œจ๋ฆฌ๋Š” ๊ฒƒ์— ๋™์˜ํ•˜์ง€๋งŒ ์ˆœ์ˆ˜ํ•œ FP๋ฅผ ๋ฐฐ์šฐ๊ณ  ์‹ถ์ง€ ์•Š๊ฑฐ๋‚˜ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋Š” Redux๋ฅผ ๋ฐฐ์šฐ๋Š” ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋ฅผ ๋ด…๋‹ˆ๋‹ค.

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

๋‚ด๊ฐ€ ์•„๋Š” ํ•œ, Immer๊ฐ€ Proxy๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์€ ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋งŒ ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ผ๋ฐ˜์ ์ธ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ์—…๋ฐ์ดํŠธ ๋ฆฌ๋“€์„œ์—๋Š” ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ •๋„๋ฉด ๊ดœ์ฐฎ์€ ๋ฐธ๋Ÿฐ์Šค๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋ณต์žก์„ฑ ๊ณ„์ธต์„ ์ถ”๊ฐ€ํ•œ๋‹ค๋Š” ๊ฒƒ์€ Immer ๊ฐ€ ํ•œ ๋ฒˆ๋„ ์—†์—ˆ๊ณ  ์ด์ œ ๊ทธ๋Ÿด ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ด๋ฅผ ์ง€์›ํ•˜๊ณ , ํ…Œ์ŠคํŠธํ•˜๊ณ , ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ‰๊ฐ€ํ•˜๊ณ , ๋งˆ์ง€๋ง‰์œผ๋กœ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์—ฌ๊ธฐ ์ด ์„น์…˜์€ ์ €๋ฅผ ๋‘๋ ต๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Immer.js - ์ž๋™ ๋™๊ฒฐ
Immer๋Š” ๋†์‚ฐ๋ฌผ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ •๋œ ๋ชจ๋“  ์ƒํƒœ ํŠธ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๋™๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒ์‚ฐ์ž ์™ธ๋ถ€์—์„œ ์ƒํƒœ ํŠธ๋ฆฌ๊ฐ€ ์šฐ๋ฐœ์ ์œผ๋กœ ์ˆ˜์ •๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋ฏ€๋กœ ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์ด ์˜ต์…˜์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กœ์ปฌ ๊ฐœ๋ฐœ ์ค‘์—๋Š” ์ผœ์ ธ ์žˆ๊ณ  ํ”„๋กœ๋•์…˜์—์„œ๋Š” ๊บผ์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ผœ๊ฑฐ๋‚˜ ๋„๋ ค๋ฉด setAutoFreeze(true / false)๋ฅผ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

๊ทธ๋ฆฌ๊ณ  ๊ฐ‘์ž๊ธฐ Development / Prod ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ๋„ ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋‹จ์ง€ ์ข‹์€ ์–‘์˜ ์ž‘์—…์ด๊ณ  ๋งŽ์€ ๊ฒƒ์„ ๊ตํ™˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๊ฑฑ์ •์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ๋‹จ์ง€ ์ œ ๊ฐœ์ธ์ ์ธ ์˜๊ฒฌ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

@codedavinci : ์ด redux-starter-kit ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์š”์ ์€ "๊ธฐ๋ณธ" Redux ์ €์žฅ์†Œ ์„ค์ • ํ”„๋กœ์„ธ์Šค ์œ„์— ์œ ์šฉํ•œ ๋„๊ตฌ์™€ ์ถ”์ƒํ™”๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด๊ฒƒ์€ Redux ์ฝ”์–ด ์ž์ฒด๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์ด _์•„๋‹™๋‹ˆ๋‹ค_. ์˜คํžˆ๋ ค ์šฐ๋ฆฌ๊ฐ€ ๊ฒฐ๊ตญ Redux ์กฐ์ง์˜ "๊ณต์‹์ ์ธ" ์ผ๋ถ€๋ฅผ ๋งŒ๋“ค๊ณ ์ž ํ•˜๋Š” ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ธฐ์กด์˜ ๋ชจ๋“  Redux ์ฝ”๋“œ๊ฐ€ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ชจ๋“  ํŠœํ† ๋ฆฌ์–ผ์€ ์—ฌ์ „ํžˆ โ€‹โ€‹๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. Redux๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๊ณต์‹์ ์œผ๋กœ ์ง€์›๋˜๋Š” ๋” ๊ฐ„๋‹จํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ถ”๊ฐ€ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

@markerikson ๋‚˜๋Š” ์Šคํƒ€ํ„ฐ ํ‚คํŠธ๋ฅผ ๊ฐ–๋Š” ๋ชฉํ‘œ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•ด ์™„์ „ํžˆ ์‹ค๋งํ–ˆ์Šต๋‹ˆ๋‹ค. Immer.js ์™€ ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฐœ๋…์„ ๋ฐ”๊พธ๋Š” ๊ฒƒ์— ์•ฝ๊ฐ„ ์ €ํ•ญํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ๋žŒ๋“ค์€ ๊ฒฐ๊ตญ ๋ฐฐ์šธ ๊ฒƒ์ž…๋‹ˆ๋‹ค starter-kit ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๊ณ  ํ•ต์‹ฌ ๊ฐœ๋…์„ ์™„์ „ํžˆ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค. ์ƒ์  ๋Œ์—ฐ๋ณ€์ด๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ด ๋ชจํ† ์ž…๋‹ˆ๋‹ค. Redux๋Š” ํ•ญ์ƒ ๊ทธ๊ฒƒ์„ ํŒ๋งคํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋ธŒ๋žœ๋“œ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.

์ถ”์‹ : Immer.js ๊ฐ€ ์•„๋ฌด ๊ฒƒ๋„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ์˜คํžˆ๋ ค ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ์ €๋Š” push , splice ์™€ ๊ฐ™์ด ํ—ˆ์šฉ๋˜๋Š” ๊ตฌ๋ฌธ ํ‘œ์ค€์— ๋Œ€ํ•ด์„œ๋งŒ ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ...

์•„๋งˆ๋„, ๊ทธ๊ฒƒ์€ ๋‚˜ ์ž์‹ ์ด ๋ณ€ํ™”์— ์ €ํ•ญํ•˜๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค :)

@codedavinci : ์˜ˆ, ์šฐ๋ ค์˜ ์š”์ ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ Redux์— ๋Œ€ํ•œ ์ผ๋ฐ˜์ ์ธ ๋ถˆ๋งŒ ์ค‘ ํ•˜๋‚˜๋Š” ์ ์ ˆํ•œ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ์—…๋ฐ์ดํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ๊ณ ํ†ต์ด๋ฉฐ Immer๋Š” ์ด๋Ÿฌํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ๋‹จ์ˆœํ™”ํ•˜๋Š” ๋ฐ ์žˆ์–ด ๋‚ด๊ฐ€ ๋ณธ ์ตœ๊ณ ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

@markerikson , ์‹ค์ œ๋กœ immer ํŒŒ๊ณ  ๋“ค์—ˆ๊ณ  ๋งค์šฐ ๊ฐ€๋ณ๊ณ  ๊ฐ„๋‹จํ•˜๋ฉฐ ํšจ์œจ์ ์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ํŒ”๋ ธ์Šต๋‹ˆ๋‹ค :) .

์–ด๋–ค ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์ €๊ฒƒ๋“ค:

https://github.com/markerikson/redux-starter-kit
https://www.npmjs.com/package/@acemarke/redux -starter-kit
https://unpkg.com/@acemarke/redux [email protected]/

๋„ค, ๋ฐ”๋กœ ๊ทธ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@nickmccurdy ์—๋Š” ๋Œ€๋ถ€๋ถ„ ํˆด๋ง๊ณผ ๊ด€๋ จํ•˜์—ฌ ๋…ผ์˜ ๋ฐ ํ•ฉ๋ณ‘์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋งŽ์€ PR์ด ์žˆ์ง€๋งŒ ํ”„๋กœ์ ํŠธ๋ฅผ ์žฅ๊ธฐ์ ์œผ๋กœ ์ง‘์ค‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋…ผ์˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ๋“ค์„ ์‚ดํŽด๋ณด๊ธฐ ์œ„ํ•ด ๋Œ์•„๋‹ค๋…€์•ผ ํ•˜์ง€๋งŒ ์ง€๋‚œ ๋ช‡ ์ฃผ ๋™์•ˆ ๋‹ค๋ฅธ ์ผ๋“ค๋กœ ๋ฐ”๋นด๋‹ค(๋‚ด์ผ ๋‚ด๊ฐ€ ํ•  ์ปจํผ๋Ÿฐ์Šค ๊ฐ•์—ฐ์„ ํฌํ•จํ•˜์—ฌ).

@nickmccurdy & @markerikson์˜ ํฐ ๋ฐœ์ „ ๐Ÿ‘ ๋‚˜๋Š” ๊ทธ๊ฒƒ์˜ ๋‹จ์ˆœํ•จ๊ณผ ํšจ์œจ์„ฑ์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค. @nickmccurdy ๋Š” ๋‹น์‹ ์ด ํ”„๋กœ์ ํŠธ์— ๋งค์šฐ ์ ๊ทน์  ๊ทธ๊ฒƒ์„ ์›ํ•˜๋Š”์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”! :)

๋ฌผ๋ก  ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ ๋ฌธ์ œ ์—์„œ ๊ณต๊ฐœ ํ† ๋ก ์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์ง€๋งŒ [email protected] ๋˜๋Š” Discord๋ฅผ ํ†ตํ•ด ์ €์—๊ฒŒ ์ง์ ‘ ์—ฐ๋ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

combineReducers ๋ถ„ํ• ์— ๋Œ€ํ•œ +1 . ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๊ฐ€์žฅ ์˜ค๋žซ๋™์•ˆ ์‚ฌ์šฉํ–ˆ๊ณ , ๋งŒ์•ฝ ๊ทธ๋ ‡๋‹ค๋ฉด ๋‚ด ๊ฐ€๊ฒŒ๊ฐ€ ์–ด๋–ป๊ฒŒ ์šด์˜๋˜๋Š”์ง€์— ๋Œ€ํ•ด ๊นŠ์ด ์ƒ๊ฐํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ˜„์žฌ ๋‹ค์Œ ๋ผ์ธ์„ ๋”ฐ๋ผ ๊ฐ์†๊ธฐ ๊ธฐ๋Šฅ์„ ์„ค์ •ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

function reducer(prevState, event, handledNavigation = false) {
  if (event.type === 'NAVIGATION' && !handledNavigation) {
    return reducer(onNavigate(prevState, event), event, true);
  }

  const pageReducer = pageReducers[prevState.activePage];

  const nextSessionState = sessionReducer(prevState.sessionState, event);
  const nextPageState = pageReducer(prevState.pageState, nextSessionState, event);

  return {
    activePage: prevState.activePage,
    pageState: nextPageState,
    sessionState: nextSessionState,
  };
}

CombineReducers๊ฐ€ ์‹ค์ œ๋กœ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@abradley2 : ๊ทธ๊ฒƒ์ด ๋‹น์‹ ์ด ์š”๊ตฌํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด Redux ์ฝ”์–ด์—์„œ combineReducers ๋ฅผ ์ œ๊ฑฐํ•  ๊ณ„ํš์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ˆ, ์šฐ๋ฆฌ๋Š” ํ•ญ์ƒ combineReducers ๊ฐ€ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ์ด๋ฉฐ ์ž์‹ ์˜ ์ƒํ™ฉ์— ๋งž๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ๊ฐ์†๊ธฐ ๋…ผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

redux-starter-kit ํŒจํ‚ค์ง€ ์ด๋ฆ„์ด ์žˆ๊ณ  ์ €์žฅ์†Œ๊ฐ€ https://github.com/reduxjs/redux-starter-kit ์˜ ์กฐ์ง์œผ๋กœ ์ด๋™๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์Šค๋ ˆ๋“œ๋ฅผ ๋‹ซ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์—์„œ ์ถ”๊ฐ€ ํ† ๋ก ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋Œ์—ฐ๋ณ€์ด ๋ฐ ์ง๋ ฌํ™” ๊ฒ€์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰