[React] Redux ์ด๋ก 

Lemonยท2022๋…„ 6์›” 27์ผ
1

React

๋ชฉ๋ก ๋ณด๊ธฐ
9/21
post-thumbnail

๐ŸŒ™ย ย ์ƒˆ๋ฒฝ๊ณต๋ถ€


Redux

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


๐Ÿค” Redux๋Š” ์–ธ์ œ์“ธ๊นŒ?

๊ธฐ๋ณธ React ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ ๋Œ€์‘ํ•˜๊ธฐ ํž˜๋“ค ์ •๋„๋กœ ์ƒํƒœ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์กŒ๋‹ค๊ณ  ํ™•์‹คํžˆ ํŒ๋‹จ์ด ๋˜์—ˆ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๊ณต์‹๋ฌธ์„œ์— ๋‚˜์™€์žˆ๋‹ค.


Redux 3๊ฐ€์ง€ ๊ทœ์น™

1) ํ•˜๋‚˜์˜ ๊ฐ์ฒด ํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ์ €์žฅ

๋ชจ๋“  ์ƒํƒœ๋Š” ํ•˜๋‚˜์˜ ์ €์žฅ์†Œ ์•ˆ์— ์ €์žฅ

2) ์ƒํƒœ๋Š” ์ฝ๊ธฐ ์ „์šฉ

๋ฆฌ๋•์Šค์˜ ์ƒํƒฏ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ์•ก์…˜ ๊ฐ์ฒด์™€ ํ•จ๊ป˜ย dispatch๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ (๋งˆ์น˜ย setState)์ด๋‹ค.
๋ณ€ํ™”์— ๋Œ€ํ•œ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

  • Dispatch๊ฐ€ ํ˜ธ์ถœ๋œ ์ˆœ์„œ๋Œ€๋กœ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ณ€๊ฒฝ๋œ ์ˆœ์„œ์™€ ๋‚ด์—ญ์„ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•œ๋‹ค.
  • Action ๊ฐ์ฒด๋Š” ํ‰๋ฒ”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์ด๋ฉฐ, ์ด ๊ฐ์ฒด๊ฐ€ Store์— ์ž…๋ ฅ๋œ ์ˆœ์„œ์™€ ๋‚ด์šฉ์„ ์ €์žฅํ•ด๋‘๋ฉด ๋‚˜์ค‘์— ๊ทธ ๊ณผ์ •์„ ์‰ฝ๊ฒŒ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

3) ๋ณ€ํ™”๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ ๋˜์–ด์•ผํ•œ๋‹ค.

๐Ÿค” ์ˆœ์ˆ˜ํ•จ์ˆ˜ ๋ž€?

๋ณด์ž๋งˆ์ž ์–ด๋–ค ๊ฐ’์ด ๋‚˜์˜ฌ์ง€ ์˜ˆ์ƒ ๊ฐ€๋Šฅํ•œ ํ•จ์ˆ˜์ด๋‹ค.

// ์˜ˆ์ธก ๊ฐ€๋Šฅ = ์ˆœ์ˆ˜ ํ•จ์ˆ˜
const add = (num) => num + 1;
add(1)

// ์˜ˆ์ธจ ๋ถˆ๊ฐ€๋Šฅ โ‰  ์ˆœ์ˆ˜ ํ•จ์ˆ˜
const add2 = (num) => num + Math.random();
add2(1)

Redux ์‚ฌ์šฉ์„ ์œ„ํ•œ ํ‚ค์›Œ๋“œ ์ˆ™์ง€

1) Action

ํ•„์ˆ˜๊ฐ’ (๊ทธ ์™ธ์—๋Š” ๊ฐœ๋ฐœ์ž ๋งˆ์Œ๋Œ€๋กœ)
์ƒํƒœ์— ์–ด๋– ํ•œ ๋ณ€ํ™”๊ฐ€ ํ•„์š”ํ•˜๊ฒŒ ๋  ๋•Œ, ์•ก์…˜์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ๊ฐ์ฒด๋กœ ํ‘œํ˜„๋œ๋‹ค.

{
  type: "ADD_TODO",
  data: {
    id: 0,
    text: "๋ฆฌ๋•์Šค ๋ฐฐ์šฐ๊ธฐ"
  }
}

2) Action Creator ์•ก์…˜ ์ƒ์„ฑ ํ•จ์ˆ˜

์•ก์…˜์„ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜์ด๋‹ค.
ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ›์•„์™€์„œ ์•ก์…˜ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.

  • ์‰ฝ๊ฒŒ ์•ก์…˜์„ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•จ
  • ์‹ค์ˆ˜๋ฅผ ์ค„์—ฌ์ฃผ๊ธฐ ์œ„ํ•จ โ†’ ์ •ํ•ด์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ
export const addCart = (item) => { // ์•ก์…˜ "์ƒ์„ฑ ํ•จ์ˆ˜"
  return {
    type: "ADD_ITEM", // ์•ก์…˜ "๊ฐ์ฒด"
    payload: item,
  };
};

๋ณดํ†ต ํ•จ์ˆ˜ ์•ž์—ย export ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์„œ ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•œ๋‹ค.


3) Dispatcher

Action ๊ฐ์ฒด๋ฅผ Reducer์— ๋ณด๋‚ด๋Š” ์—ญํ• ์„ ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
์Šคํ† ์–ด์˜ ๋‚ด์žฅํ•จ์ˆ˜ โ†’ store.dispatch() ํ˜•ํƒœ๋กœ ์ œ๊ณตํ•œ๋‹ค.
๋ฐ˜๋“œ์‹œ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค.


4) Reducer

๋ฆฌ๋“€์„œ๋Š” ๋ณ€ํ™”๋ฅผ ์ผ์œผํ‚ค๋Š” ํ•จ์ˆ˜๋กœ ์ด์ „ ์ƒํƒฏ๊ฐ’๊ณผ ์•ก์…˜ ๊ฐ์ฒด๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ย ์ƒˆ๋กœ์šด ์ƒํƒฏ๊ฐ’์„ ๋งŒ๋“œ๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ด๋‹ค. ์˜ˆ์ธก ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.
๋ฆฌ๋“€์„œ๋Š” ๋‘๊ฐ€์ง€์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์•„์˜จ๋‹ค. (previousState, action) => newState ์ด๋Ÿฐ ํ˜•ํƒœ์ด๋‹ค.

function reducer(state, action) {
  // ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง
  return alteredState;
}

์ •๋ฆฌ, ํ˜„์žฌ์˜ ์ƒํƒœ์™€, ์ „๋‹ฌ ๋ฐ›์€ ์•ก์…˜์„ ์ฐธ๊ณ ํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๋ฐ˜ํ™˜ํ•œ๋‹ค.


5) Store

  • Redux ์•ฑ ์ „์ฒด์˜ ์ƒํƒœ๋กœ ๋ณดํ†ต ๊นŠ๊ฒŒ ์ค‘์ฒฉ๋˜์–ด ์žˆ๋Š” ๊ฐ์ฒด์ด๋‹ค.
  • store์—์„œ ๊ด€๋ฆฌ๋˜๊ณ ,ย store.getState()๋กœ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ญ์ƒ ์ง๋ ฌํ™”(Serialization) ๊ฐ€๋Šฅํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— JSON์œผ๋กœ ์‰ฝ๊ฒŒ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ๋“ค์€ ์ œ์™ธํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค(ex. ํ•จ์ˆ˜, Promise ๋“ฑ)

์ฝ”๋“œ ์˜ˆ์‹œ (+์„ค๋ช…)

const INITIAL_STATE = [];

// redux/cart.js
export default function cartReducer(state = INITIAL_STATE, action) { 
// ์ธ์ž๋กœ state, action 
// state = INITIAL_STATE -> ์ฒซ๋ฒˆ์งธ ๊ฐ’์œผ๋กœ ์ธ์ž ์•ˆ๋“ค์–ด์˜ค๋ฉด INITIAL_STATE๋ฅผ ์“ฐ๊ฒ ๋‹ค (useState() ์ดˆ๊ธฐ๊ฐ’๊ณผ ์œ ์‚ฌ)
  
	switch (action.type) {
	// switch -> if ๋ฌธ์ด๋ž‘ ์œ ์‚ฌ, ๊ฐœ๋… ํ™•์‹คํžˆ ๋ชฐ๋ผ๋„ ๋จ
	// action.type ์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅธ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ๊ฒƒ์ด๋‹ค.
    
		case "ADD_ITEM": // action.type์ด "ADD_ITEM"์ด๋ฉด
      return [...state, action.payload]; // ์ด์ „ ์ƒํƒœ ๋ณต์‚ฌํ•œ ๋‹ค์Œ์— ์ƒˆ๋กœ์šด item์„ ์ถ”๊ฐ€
    
		case "DELETE_ITEM": // action.type์ด "DELETE_ITEM"์ด๋ฉด
      return state.filter((product) => product.id !== payload.id); 
			// ์ด์ „ state์— filter ๋ฉ”์†Œ๋“œ๋กœ id ๋ž‘ ๋˜‘๊ฐ™์€ ๊ฒƒ๋“ค ์ง€์šด ๋’ค ์ƒˆ๋กœ ์ƒํƒœ ์ €์žฅ
    
		default: // ๊ธฐ๋ณธ๊ฐ’ ์„ค์ • -> ๋งŒ์•ฝ์— action.type์— ๊ฑธ๋ฆฌ๋Š”๊ฒŒ ์•„๋ฌด๊ฒƒ๋„ ์—†์„ ๊ฒฝ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ
      return state; // ํ•ด๋‹น ์‚ฌํ•ญ ์—†์œผ๋ฉด ์ด์ „ ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฆฌํ„ด
  }
}

// redux/index.js
import { combineReducers } from "redux";
import cartReducer from "./cartReducer";

export default combineReducers({ cartReducer });

Redux๋ž€

  1. View์—์„œ ์œ ์ €๊ฐ€ ์ผ์œผํ‚ค๋Š” ํ–‰๋™์— ๋งž๊ฒŒย Actionย ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๊ณ ,
  2. Action์€ย Dispatcher๋ฅผ ํ†ตํ•ดย Reducer๋กœ ์ „๋‹ฌ๋˜๊ณ ,
  3. Action์˜ type์— ๋”ฐ๋ผ Reducer ๋‚ด์— ๋ฏธ๋ฆฌ ์ •ํ•ด์ ธ ์žˆ๋˜ ๋กœ์ง์ดย Store๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ ,
  4. ๋ณ€๊ฒฝ๋œ Store์˜ ๋‚ด์šฉ์ดย View๋กœ ๋ฐ˜์˜๋˜๋Š” ํŒจํ„ด์ด๋ผ๊ณ  ์š”์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค.
profile
๊ฐœ๋ฏธ๋Š” ๋š ๋š ..์˜ค๋Š˜๋„ ๋š ๋š ๐Ÿœ

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