๐Ÿ“• ์žฌ๋ฏธ์žˆ๋Š” Redux(3)(redux ๋ฏธ๋“ค์›จ์–ด ์จ๋ณด๊ธฐ)

Lee Jooamยท2022๋…„ 6์›” 16์ผ
0

๋ฏธ๋“ค์›จ์–ด๋Š” ์–‘ ์ชฝ์„ ์—ฐ๊ฒฐํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ค‘๊ฐ„์—์„œ ๋งค๊ฐœ ์—ญํ• ์„ ํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด์ด๋‹ค.
์ถœ์ฒ˜: ์œ„ํ‚คํ”ผ๋””์•„

๐Ÿ™„ ๋ฏธ๋“ค์›จ์–ด ์ ์šฉํ•˜๊ธฐ

๋ฆฌ๋•์Šค์—์„œ ๋ฏธ๋“ค์›จ์–ด๋Š” ์•ก์…˜๊ณผ store ์ค‘๊ฐ„์—์„œ ํŠน์ • ์ž‘์—…์„ ํ•ด์ฃผ๋Š” ๋ชจ๋“ˆ์ด๋‹ค. ๋ฆฌ๋•์Šค๋Š” store๋ฅผ ๋งŒ๋“ค ๋•Œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, redux-saga, redux-thunk ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฏธ๋“ค์›จ์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ์กด์žฌํ•œ๋‹ค.

์ด ํฌ์ŠคํŠธ์—์„œ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์‚ฌ์šฉ๋ณด๋‹ค๋Š” ๋ฏธ๋“ค์›จ์–ด ๊ทธ ์ž์ฒด์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

// module/counter.js
const INCREMENT = "counter/INCREMENT";
const DECREMENT = "counter/DECREMENT";

export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });

const initialState = 0;

function counter(state = initialState, action) {
  switch (action.type) {
    case INCREMENT:
      return state + 1;
    case DECREMENT:
      return state - 1;
    default:
      return state;
  }
}

export default counter;
// module/index.js

import { combineReducers } from "redux";
import counter from "./counter";

const rootReducer = combineReducers({ counter });

export default rootReducer;
import React, { useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { increment } from "./module/counter";

const App = () => {
  const count = useSelector((state) => state.counter);
  const dispatch = useDispatch();
  return (
    <>
      <div>{count}</div>
      <button onClick={() => dispatch(increment())}>+</button>
    </>
  );
};
export default App;
// index.js
const store = createStore(rootReducer);

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

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ์—์„œ App ์ปดํฌ๋„ŒํŠธ๋Š” react-redux ํŒจํ‚ค์ง€๊ฐ€ ์ œ๊ณตํ•˜๋Š” hook์„ ํ†ตํ•ด counter state์— ์ ‘๊ทผํ•˜๊ณ  ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ console์„ ํ†ตํ•ด ๋กœ๊น…ํ•˜๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž.

// middleware.js
export function loggerMiddleware(store) {
  return function (next) {
    return function (action) {
      console.log(store.getState());
      console.log(action);

      next(action);
    };
  };
}

๋ฆฌ๋•์Šค์—์„œ ๋ฏธ๋“ค์›จ์–ด์˜ ๊ทœ๊ฒฉ์€ store๋ฅผ ๋ฐ›์•„ next๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•˜๊ณ , next๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜์—์„œ action์„ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

export const loggerMiddleware = (store) => (next) => (action) => {
  console.log(store.getState());
  console.log(action);

  next(action);
}

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ๊ฒ ๋‹ค.

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ์ด์œ ๋Š” ๋ฆฌ๋•์Šค ๋‚ด๋ถ€์˜ applyMiddleware๋ฅผ ํ†ตํ•ด ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ ์šฉ์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

ํ•ด๋‹น ๋‚ด๋ถ€์˜ ๊ตฌํ˜„ ์‚ฌํ•ญ์ด ์œ„์™€ ๊ฐ™์€ ํ˜•ํƒœ๋ฅผ ๋”ฐ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ์—๋„ ์œ„์™€ ๊ฐ™์€ currying ํŒจํ„ด์„ ์ด์šฉํ•ด์•ผํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋งค๋ฒˆ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ store์™€ next๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ  action๋งŒ ์ „๋‹ฌ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

store๋Š” ๊ณ ์ •์ด๊ณ , next๋„ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด, ๋งˆ์ง€๋ง‰์ด๋ผ๋ฉด reducer์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ณ ์ •๊ฐ’์ด๋‹ค. ๋ณ€ํ™”ํ•˜๋Š” ๋Œ€์ƒ์ธ action์—๋งŒ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋‘˜ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ๋„ ์žฅ์ ์ด๋‹ค.

const store = createStore(rootReducer, applyMiddleware(loggerMiddleware));

index.js ํŒŒ์ผ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ ์šฉ์‹œ์ผฐ๋‹ค. ์ฝ˜์†” ์ฐฝ์„ ํ†ตํ•ด ์ „๋‹ฌ๋ฐ›์€ ํ˜„์žฌ store์˜ state์™€ ์ „๋‹ฌ๋ฐ›์€ ์•ก์…˜์ด ๋ฌด์—‡์ธ์ง€๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค!

๐ŸŽ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

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

// module/board.js
const FETCH_START = "board/fetch_start";
const FETCH_FAILED = "board/fetch_failed";
const FETCH_SUCCESS = "board/fetch_success";

const initialState = {
  loading: false,
  boards: [],
};

export const createRequestThunk = (id) => async (dispatch, getState) => {
  dispatch({ type: FETCH_START });

  try {
    const result = await fetch(
      `https://jsonplaceholder.typicode.com/todos/${id + 1}`
    ).then((res) => res.json());
    dispatch({ type: FETCH_SUCCESS, payload: result });
  } catch (e) {
    console.log(e);
    dispatch({ type: FETCH_FAILED });
  }
};

function board(state = initialState, action) {
  switch (action.type) {
    case FETCH_START:
      return { ...state, loading: true };
    case FETCH_SUCCESS:
      return {
        ...state,
        boards: state.boards.concat(action.payload),
        loading: false,
      };
    case FETCH_FAILED:
      return {
        ...state,
        loading: false,
      };
    default:
      return state;
  }
}

export default board;

์ถ”๊ฐ€์ ์ธ ์ž‘์—…์„ ์œ„ํ•ด board๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๋ฆฌ๋“€์„œ๋ฅผ ํ•˜๋‚˜ ์ƒ์„ฑํ–ˆ๋‹ค. ์ด ๋ฆฌ๋“€์„œ๋ฅผ ๊ธฐ์กด rootReducer์— ์—ฐ๊ฒฐํ•ด์•ผํ•œ๋‹ค.

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

createRequestThunk๋ฅผ ํ†ตํ•ด ๋ฐ˜ํ™˜๋ฐ›์€ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋Š” ์ผ์€ ๊ฐ„๋‹จํ•˜๋‹ค.

  1. ๋น„๋™๊ธฐ ์ž‘์—… ์‹œ์ž‘์„ ์•Œ๋ฆฌ๋Š” ์•ก์…˜ dispatch
  2. ๋น„๋™๊ธฐ ์ž‘์—… ์‹คํ–‰
  3. ์‹คํ–‰ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋น„๋™๊ธฐ ์ž‘์—…์˜ ์„ฑ๊ณต, ์‹คํŒจ ์•ก์…˜ dispatch

์‚ฌ์šฉ์€ App ์ปดํฌ๋„ŒํŠธ์—์„œ ํ•œ๋‹ค.

onst App = () => {
  const count = useSelector((state) => state.counter);
  const boards = useSelector((state) => state.board.boards);
  const loading = useSelector((state) => state.board.loading);
  const dispatch = useDispatch();

  const fetchData = () => {
    dispatch(increment());
    dispatch(createRequestThunk(count));
  };
  return (
    <>
      <div>{count}</div>
      <button onClick={fetchData}>+</button>
      {loading ? (
        <span>Loading...</span>
      ) : (
        boards.map((item) => <div>{item.title}</div>)
      )}
    </>
  );
};
export default App;

fetchData ํ•จ์ˆ˜๋ฅผ ๋ณด๋ฉด ๋‘ ๋ฒˆ์งธ dispatch๋กœ ํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ”๋‹ค. dispatch์— ์ „๋‹ฌํ•˜๋Š” ์•ก์…˜์€ plain object์—ฌ์•ผ ํ•˜๋Š”๋ฐ ์ด๊ฒƒ์€ ๊ทœ์น™์— ์–ด๊ธ‹๋‚œ๋‹ค.

์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// middleware.js

...

export function thunkMiddleware(store) {
  return function (next) {
    return function (action) {
      if (typeof action === "function") {
        action(store.dispatch, store.getState);
      } else {
        next(action);
      }
    };
  };
}

์ด ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ํ•˜๋Š” ์—ญํ• ์€ ์•ก์…˜์ด ํ•จ์ˆ˜ ํƒ€์ž…์œผ๋กœ ๋“ค์–ด์˜ฌ ๋•Œ์™€ ๊ทธ๋ ‡์ง€ ์•Š์„ ๋•Œ๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ๊ฐ๊ธฐ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

ํ•จ์ˆ˜ ํƒ€์ž…์ด ์•„๋‹ ๋• ํ‰๋ฒ”ํ•˜๊ฒŒ next ํ˜ธ์ถœ์„ ํ•˜์ง€๋งŒ ํ•จ์ˆ˜ ํƒ€์ž…์ผ ๊ฒฝ์šฐ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ฒ„๋ฆฐ๋‹ค.

์ด๋•Œ createRequestThunk์— store.dispatch์™€ store.getState๋ฅผ ์ „ํ•ด ๋ฆฌ๋•์Šค์˜ ๊ธฐ๋Šฅ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

createRequestThunk์˜ ๊ฒฝ์šฐ ๋‚ด๋ถ€์—์„œ dispatch๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ๋‹ค์‹œ ๋ฏธ๋“ค์›จ์–ด๋กœ ๋“ค์–ด๊ฐ€๊ณ  ์ „๋‹ฌํ•œ ์•ก์…˜์ด plain object์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋‹ค๋ฅธ ์ฒ˜๋ฆฌ ์—†์ด next์— action์„ ์ „๋‹ฌํ•ด ํ˜ธ์ถœํ•œ๋‹ค.

๐Ÿ”จ redux-thunk

redux thunk ํŒจํ‚ค์ง€๊ฐ€ ํ•ด์ฃผ๋Š” ์ผ์ด ์œ„์™€ ๊ฐ™์€ ์ž‘์—…์ด๋‹ค. ๋ฌผ๋ก  ๋‚ด๋ถ€์ ์œผ๋กœ ๋” ์ •๊ตํ•œ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ฒ ์ง€๋งŒ ์ „์ฒด์ ์ธ ๋ชจ์Šต์€ ์œ„์— ์ž‘์„ฑํ•œ ์˜ˆ์‹œ์™€ ๊ฐ™๋‹ค.

๋™๊ธฐ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฆฌ๋•์Šค์˜ ์•ก์…˜๋“ค์„ ํ•œ ๋ฉ์–ด๋ฆฌ๋กœ ๋ฌถ์–ด ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

ํ›„๊ธฐ

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

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ๊ฑธ์–ด๊ฐ€๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

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