Redux Toolkit (feat. Flux Pattern)

손유민·2024년 12월 12일

리덕스 툴킷이란?

  • 리덕스툴킷
    리덕스 툴킷은 이전에 본 리덕스를 개량한 것이다.
    코드는 더 적게, 그리고 리덕스를 더 편하게 쓰기 위한 기능들을 흡수해서 만든 것.

  • 새로운 것인가?
    아니다. 리덕스툴킷은 기존 리덕스와 구조나 패러다임 모두 똑같다.
    전체 코드의 양을 줄이기 위해 새로운 API가 추가되었고 일일이 손으로 만들어 줘야 했던 ducks 패턴의 요소들이 어느정도 자동화 되었다.
    컴포넌트에서 useSelector를 통해 사용하는 것은 모두 같다.
    그저 모듈 파일이 바뀐것 뿐이다.

일반 리덕스와 코드 비교

  • 툴킷 설치하기
    CRA를 통해 새로운 프로젝트를 생성하고 아래 코드를 통해 패키지를 설치한다.
yarn add react-redux @reduxjs/toolkit
  • count 프로그램 코드 비교
// 일반 리덕스 예시 코드

// Action Value
const ADD_NUMBER = "ADD_NUMBER";
const MINUS_NUMBER = "MINUS_NUMBER";

// Action Creator
export const addNumber = (payload) => {
  return {
    type: ADD_NUMBER,
    payload,
  };
};

export const minusNumber = (payload) => {
  return {
    type: MINUS_NUMBER,
    payload,
  };
};

// Initial State
const initialState = {
  number: 0,
};

// Reducer
const counter = (state = initialState, action) => {
  switch (action.type) {
    case ADD_NUMBER:
      return {
        number: state.number + action.payload,
      };
    // [퀴즈 답]
    case MINUS_NUMBER:
      return {
        number: state.number - action.payload,
      };
    default:
      return state;
  }
};

// export default reducer
export default counter;

일반 리덕스의 counter 프로그램 모듈이다. action value, action creator를 별도로 생성해야했다. 그리고 리듀서에서 값을 어떻게 변화시킬지 만들어줘야 했다.

아래 코드는 리덕스 툴킷을 사용해 만든 counter 프로그램이다. 확실히 코드의 양이 줄었다.

// src/redux/modules/counterSlice.js

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

const initialState = {
  number: 0,
};

const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    addNumber: (state, action) => {
      state.number = state.number + action.payload;
    },

    minusNumber: (state, action) => {
      state.number = state.number - action.payload;
    },
  },
});

// 액션크리에이터는 컴포넌트에서 사용하기 위해 export 하고
export const { addNumber, minusNumber } = counterSlice.actions;
// reducer 는 configStore에 등록하기 위해 export default 합니다.
export default counterSlice.reducer;

차이첨은 action value와 action creator를 직접 생성해주지 않고 action value, action creator, reducer 모두 하나로 합쳐졌다.

슬라이스만 한번 다시 봐보자

//createSlice API 뼈대

const counterSlice = createSlice({
	name: '', // 이 모듈의 이름
	initialState : {}, // 이 모듈의 초기상태 값
	reducers : {}, // 이 모듈의 Reducer 로직
})

counterSlice 리듀서 객체 안에서 만들어주는 함수가 리듀서의 로직이 되면서도 동시에 action creator가 된다. 그리고 action value까지 함수의 이름을 따서 자동으로 만들어진다. 그래서 reducer만 만들어주면 된다.

// counterSlice.js의 Slice 구조

const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    // 리듀서 안에서 만든 함수 자체가 리듀서의 로직이자, 액션크리에이터가 된다.
    addNumber: (state, action) => {
      state.number = state.number + action.payload;
    },

    minusNumber: (state, action) => {
      state.number = state.number - action.payload;
    },
  },
});

그리고 일반 리덕스에서 export를 통해 각각의 action creator를 내보내주었던 것을 아래와 같이 작성하면 똑같이 내보낼 수 있다.

// 액션크리에이터는 컴포넌트에서 사용하기 위해 export 하고
export const { addNumber, minusNumber } = counterSlice.actions;
// reducer 는 configStore에 등록하기 위해 export default 합니다
export default counterSlice.reducer;
  • configStore 비교

    • 일반 리덕스
    // 일반 리덕스 combineReducers 예시 코드
    
    import { createStore } from "redux";
    import { combineReducers } from "redux";
    import counter from "../modules/counter";
    
    const rootReducer = combineReducers({
      counter,
    });
    const store = createStore(rootReducer);
    export default store;
    • 리덕스 툴킷
      configStore에서도 아래와 같이 작성하면 된다.
    // src/redux/modules/config/configStore.js
    
    import { configureStore } from "@reduxjs/toolkit";
    /**
     * import 해온 것은 slice.reducer
     */
    import counter from "../modules/counterSlice";
    import todos from "../modules/todosSlice";
    
    /**
     * 모듈(Slice)이 여러개인 경우
     * 추가할때마다 reducer 안에 각 모듈의 slice.reducer를 추가해줘야함
     *
     * 아래 예시는 하나의 프로젝트 안에서 counter 기능과 todos 기능이 모두 있고,
     * 이것을 각각 모듈로 구현한 다음에 아래 코드로 2개의 모듈을 스토어에 연결해준 것
     */
    const store = configureStore({
      reducer: { counter: counter, todos: todos },
    });
    
    export default store;
    • todos 모듈
      아래 코드를 작성해서 todosSlice.js를 만들고 위 설명과 같이 configureStore에 todosSlice를 추가해보자.
    // src/redux/modules/todosSlice.js
    
    import { createSlice } from "@reduxjs/toolkit";
    
    const initialState = {
      todos: [],
    }; // 단순히 비어있는 todosSlice이다.
    
    const todosSlice = createSlice({
      name: "todos",
      initialState,
      reducers: {},
    });
    
    export const {} = todosSlice.actions;
    export default todosSlice.reducer;

    그리고 이렇게 생성한 store를 export defaul해서 최상위의 index.js Provider에 주입해 주는것은 바뀌지 않는다.

    // index.js
    
    import React from "react";
    import ReactDOM from "react-dom/client";
    import App from "./App";
    import { Provider } from "react-redux";
    import store from "./redux/config/configStore";
    
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <Provider store={store}>
        <App />
      </Provider>
    );

    App.jsx에서는 툴킷을 사용해서 모듈을 조회할 수 있다. 방식은 일반 리덕스를 사용했을 때와 동일하다.
    아래 코드를 작성하여 리덕스 모듈들이 연결되었는지 확인해보자.

    // src/App.js
    
    import React from "react";
    import { useSelector } from "react-redux";
    
    const App = () => {
      // Store에 있는 todos 모듈 state 조회하기
      const todos = useSelector((state) => state.todos);
    
      // Store에 있는 counter 모듈 state 조회하기
      const counter = useSelector((state) => state.counter);
    
      return <div>App</div>;
    };
    
    export default App;

Redux Devtools 사용하기

  • devtools 소개
    리덕스를 사용하면 리덕스 devtools를 사용할 수 있다.
    현재 프로젝트의 state 상태라던가, 어떤 액션이 일어났을 때 그 액션이 무엇이고, 그것으로 인해 state가 어떻게 변경되었는지 등
    리덕스를 사용하여 개발할 때 아주 편리하게 사용할 수 있다.

구글 웹스토어에서 플러그인 설치

  • 사용법
    설치 후 만약 리액트 프로젝트에서 리덕스를 사용하고 있으면 아래와 같이 플러그인에 녹색으로 불이 켜진다.

그리고 개발자도구 탭에서 Redux 메뉴를 볼 수 있다.

프로젝트가 점점 복잡해질수록 로그를 확인하기 쉽지 않은데, 이 툴을 이용하면 쉽게 디버깅이 가능하다.

Flux 패턴

profile
프론트가 되고싶은 웹디자이너

0개의 댓글