030

๐Ÿ˜‰ ํ—ท๊ฐˆ๋ ธ๋˜ & ๋ชฐ๋ž๋˜ ๋ถ€๋ถ„๋“ค๋งŒ ์ •๋ฆฌํ•˜๋Š” ๋‚˜๋งŒ์˜ TIL
๐Ÿ˜ฏ ๋ชจ๋“  ๊ฐ•์˜ ๋‚ด์šฉ์€ ์ ์ง€ ์•Š์•„์š”!

์˜ค๋Š˜์˜ ์†Œ๊ฐ์€?
๋‚œ..์•„์ง recoil์˜ ์•„ํ† ๋ฏนํ•จ์—์„œ ํ—ค์–ด๋‚˜์˜ค์ง€ ๋ชปํ–ˆ๋‹ค.
redux์™€ ์นœํ•ด์ง€๋ ค๋ฉด ์•„์ง ๋ฉ€์—ˆ์Šต๋‹ˆ๋‹ค.
์•„๋‹ˆ ๋ฏธ๋“ค์›จ์–ด ์™œ ๊ทธ๋ ‡๊ฒŒ ๋งŽ์•„์š”?




Redux

Redux๋Š” Prop Drilling ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด contextAPI์™€ ์–ด๋–ค ์ ์ด ๋‹ค๋ฅผ๊นŒ?



1. ContextAPI

  • React ๋‚ด์žฅ API
  • Provider๊ฐ€ ๊ฐ€์ง„ ์ƒํƒœ๋ฅผ Consumer๊ฐ€ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉ

2. Redux

  • Thrid Party ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • ๋ฏธ๋“ค์›จ์–ด ๊ธฐ๋Šฅ ์ œ๊ณต
  • ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ œ๊ณต
  • Reducer๋ฅผ ํ†ตํ•ด Store์— ์ƒํƒœ๋ฅผ ์ €์žฅ, ํ•ด๋‹น ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด View๊ฐ€ ๋ Œ๋”๋ง
  • ์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.


์ฆ‰ Redux๋Š” ContextAPI์™€ useReducer Hook์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๋‹ค.

Redux๊ฐ€ ๋งŽ์€ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ํ•ด์„œ ๋ฌด์กฐ๊ฑด Redux๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.




Redux 3๊ฐ€์ง€ ์›์น™

1. Single source of truth

ํ•˜๋‚˜์˜ ํ”„๋กœ์ ํŠธ (์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜) ์•ˆ์—๋Š” ํ•˜๋‚˜์˜ ์Šคํ† ์–ด๋งŒ ์‚ฌ์šฉํ•˜์ž๋Š” ์›์น™์ด๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋””๋ฒ„๊น…์ด ์‰ฌ์›Œ์ง€๊ณ  ์„œ๋ฒ„์˜ ์ง๋ ฌํ™”๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ ์‰ฝ๊ฒŒ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„๋“ค์—ฌ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.


2. State is read-only

์ƒํƒœ๋ฅผ ๋ณ€ํ™”์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์€ ์˜ค์ง Action์„ ์ผ์œผํ‚ค๋Š” ๋ฐฉ๋ฒ• ๋ฟ์ด๋‹ค.


3. Changes are made with pure functions

๋ณ€ํ™”๋ฅผ ์ผ์œผํ‚ค๋Š” Reducer ํ•จ์ˆ˜๋Š” ์ˆœ์ˆ˜ํ•œ ํ•จ์ˆ˜์—ฌ์•ผ ํ•œ๋‹ค.

  1. ๋ฆฌ๋“€์„œ ํ•จ์ˆ˜๋Š” ์ด์ „ ์ƒํƒœ์™€ ์•ก์…˜ ๊ฐ์ฒด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š”๋‹ค.
  2. ํŒŒ๋ผ๋ฏธํ„ฐ ์™ธ์˜ ๊ฐ’์—๋Š” ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค.
  3. ์ด์ „ ์ƒํƒœ๋Š” ์ ˆ๋Œ€๋กœ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ , ๋ณ€ํ™”๋ฅผ ์ค€ ์ƒˆ๋กœ์šด ์ƒํƒœ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  4. ๋˜‘๊ฐ™์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ˜ธ์ถœ๋œ Reducer ํ•จ์ˆ˜๋Š” ์–ธ์ œ๋‚˜ ๋˜‘๊ฐ™์€ ๊ฒฐ๊ณผ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.




Redux ๊ธฐ์ดˆ

Redux์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ€์žฅ ๊ธฐ์ดˆ์ ์ธ ์šฉ์–ด๋Š” Store, action, dispatch, reducer์ด ์žˆ๋‹ค.


1. Store

์ƒํƒœ๊ฐ€ ๋“ค์–ด์žˆ๋Š” ๋ถ€๋ถ„์„ Store๋ผ๊ณ  ํ•œ๋‹ค.

ํ•˜๋‚˜์˜ ํ”„๋กœ์ ํŠธ๋Š” ํ•˜๋‚˜์˜ Store๋งŒ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.


2. Action

์ƒํƒœ์— ๋ณ€ํ™”๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด, Action์„ ์ผ์œผ์ผœ์•ผ ํ•œ๋‹ค.

Action์€ ๊ฐ์ฒด๋กœ ํ‘œํ˜„๋˜๋ฉฐ **typeํ•„๋“œ๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.**

import { v4 } from "uuid";
import { Action } from "./types";

export const addTask = (content: string): Action => {
  return {
    type: "ADD_TASK",
    payload: {
      id: v4(),
      content,
      complete: false,
    },
  };
};

export const updateTask = (
  id: string,
  content: string,
  complete: boolean
): Action => {
  return {
    type: "UPDATE_TASK",
    payload: {
      id,
      content,
      complete,
    },
  };
};

export const removeTask = (id: string): Action => {
  return {
    type: "REMOVE_TASK",
    payload: {
      id,
      content: "",
      complete: false,
    },
  };
};


3. Dispatch

Store์˜ ๋‚ด์žฅ ํ•จ์ˆ˜ ์ค‘ ํ•˜๋‚˜์ธ ๋””์ŠคํŒจ์น˜๋Š” ์•ก์…˜ ๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

์ฆ‰ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Store์˜ Dispatchํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.

์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ์™€ ๊ฐ™๋‹ค.

const dispatch = useDispatch();

dispatch(addTask(task)); // ์ด๋Ÿฐ ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.


4. Reducer

Reducer๋Š” ํ˜„์žฌ ์ƒํƒœ์™€ ์•ก์…˜ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ํ•„์š”ํ•˜๋‹ค๋ฉด ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

import { Action, Task } from "./types";

export const tasks = (state: Task[] = [], action: Action) => {
  switch (action.type) {
    case "ADD_TASK": {
      const newTask = action.payload;
      return [...state, newTask];
    }
    case "UPDATE_TASK": {
      const updatedTask = action.payload;
      return state.map((oldTask) =>
        oldTask.id === updatedTask.id ? updatedTask : oldTask
      );
    }
    case "REMOVE_TASK": {
      const removedTask = action.payload;
      return state.filter((task) => task.id !== removedTask.id);
    }
    default: {
      return state;
    }
  }
};




Redux-toolkit

์‹ค์Šต์—์„œ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋‹ค๋ค˜์ง€๋งŒ, ๊ฐ€์žฅ ์ค‘์š”ํ•ด ๋ณด์ด๋Š” ๊ณต์‹ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ redux-toolkit์— ๋Œ€ํ•ด ์งš์–ด๋ณด๋„๋ก ํ•˜์ž.

Redux๋ฅผ ์•„๋ฌด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด, Type ์ •์˜ โ†’ action ํ•จ์ˆ˜ ์ •์˜ โ†’ reducer ์ •์˜ ํ›„์—์•ผ ํ•˜๋‚˜์˜ Action์ด ์ƒ์„ฑ๋œ๋‹ค.

์ด๋Ÿฐ ๋„ˆ๋ฌด ๋ณต์žกํ•œ ์ผ๋ จ์˜ ๊ณผ์ •์„ ํ•จ์ถ•์‹œ์ผœ์ค„ ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Redux-toolkit์ด๋‹ค.


1. configureStore

configureStore์„ ์‚ฌ์šฉํ•˜๋ฉด store๋ฅผ ๋งŒ๋“ค์–ด์ค€๋‹ค. ๋งˆ์น˜ createStore๊ณผ ๊ฐ™๋‹ค.

ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์ ์€ reducer ๋ฟ๋งŒ ์•„๋‹Œ middleware, devTools ์‚ฌ์šฉ ์—ฌ๋ถ€, ์Šคํ† ์–ด์˜ ์ดˆ๊ธฐ ๊ฐ’ ๋“ฑ์„ ๊ฐ™์ด ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

export const store = configureStore({
  reducer: rootReducer,
  middleware: [logger],
  devTools: true,
});


2. createSlice

createSlice๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด createAction, createReducer๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ํ•จ๊ป˜ ์‚ฌ์šฉ๋œ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด slice ์ด๋ฆ„์„ ์ ‘๋‘์‚ฌ๋กœ Action Type์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. (tasks/add)

export const tasks = createSlice({
  name: "tasks",
  initialState: [] as Task[],
  reducers: {
    add: {
      reducer: (state: Task[], action: PayloadAction<Task>) => {
        state.push(action.payload);
      },
      // prepare๋ฅผ ์ž‘์„ฑํ•จ์œผ๋กœ์จ ๋ฆฌ๋“€์„œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ด์ „์— ์•ก์…˜์˜ ๋‚ด์šฉ์„ ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
      prepare: (content: string) => ({
        payload: {
          id: v4(),
          content,
          complete: false,
        },
      }),
    },
    update: {
      reducer: (state: Task[], action: PayloadAction<Task>) => {
        const index = state.findIndex((task) => task.id === action.payload.id);
        state[index] = action.payload;
      },
      prepare: (id: string, content: string, complete: boolean) => ({
        payload: {
          id,
          content,
          complete,
        },
      }),
    },
    remove: {
      reducer: (state: Task[], action: PayloadAction<Pick<Task, "id">>) => {
        const index = state.findIndex((task) => task.id === action.payload.id);
        if (index !== -1) state.splice(index, 1);
      },
      prepare: (id: string) => ({
        payload: { id },
      }),
    },
  },
});




Redux๋ฅผ ์‚ฌ์šฉํ•œ ์‹ค์Šต์„ ํ•˜๊ณ  ๋Š๋‚€ ์ ์€, ์ง„์ž…์žฅ๋ฒฝ์ด ๋งค์šฐ ๋†’๋‹ค.

๋˜ํ•œ ๋‹ค๋ฅธ ์‚ฌ์šฉ ์˜ˆ์‹œ๋ฅผ ์ฐพ์•„๋ณด๋Š”๋ฐ, ์‚ฌ๋žŒ๋งˆ๋‹ค ๊ฐ์ž ๋‹ค ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค..

์ž์ฃผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฐ์„ ์ตํžˆ๋Š” ์ˆ˜๋ฐ–์— ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ํ 





์ถœ์ฒ˜

https://kyounghwan01.github.io/blog/React/redux/redux-basic/

http://blog.hwahae.co.kr/all/tech/tech-tech/6946/

ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค

์นดํ…Œ๊ณ ๋ฆฌ: ,

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ