useReducer를 Redux로 리팩토링하기

유나랑·2024년 8월 20일

코딩온 X SeSAC

목록 보기
10/30
post-thumbnail

JS 상태관리 라이브러리인 Redux를 배웠다. 리액트의 상태관리 라이브러리로 가장 많이 사용하는데 컴포넌트 수가 많은 프로젝트에서 전역으로 상태를 관리하기 위해 사용한다.
간단히 용어 정리 후 useReducer hook으로 작성했던 코드를 Redux를 이용한 코드로 리팩토링해보겠다.

Redux 용어 정리

  • Store: 상태를 저장하는 오직 하나의 공간으로 Store 안에는 현재 애플리케이션 상태와 리듀서가 들어있다. 이 리듀서를 이용하여 데이터를 조작한다.
  • Selector: 사용할 상태를 선택
  • Action: 어떠한 상태의 변화를 일으키는 사건으로 컴포넌트에서 Store로 운반할 데이터이다. (객체 형태)
  • Dispatch: Action을 Store랑 연결해주는 역할로 액션을 Store에 보내는 행위를 의미한다. 객체를 파라미터로 넣어서 호출.
  • Reducer: 새로운 상태를 반환하는 함수로 type에 따라 변화를 일으킨다.
    👉 Action -> Store -> Reducer -> 새로운 상태 생성 의 흐름으로 이해하면 된다.

📌 시각적으로 이해하기

여기서 Redux Toolkit을 알아야 Redux를 더 편리하게 사용할 수 있는데 Redux Toolkit에 대해서도 간단히 정리해보자.

Redux Toolkit(RTK)

일반적인 작업들을 단순화해주는 유틸리티가 포함되어있어 코드를 더 간결하게 작성할 수 있다.

  • configureStore(): Redux store를 생성하기 위한 함수, 여러 미들웨어와 Reducer를 쉽게 통합할 수 있다.
  • createSlice(): Reducer와 Acion을 함께 생성하는 함수, 슬라이스라는 개념을 사용하여 액션 타입, 액션 생성 함수, 리듀서를 한 번에 정의한다.

useReducer hook를 사용한 코드

(Bank.js)

import React, { useReducer, useState } from "react";

// 초기값 설정
const initalState = { value: 0 };

// reducer 함수(type 정의)
const bankReducer = (state, action) => {

  switch (action.type) {
    case "plus": {
      return { value: state.value + action.value };
    }
    case "minus": {
      return { value: state.value - action.value };
    }
    default: {
      return { state };
    }
  }
};

function Bank() {
  // hook 사용
  const [money, setMoney] = useState("");
  const [state, dispatch] = useReducer(bankReducer, initalState);

  // 이벤트 핸들러 정의
  const onPlus = (e) => {
    dispatch({ type: "plus", value: money });
    setMoney("");
  };
  const onMinus = (e) => {
    dispatch({ type: "minus", value: money });
    setMoney("");
  };
  return (
    <div>
      <h2>짱구 은행</h2>
      <div>잔고: {state.value}</div>
      <input
        type="number"
        value={money}
        onChange={(e) => setMoney(parseInt(e.target.value))}
      />
      <button onClick={onPlus}>입금</button>
      <button onClick={onMinus}>출금</button>
    </div>
  );
}

export default Bank;

Redux로 리팩토링

1. store/bankSlice.js
: createSlice함수를 사용하여 Reducer와 Action 생성자 정의


import { createSlice } from "@reduxjs/toolkit";

// 1. 슬라이스 객체 정의
const bankSlice = createSlice({
  name: "bank",
  initialState: { number: 0 },
  reducers: {
    bankPlus: (state, action) => {
      state.number += action.payload;
    },
    bankMinus: (state, action) => {
      state.number -= action.payload;
    },
  },
});

export const { bankPlus, bankMinus } = bankSlice.actions;
export default bankSlice.reducer;

2. Bank.js
useSelector을 이용해 Store의 상태를 선택하고 useDispatch를 이용해 Action을 Store로 보냄.

import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { bankPlus, bankMinus } from "../store/bankSlice";

export default function BankContainer() {
  // 사용할 상태 선택
  const bank = useSelector((state) => state.bank.number);
  
  const dispatch = useDispatch();

  const [money, setMoney] = useState("");

  // 이벤트 핸들러
  const onClickPlus = () => {
    dispatch(bankPlus(money));
    setMoney("");
  };

  const onClickMinus = () => {
    dispatch(bankMinus(money));
    setMoney("");
  };

  const onchangeMoney = (e) => {
    setMoney(Number(e.target.value));
  };

  return (
    <div>
      <h3>usereducer를 redux로 리팩토링</h3>
      <h2>짱구 은행</h2>
      <h3>잔고: {bank}</h3>
      <input type="text" value={money} onChange={onchangeMoney} />
      <button onClick={onClickPlus}>입금</button>
      <button onClick={onClickMinus}>출금</button>
    </div>
  );
}

3. store/index.js
configureStore 함수를 사용하여 Store 생성

// 2. store 생성
import { configureStore } from "@reduxjs/toolkit";
import bankReducer from "./bankSlice";

const store = configureStore({
  reducer: {
    bank: bankReducer,
  },
});

export default store;

4. index.js
redux를 사용하기 위해 provider를 사용하여 store 데이터를 전달

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import store from "./store";
import { Provider } from "react-redux";

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

마무리하며

위에 코드양으로만 보면 Redux가 복잡해보여 왜 사용하나할 수 있지만 컴포넌트가 많아지면 관리할 상태도 많아져 상태를 전역으로 관리할 수 있는 Redux가 효율적이다. 곧 있을 프로젝트에서 사용해야하는데 더 예시를 찾아보고 연습해야겠다.

출처 및 참고
1. [새싹 X 코딩온] 영등포 캠퍼스 6기 입문자도 가능한 웹 개발자 부트캠프 강의 교안
2. https://ko.redux.js.org/tutorials/essentials/part-1-overview-concepts

0개의 댓글