๐Ÿซ† Redux์™€ Recoil์˜ ์ฐจ์ด

์ง€์ฝ”ยท2025๋…„ 10์›” 8์ผ

FE STUDY

๋ชฉ๋ก ๋ณด๊ธฐ
7/8
post-thumbnail

์ตœ๊ทผ์— ๋ฉด์ ‘์„ ๋ช‡ ๊ตฐ๋ฐ ๋ดค๋Š”๋ฐ, ์งˆ๋ฌธ์„ ๋ฐ›์•˜์ง€๋งŒ ์ œ๋Œ€๋กœ ๋‹ต๋ณ€ํ•˜์ง€ ๋ชปํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ผญ ์งš๊ณ  ๋„˜์–ด๊ฐ€์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐํ•œ ๊ฒƒ ์ค‘ ํ•˜๋‚˜๊ฐ€ "Redux์™€ Recoil์˜ ์ฐจ์ด"์ด๋‹ค. ์˜ค๋Š˜์€ ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์ •๋ฆฌํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

Redux์™€ Recoil์€ ๋ฌด์—‡์„ ์œ„ํ•œ ๊ฒƒ์ธ๊ฐ€

๋จผ์ € Redux์™€ Recoil ๋ชจ๋‘ ๋ฆฌ์•กํŠธ์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๐Ÿค” ๊ทธ๋Ÿผ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” ์™œ ํ•„์š”ํ•œ ๊ฒƒ์ผ๊นŒ?

๋ฆฌ์•กํŠธ๋Š” ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ”์ธ๋”ฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๋‹จ๋ฐฉํ–ฅ ๋ฐ”์ธ๋”ฉ์ด๋ผ ํ•จ์€, ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์—์„œ state๋ฅผ ์ „๋‹ฌํ•  ๋•Œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ state๋ฅผ props๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
๋”ฐ๋ผ์„œ ์ด์™€ ๋ฐ˜๋Œ€๋กœ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ state๋ฅผ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

  1. ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ state๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋ฅผ props๋กœ ๋„˜๊ฒจ์ค€๋‹ค.
  2. ๋ฆฌ์•กํŠธ์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋Š” Context API๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  3. Redux, Recoil๊ณผ ๊ฐ™์€ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

๊ฐ„๋‹จํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋ผ๋ฉด 1๋ฒˆ ๋ฐฉ์‹์„ ์ด์šฉํ•ด์„œ ์›ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ state๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์กฐ๊ธˆ๋งŒ ๋ณต์žกํ•ด์ ธ๋„(์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด depth๊ฐ€ ์ปค์งˆ์ˆ˜๋ก) 1๋ฒˆ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ๊ฐ€ ๋ฌด์ฒ™ ์–ด๋ ค์›Œ์ง„๋‹ค. ๋”ฐ๋ผ์„œ 3๋ฒˆ๊ณผ ๊ฐ™์ด ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์ฃผ๋ฅผ ์ด๋ฃจ๊ฒŒ ๋˜์—ˆ๋‹ค.


Redux

Redux๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ๋Š” ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ€๊ธฐ๊นŒ์ง€ ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋“ค์— props๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ์ง€๋งŒ, Redux๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Store๋ผ๋Š” ์ค‘์•™ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•ด ์ƒํƒœ๋ฅผ ์‰ฝ๊ฒŒ ์ €์žฅํ•˜๊ณ  ๊บผ๋‚ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

์ด์ฒ˜๋Ÿผ Redux๋Š” ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์— ์‚ฌ์šฉ๋˜๋Š” state๋“ค์„ ๋ถ„๋ฆฌ, ํ†ตํ•ฉํ•ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

๐Ÿงฟ MVC ํŒจํ„ด


Redux๊ฐ€ ๋“ฑ์žฅํ•˜๊ธฐ ์ „, ํ”„๋ก ํŠธ์—”๋“œ ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์€ MVC ํŒจํ„ด ์ด์—ˆ๋‹ค.

  • Model - ๋ฐ์ดํ„ฐ์˜ ํ˜•์‹์ด๋‚˜ ๊ตฌ์กฐ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ๋ชจ๋ธ์— ๋งž์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋Š” ํ๋ฆ„์„ ์ œ์–ด ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  • View - ์ฝ”๋“œ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ๋ถ€๋ถ„์„ ๋‹ด๋‹นํ•œ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ๋ชจ์Šต๊ณผ ํ˜•ํƒœ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.
  • Controller - ๋ณ€ํ™”ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. View์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐ์ดํ„ฐ๋‚˜ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ Model๊ณผ View์— ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

MVC ํŒจํ„ด ์€ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ชจ๋ธ์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๋ทฐ๋„ ๋ณ€๊ฒฝ๋˜๊ณ , ์‚ฌ์šฉ์ž์— ์˜ํ•ด ๋ทฐ์—์„œ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚œ๋‹ค๋ฉด ๋ชจ๋ธ ๋˜ํ•œ ๋ณ€๊ฒฝ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์€ ์„ค๊ณ„ํ•˜๊ธฐ๋„ ๊ฐ„๋‹จํ•˜๊ณ , ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ๋„ ์‰ฝ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง„๋‹ค๋ฉด, ํ•œ ๊ฐœ์˜ ๋ชจ๋ธ์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ทฐ๋ฅผ ์กฐ์ž‘ํ•˜๊ณ , ํ•œ ๊ฐœ์˜ ๋ทฐ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ชจ๋ธ์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

๐Ÿงฟ Flux ํŒจํ„ด

2014๋…„ ํŽ˜์ด์Šค๋ถ์—์„œ MVC ํŒจํ„ด์˜ ๋Œ€์•ˆ์œผ๋กœ Flux ๋ผ๋Š” ์ƒˆ๋กœ์šด ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด์„ ๊ฐœ๋ฐœํ–ˆ๋‹ค.

Flux ํŒจํ„ด ์€ MVC ํŒจํ„ด์—์„œ ๊ฒช์€ ๋ณต์žกํ•œ ์ƒํ™ฉ์„ ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉ์ ์œผ๋กœ ๊ฐœ๋ฐœ๋˜์—ˆ๊ณ , ๊ทธ ๋ฐฉ๋ฒ•์œผ๋กœ "๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„"์„ ์ ์šฉํ•˜์˜€๋‹ค.

  • View์—์„œ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ, ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๊ณ  Action์„ ๋„˜๊ฒจ์ฃผ๊ณ 
  • Action์€ ๋ฐ˜๋“œ์‹œ Dispatcher๋ฅผ ์ง€๋‚˜๊ฒŒ ๋˜๋ฉฐ
  • Dispatcher๋ฅผ ํ†ตํ•ด ์ง์ ‘์ ์ธ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚˜ Store์— ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ ์šฉ๋˜๋ฉด
  • View๋Š” ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋ฅผ Store๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ ๋ฐ›๋Š”๋‹ค.

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

๊ทธ๋ฆฌ๊ณ  2015๋…„์— Dan Abramov์— ์˜ํ•ด React + Flux ๊ตฌ์กฐ์— Reducer๋ฅผ ๊ฒฐํ•ฉํ•œ Redux๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค.

๐ŸŽฏ Redux์˜ 3์›์น™

1๏ธโƒฃ ์ง„์‹ค์€ ํ•˜๋‚˜์˜ ์†Œ์Šค๋กœ๋ถ€ํ„ฐ
์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ์ƒํƒœ๋Š” ํ•˜๋‚˜์˜ Store ์•ˆ์— ํ•˜๋‚˜์˜ ๊ฐ์ฒด ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ์ €์žฅ๋œ๋‹ค.

๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์€ Reducer๋ฅผ ํ†ตํ•ด ์ผ์–ด๋‚˜๋ฉฐ, ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๋Š” Store์— ์ €์žฅ๋œ๋‹ค. ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ Store์— ๊ธฐ๋ก๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ํ๋ฆ„์˜ ์›์ฒœ์€ ํ•ญ์ƒ Store์—ฌ์•ผ ํ•œ๋‹ค.

์ƒํ™ฉ๊ณผ ํ•„์š”์— ๋”ฐ๋ผ Store๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ, ๋„ˆ๋ฌด ๋งŽ์„ ๊ฒฝ์šฐ ๊ธฐ์กด MVC ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์žˆ๋Š”์ง€ ์ƒ๊ฐํ•ด๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค. Store๋Š” ๋ณดํ†ต 1๊ฐœ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

2๏ธโƒฃ State๋Š” ์ฝ๊ธฐ ์ „์šฉ์ด๋‹ค
์ƒํƒœ๋ฅผ ๋ณ€ํ™”์‹œํ‚ค๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ Action ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ• ๋ฟ์ด๋‹ค.

View์—์„œ ์ผ์–ด๋‚˜๋Š” ์ด๋ฒคํŠธ๋Š” ์ง์ ‘ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ๋Š” ์•ˆ๋˜๋ฉฐ, ์ด๋ฒคํŠธ๋Š” ๋‹จ์ง€ Action ๊ฐ์ฒด๋ฅผ Reducer๋กœ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ๋งŒ ํ•œ๋‹ค. ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์€ Reducer๋งŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. Reducer ์ด์™ธ์˜ ๊ณต๊ฐ„์—์„œ ์ƒํƒœ๋Š” ์ฝ๊ธฐ ๋ชจ๋“œ์ด๋‹ค.

3๏ธโƒฃ ๋ณ€ํ™”๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ๋˜์–ด์•ผ ํ•œ๋‹ค
Action ๊ฐ์ฒด์— ์˜ํ•ด ์ƒํƒœ ํŠธ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™”ํ•˜๋Š”์ง€๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์ˆœ์ˆ˜ ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

์ˆœ์ˆ˜ Reducer ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ณณ์—์„œ ์™ธ๋ถ€์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ๋™์ผํ•œ ์ž…๋ ฅ๊ฐ’์—๋Š” ๋™์ผํ•œ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜์˜ ๊ธฐ๋ณธ ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด์„œ๋„, ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ํŠน์ง•์„ ์ถ”๊ฐ€๋กœ ๊ฐ–๋Š”๋‹ค.

  • Reducer๋Š” ๋ฐ˜๋“œ์‹œ ์ด์ „ ์ƒํƒœ์™€ Action์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.
  • Reducer๋Š” ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ ์ด์ „์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๐Ÿ›’ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์˜ˆ์‹œ

Action

Action์€ ๋ณดํ†ต type๊ณผ payload ์†์„ฑ์„ ๊ฐ€์ง€๋ฉฐ, '๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚ฌ๋Š”์ง€'๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค.

{
  type: 'cart/addItem',
  payload: { id: 1, name: 'T-Shirt' }
}  

Redux Toolkit์—์„œ๋Š” createSlice ํ•จ์ˆ˜๊ฐ€ action creator๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ์ง์ ‘ Action ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

Reducer

๋จผ์ € Reducer๋Š” Store์™€ Action ๊ฐ์ฒด๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ ์ƒˆ๋กœ์šด ์ƒํƒœ ๊ฐ’์„ ์ถœ๋ ฅํ•˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋‹ค. Reducer๋Š” ๋ฐ˜๋“œ์‹œ ์ˆœ์ˆ˜ ํ•จ์ˆ˜์—ฌ์•ผ ํ•˜๋ฉฐ, ์™ธ๋ถ€ ์ƒํƒœ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋ฉด ์•ˆ๋œ๋‹ค. Action ๊ฐ์ฒด๋Š” ๋ฐ˜๋“œ์‹œ type ์†์„ฑ ๊ฐ’์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.

2025๋…„ ๊ธฐ์ค€ Redux Toolkit์—์„œ๋Š” createSlice ๋กœ Reducer๋ฅผ ์ •์˜ํ•œ๋‹ค. Redux Toolkit์€ ๋‚ด๋ถ€์ ์œผ๋กœ Immer๋ฅผ ์‚ฌ์šฉํ•ด ๋ถˆ๋ณ€์„ฑ์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, state.push() ์ฒ˜๋Ÿผ ์ง์ ‘ state๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ฝ”๋“œ๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค.

// stor/cartSlice.js
import { createSlice } from "@reduxjs/toolkit";

const cartSlice = createSlice(
  name: 'cart',
  initialState: [],
  reducers: {
    addItem: (state, action) => {
      state.push(action.payload);
    },
    deleteItem: (state, action) => {
      return state.filter(item => item.id !== action.payload.id);
    },
  },
);

export const { addItem, deleteItem } = cartSlice.actions;
export default cartSlice.reducer;

Store

Store๋Š” ๋ชจ๋“  state๋“ค์„ ๋‹ด๊ณ  ์žˆ๋Š” ์ค‘์•™ ์ €์žฅ์†Œ์ด๋‹ค. ๋ฆฌ์•กํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์— ์ƒํƒœ๋ฅผ ๊ณต๊ธ‰ํ•˜๊ธฐ ์œ„ํ•ด Provider ๋กœ ๊ฐ์‹ผ๋‹ค.

// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import cartReducer from './cartSlice';

const store = configureStore({
  reducer: {
    cart: cartReducer,
  },
});

export default store;
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

useDispatch()

useDispatch ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ Store์— Action์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ํ›…์ด๋‹ค. Redux Store ์ „์ฒด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

import { useDispatch } from 'react-redux';
import { addItem } from '../store/cartSlice';

function Product({ product }) {
  const dispatch = useDispatch();
  
  const handleAdd = () => {
    dispatch(addItem(product));
  };
  
  return <button onClick={handleAdd}>์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‹ด๊ธฐ</button>;
}

useSelector()

useSelector ๋Š” Store์˜ ์ƒํƒœ ์ค‘ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์„ ํƒํ•ด์„œ ๊ฐ€์ ธ์˜ค๋Š” ํ›…์ด๋‹ค.

import { useSelector } from 'react-redux';

function CartList() {
  const items = useSelector((state) => state.cart);
  
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
  	  ))}
    </ul>
  );
}

๐Ÿ” ์ „๋ฐ˜์ ์ธ ํ๋ฆ„

  1. ์ปดํฌ๋„ŒํŠธ์—์„œ dispatch(action)๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
  2. Reducer๊ฐ€ ํ˜„์žฌ state์™€ action์„ ๋ฐ›์•„ ์ƒˆ๋กœ์šด state๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.
  3. Store๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ state๋ฅผ ๊ตฌ๋… ์ค‘์ธ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•œ๋‹ค.
  4. useSelector ๋ฅผ ์‚ฌ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณ€๊ฒฝ๋œ ๊ฐ’์œผ๋กœ ์ž๋™ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค.

์ฆ‰, ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„์€ "๋‹จ๋ฐฉํ–ฅ"์ด๋ฉฐ, ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๋‹ค.
Component -> Action -> Reducer -> Store -> Component


Redux์˜ ํŠน์ง• ์ •๋ฆฌํ•˜๊ธฐ

  • ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฏ€๋กœ, ์–ด๋А ์ปดํฌ๋„ŒํŠธ์— ์ƒํƒœ๋ฅผ ๋‘์–ด์•ผ ํ• ์ง€ ๊ณ ๋ฏผํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
  • ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ฐ€์ง„๋‹ค.
  • ์ƒํƒœ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค.
  • Flux ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋”ฐ๋ฅธ๋‹ค. โžก๏ธ redux-thunk๋‚˜ redux-saga์™€ ๊ฐ™์€ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ํ•„์ˆ˜์ด๋‹ค.
  • ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฌ๋‹์ปค๋ธŒ๊ฐ€ ๋†’์€ ํŽธ์ด๋‹ค.
  • ์ดˆ๊ธฐ ์„ธํŒ…์ด ์š”๊ตฌ๋˜๋ฉฐ, ๊ฐ„๋‹จํ•œ ์ƒํƒœ ํ•˜๋‚˜๋ฅผ ๊ด€๋ฆฌํ•˜๋”๋ผ๋„ ๋งŽ์€ ์ฝ”๋“œ๋Ÿ‰์ด ํ•„์š”ํ•˜๋‹ค.
  • React ์ „์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋‹ค.

Recoil

Recoil์€ React์˜ context API๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋œ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ, ํŽ˜์ด์Šค๋ถ์—์„œ ๋งŒ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

๋ฆฌ์•กํŠธ์˜ ํ˜ธํ™˜์„ฑ์ด๋‚˜ ๋‹จ์ˆœํ•จ์„ ์ƒ๊ฐํ•œ๋‹ค๋ฉด ๋ฆฌ์•กํŠธ์— ๋‚ด์žฅ๋œ hooks๋‚˜ context API๋ฅผ ์‚ฌ์šฉํ•ด ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ๋ฐ”๋žŒ์งํ•˜์ง€๋งŒ, ์ด ๊ฒฝ์šฐ๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ํ•œ๊ณ„๊ฐ€ ์กด์žฌํ•œ๋‹ค.

๐Ÿ”Ž hooks๋‚˜ context API๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์˜ ๋ฌธ์ œ์ 

  1. ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ๋ฅผ ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด ๊ณผ์ •์—์„œ ๊ฑฐ๋Œ€ํ•œ ํŠธ๋ฆฌ์˜ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  2. Context๋Š” ๋‹จ์ผ ๊ฐ’๋งŒ ์ €์žฅ ๊ฐ€๋Šฅํ•˜๊ณ , ์ž์ฒด Consumer๋ฅผ ๊ฐ€์ง€๋Š” ์—ฌ๋Ÿฌ ๊ฐ’๋“ค์˜ ์ง‘ํ•ฉ์„ ๋‹ด๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

Recoil์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฐฉํ–ฅ ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, React ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•œ๋‹ค. ์ด ๊ทธ๋ž˜ํ”„์˜ ๋ฟŒ๋ฆฌ ์—ญํ• ์„ ํ•˜๋Š” ๊ฒƒ์ด atom, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํŒŒ์ƒ๋œ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๊ฐ€ selector ์ด๋‹ค. ์ƒํƒœ์˜ ํ๋ฆ„์€ atom -> selector -> component ์™€ ๊ฐ™์ด ํ๋ฅธ๋‹ค.

Atom

atom ์€ Recoil์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์ตœ์†Œ ๋‹จ์œ„์˜ ์ƒํƒœ๋กœ, ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋™์‹œ์— ๊ตฌ๋…ํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. atom ์ด ์—…๋ฐ์ดํŠธ๋˜๋ฉด, ๊ฐ๊ฐ์˜ ๊ตฌ๋…๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋ฐ˜์˜ํ•ด ๋ฆฌ๋ Œ๋”๋งํ•œ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

atom ์€ atom() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ์„ฑํ•œ๋‹ค. atom ์˜ ํ‚ค ๊ฐ’์€ ์ „์—ญ์ ์œผ๋กœ ๊ณ ์œ ํ•ด์•ผ ํ•œ๋‹ค.

const fontSizeState = atom({
  key: 'fontSizeState', // ๊ณ ์œ ํ•œ ํ‚ค ๊ฐ’
  default: 14,
});

๋™์ผํ•œ atom ์ด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋ฉฐ, ์ปดํฌ๋„ŒํŠธ์—์„œ atom ์„ ์ฝ๊ณ  ์“ธ ๋•Œ๋Š” useRecoilState(keyValue) ๋ผ๋Š” ํ›…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

Selector

selector ๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ atom์ด๋‚˜ selector๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ ์ƒˆ๋กœ์šด ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ด๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด, atom ์— ์ €์žฅ๋œ ์›๋ณธ ์ƒํƒœ๋กœ๋ถ€ํ„ฐ ํŒŒ์ƒ๋œ ์ƒํƒœ๋ฅผ ๋งŒ๋“œ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. selector ๋Š” ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์€ atom์ด๋‚˜ selector๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์žฌ์‹คํ–‰๋œ๋‹ค.

selector ๋ฅผ ์ •์˜ํ•  ๋•Œ get ์†์„ฑ์€ ๊ณ„์‚ฐ ๋กœ์ง์„ ๋‹ด์€ ํ•จ์ˆ˜์ด๋‹ค. ์ด ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” get() ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ atom์ด๋‚˜ selector์˜ ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ ‘๊ทผํ•˜๋ฉด ์ž๋™์œผ๋กœ ์˜์กด ๊ด€๊ณ„๊ฐ€ ๋“ฑ๋ก๋ผ, ์ฐธ์กฐ ์ค‘์ธ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค selector ๊ฐ€ ์žฌ์‹คํ–‰๋œ๋‹ค.

const userNameSelector = selector({
  key: 'userNameSelector',
  get: ({ get }) => {
    const user = get(userState);
    return `${user.firstName} ${user.lastName}`;
  },
});

๊ธฐ๋ณธ์ ์œผ๋กœ selector๋Š” ์ฝ๊ธฐ ์ „์šฉ์ด๋ฉฐ, useRecoilValue(selectorName) ํ›…์œผ๋กœ ๊ฐ’์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ set ์†์„ฑ์„ ํ•จ๊ป˜ ์ •์˜ํ•˜๋ฉด ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ selector ๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. useRecoilState(selectorName) ํ›…์œผ๋กœ get/set ๋ชจ๋‘ ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

Recoil์˜ ํŠน์ง• ์ •๋ฆฌํ•˜๊ธฐ

  • Recoil์€ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ์˜ ์ƒํƒœ ๊ด€๋ฆฌ์ฒ˜๋Ÿผ ๊ฐ„๋‹จํ•œ get/set ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก, boilerplate-free API ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
// example
const [user, setUser] = useRecoilState(userState);
  • ๋™์‹œ์„ฑ ๋ชจ๋“œ๋ฅผ ๋น„๋กฏํ•œ ๋‹ค์–‘ํ•œ React ๊ธฐ๋Šฅ๋“ค๊ณผ ํ˜ธํ™˜์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    Recoil์ด React ํŒ€์—์„œ ๋งŒ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋™์‹œ์„ฑ ๋ชจ๋“œ, Suspense, React.lazy ๋“ฑ์˜ ์ตœ์‹  ๊ธฐ๋Šฅ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ†ตํ•ฉ๋œ๋‹ค.

๋™์‹œ์„ฑ ๋ชจ๋“œ(Concurrent Mode)
ํ๋ฆ„์ด ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ์ด๋ฉฐ, ๋ฆฌ์•กํŠธ์—์„œ ๋ Œ๋”๋ง์˜ ๋™์ž‘ ์šฐ์„  ์ˆœ์œ„๋ฅผ ์ •ํ•ด ์ ์ ˆํ•œ ๋•Œ์— ๋ Œ๋”๋งํ•ด์ค€๋‹ค.

  • ์ƒํƒœ ์ •์˜์— ๋Œ€ํ•œ ์ฝ”๋“œ ๋ถ„ํ• (Code Splitting)์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    Recoil์˜ ์ƒํƒœ(atom, selector)๋Š” ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ ๊ธฐ๋Šฅ๋ณ„๋กœ ๋…๋ฆฝ์ ์ธ ์ƒํƒœ ๋ชจ๋“ˆ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜์— ์šฉ์ดํ•˜๋‹ค.
  • ์ „์—ญ ์ƒํƒœ๋ฅผ ๊ต์ฒด ์‹œ ์ปดํฌ๋„ŒํŠธ ์ˆ˜์ •์ด ๋ถˆํ•„์š”ํ•˜๋‹ค.
    Recoil์˜ atom ์„ ๋‹ค๋ฅธ atom ์œผ๋กœ ๊ต์ฒดํ•˜๋”๋ผ๋„, ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ƒํƒœ๋ฅผ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ์˜ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • ์ „์—ญ ์ƒํƒœ์˜ ์ง€์†์„ฑ
    Recoil์˜ ์ƒํƒœ๋Š” React ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์™€ ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๊ฑฐ๋‚˜ ์–ธ๋งˆ์šดํŠธ๋˜์–ด๋„ ์ „์—ญ ์ƒํƒœ๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋œ๋‹ค.
  • atom๊ณผ selector๋งŒ ์•Œ๊ณ ๋„ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•  ์ •๋„์˜ ๋‚ฎ์€ ๋Ÿฌ๋‹ ์ปค๋ธŒ

๐Ÿšจ ์ฃผ์˜ํ•  ์ 

Recoil์€ ๋นŒ๋“œ ํ›„ ES5 ๋ฌธ๋ฒ•์œผ๋กœ ๋ณ€ํ™˜๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, Recoil์„ ES5์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐ˜๋“œ์‹œ ES6์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

Reference

Redux Essentials, Part 1: Redux Overview and Concepts
๋ฆฌ๋•์Šค(Redux)๋Š” ์™œ ์“ฐ๋Š” ๊ฑด๋ฐโ‰
Recoil ๊ณต์‹ ๋ฌธ์„œ - ์†Œ๊ฐœ - ๋™๊ธฐ

profile
๊พธ์ค€ํ•จ์ด ๋ฌด๊ธฐ

0๊ฐœ์˜ ๋Œ“๊ธ€