본문 바로가기
이론/Frontend

Redux와 RTK로 상태 관리하기

by 유세지 2024. 3. 3.

 

 

 

 

들어가며

React에서 전역 상태를 관리하는 방법은 다양합니다. React가 공식적으로 제공하는 Context API부터, 얼마전에 소개했던 Zustand 등 수많은 라이브러리들이 있습니다. 오늘은 그중에서도, 꽤 오랜 시간 사용되어 온 Redux와 RTK에 대해 알아보겠습니다.

 

 

Redux

 

 

Web Flux의 구조

 

 

Redux는 Flux 패턴을 따라가는 상태 컨테이너입니다. Dispatcher의 Action를 통해 Store의 상태를 변화시키고, 이 상태가 다시 View에 차례대로 반영되는 단방향 구조가 Flux 패턴의 핵심입니다. 이러한 구조로 만들어진 컨테이너를 활용하여 쉽게 데이터를 집약하고, 일관성 있게 관리할 수 있다는 것이 바로 Redux의 장점입니다.

 

예시를 통해 React에서 Redux를 사용하는 방법에 대해 알아보겠습니다.

 

Redux 사용하기

npm install redux react-redux
# 또는
yarn add redux react-redux

 

먼저 사용하는 패키지 매니저를 통해 react-redux 패키지를 설치해줍니다.

 

설치가 끝났다면 첫번째로, 데이터를 보관할 store를 만들어봅시다.

이번에 만들 상태는 Zustand로 전역 상태 관리하기에서 사용했던 것과 같습니다.

 

import { createStore } from 'redux'
import { theme } from './reducer'

// 상태 저장에 사용될 store입니다.
const store = createStore(theme);

export default store;

 

 

redux의 createStore 함수를 사용하여 theme이라는 상태를 저장하기 위한 store를 만들었습니다.

이제 store라는 이름을 통해 theme의 상태를 컨트롤하거나 불러올 수 있습니다.

 

다음으로는 createStore에 들어갈 theme 함수를 만들어보겠습니다.

이 theme 함수에서는 상태 변화에 필요한 로직들을 들고 있다가,

인자로 들어온 action의 키 값을 통해 해당 로직을 실행시킵니다.

 

먼저 action을 정의합니다.

우리는 "토글을 통해 다크모드를 끄고 켜는 로직" 이 필요하므로,

아래와 같이 action을 구성해주도록 하겠습니다.

 

src/action.js

// action을 정의합니다.
export const TOGGLE_DARK_MODE = 'TOGGLE_DARK_MODE';

export const toggleDarkMode = () => ({
  type: TOGGLE_DARK_MODE,
});

 

 

구성한 action을 토대로 reducer도 만들어줍니다.

 

src/reducer.js

import { TOGGLE_DARK_MODE } from "./action";

const initialState = {
  isDarkMode: false,
};

/** 
 * 상태 변화에 사용될 함수입니다.
 * reducer에 해당합니다.
 */
export function theme(state = initialState, action) {
  switch (action.type) {
    case TOGGLE_DARK_MODE:
      return {
        isDarkMode: !state.isDarkMode,
      };
    default:
      return state;
  }
}

 

 

이제 어디서든 해당 상태를 사용할 수 있도록 Provider를 세팅해주겠습니다.

 

src/index.js

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

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

 

 

react-redux에서 Provider 컴포넌트를 불러와 App 컴포넌트를 감싸도록 설정합니다.

이렇게 해서 Provider 하위의 컴포넌트에선 store에 접근할 수 있습니다.

 

src/Components.jsx

import { useSelector, useDispatch } from 'react-redux';
import { toggleDarkMode } from './actions';

export function DarkModeIndicator() {
  const isDarkMode = useSelector((state) => state.isDarkMode)

  return <div>{isDarkMode ? 'on' : 'off'}</div>
}

export function ToggleDarkModeButton() {
  const dispatch = useDispatch();

  return (
    <button onClick={() => dispatch(toggleDarkMode())}>change</button>
  );
}

 

 

실행해보면, 상태가 정상적으로 표시되고, 바뀌는 것까지 확인할 수 있습니다.

 

Redux를 통해 상태를 관리하는 모습입니다

 

 

여기까지가 Redux를 이용한 상태 관리 방법입니다.

 

 

Toolkit의 등장

위의 예시를 보시다시피, Redux는 하나의 상태를 관리하기 위해 action, reducer, store 등 많은 양의 코드를 필요로 합니다. 이는 최근에 떠오르는 상태 관리 라이브러리들이 적은 양의 보일러 플레이트로 각광받는 이유 중 하나이기도 합니다. 그만큼 Redux는 특유의 코드 양으로 인해 개발자들에게 피로감을 주었고, 관리의 어려움도 동반하였습니다.

 

이러한 Redux를 구원하기 위해, RTK(Redux ToolKit) 라는 라이브러리가 등장했습니다. 현재의 Redux 프로젝트를 RTK 프로젝트로 변경하며 어떤 점이 달라졌는지 살펴보겠습니다.

 

 

  • createStore를 대체하는 configureStore

RTK에선 createStore 대신 configureStore 를 사용합니다.

 

src/store.js

import { configureStore } from "@reduxjs/toolkit";
import themeReducer from "./themeSlice";

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

export default store;

 

여담으로 현재 Redux의 createStore는 취소선 처리(deprecated)가 되어있는데, configureStore를 사용하도록 유도하는 역할을 하고 있었습니다. (실제로 코드가 삭제되거나 지원을 멈추지는 않았습니다.)

 

 

  • Action, Reducer 등을 한 번에 담당하는 Slice

RTK에서는 기존에 따로 선언했던 이름, 액션, 초기 상태, 리듀서등을 Slice라는 단위로 한 곳에 묶어 처리합니다.

 

src/store.js

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

const initialState = {
  isDarkMode: false,
};

const themeSlice = createSlice({
  name: "theme",
  initialState,
  reducers: {
    toggleDarkMode: (state) => {
      state.isDarkMode = !state.isDarkMode;
    },
  },
});

export const { toggleDarkMode } = themeSlice.actions;
export default themeSlice.reducer;

 

 

이렇게 createSlice를 이용하여 묶어서 선언하게 되면, themeSlice 내부에 actions와 reducer 등이 자동으로 함께 선언됩니다. 기존과 대비하여 코드 양이 확 줄어든게 느껴지시나요? 이러한 효과 덕분에 많은 사람들이 Redux를 사용할때는 RTK를 함께 사용하고, 공식 문서에서도 이를 권장하고 있습니다.

 

 

공식 문서에서도 Redux Toolkit 사용을 권장하고 있습니다.

 

 

 

마지막으로, 컴포넌트에서의 호출만 Action 파일에서 Slice 파일로 변경해주면 RTK 적용이 끝납니다.

 

src/Component.jsx

import { useSelector, useDispatch } from "react-redux";
/**
 * Action 파일 대신, Slice에서 불러옵니다.
 */
import { toggleDarkMode } from "./themeSlice";

export function DarkModeIndicator() {
  const isDarkMode = useSelector((state) => state.isDarkMode);

  return <div>{isDarkMode ? "on" : "off"}</div>;
}

export function ToggleDarkModeButton() {
  const dispatch = useDispatch();

  return <button onClick={() => dispatch(toggleDarkMode())}>change</button>;
}

 

 

이제 코드를 실행하여 확인해보면,

 

RTK로 변경이 완료되었습니다.

 

 

 

마치며

이번 시간에는 오래도록 사용되어 왔던 Redux에 대해 알아보았습니다. Flux 패턴을 충실히 따르는 구조나 편리한 디버깅 툴의 지원 등으로 많은 사용자를 모았었고, 현재는 RTK를 통해 코드의 보일러 플레이트를 줄여 유지보수성을 크게 개선하였습니다. 실제로 다른 라이브러리들과 비교해보면 Redux의 사용률은 상당이 높은 편입니다. (물론, 그만큼 역사도 오래되었네요)

 

 

상태 관리 라이브러리들의 Npm trend 비교

 

 

 

비슷한 문제를 해결하기 위해 많은 기술들이 등장했지만 아직까지 굳건히 자리를 지키고 있고, 이러한 추세는 당분간은 변함없이 유지되지 않을까 싶습니다. 뚜렷한 장점을 가진 라이브러리이니, 프로젝트에 잘 적용할 수 있다면 여전히 고려해볼만 하다는 생각이 드네요.

 

읽어주셔서 감사합니다.

 

 

참고문서

반응형

댓글