Redux Toolkit

goodjam92·2023년 4월 11일
0

redux

목록 보기
3/3
post-thumbnail

서론

이 전에 작성 된 글을 이어서 이번엔 Redux Toolkit을 사용하여 코드를 다듬어보려고 합니다.
먼저 RTK라고도 불리는 Redux Toolkit에 대해 간략하게 알아보고 바로 코드를 수정해보겠습니다!

이 글은 노마드코더의 Redux 강의를 보고 학습하며 다시 정리한 내용입니다.

Redux Toolkit (RTK)

Redux Toolkit은 Redux 구성 작업을 단순화하고, 실수를 방지하며, 쉽게 사용할 수 있도록 하기위해 만들어진 도구입니다. Redux에 대해 우려하는 세 가지를 해결하기 위해 만들어졌다고 합니다.

  • 저장소를 설정하는 것이 복잡하다.
  • 쓸만하게 만들려면 많은 패키지들의 설치가 필요하다.
  • 보일러플레이트 코드들을 너무 많이 필요로 한다.

이러한 문제를 해결하기 위해 만든 RTK는 Redux에서 강력하게 권장한다고 합니다. 이 도구로 인해 코드를 더 좋게 유지보수하기 쉽게 만들어준다고 하네요.

설치

yarn add @reduxjs/toolkit
//or
npm install @reduxjs/toolkit

코드 리팩토링

그럼 이전에 작성했던 redux 문법들을 다음과 같이 RTK 문법으로 수정해보도록 하겠습니다.

  • createStore -> configureStore()
  • reducer -> createReducer()
  • actionFunction -> createAction()

마지막에는 리듀서 함수와 액션 생성자와 타입을 모두 포함하는 으마무시한 createSlice() 함수로 리팩토링을 진행해보도록 하겠습니다.

action.js

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

export const addToDo = createAction("ADD");
export const deleteToDo = createAction("DELETE");

먼저 액션 생성 함수를 이렇게 간단하게 1줄로 표현해줄 수 있게 되었습니다. 이전 코드와 비교해보면 훨씬 간결해진 것을 알 수 있습니다. createAction(action.type) 함수 괄호 안에는 type으로 사용할 매개변수만 전달하면 됩니다.

이제 store를 리팩토링하면서 action을 어떻게 받아서 처리하는지 확인해보도록 하겠습니다.

store.js

import { createReducer, configureStore } from "@reduxjs/toolkit";
import { addToDo, deleteToDo } from "../action";

// createReducer();
const toDoReducer = createReducer([], {
  [addToDo]: (state, action) => {
    state.push({ text : action.payload, id : Date.now() });
  },
  [deleteToDo]: (state, action) => 
    state.filter((toDo) => toDo.id !== action.payload); 
});

export const store = configureStore({ reducer : toDoReducer })

store의 리팩토링을 하였습니다. 달라진 부분이 보이시나요?

어떤 부분이 리팩토링 되었을까요?

  • createReducer(초기값, { 리듀서 역할 수행 }) 사용
  • switch 문 -> createAction 변수 사용 = [addToDo]
  • action.payload
    • payloadcreateAction()을 사용하게 되면 action.type외의 값은 모두 payload에 담기게 됩니다. (action.text === action.payload)
    • action.payload는 리덕스의 관습이라고 보면 되겠습니다.
  • createStore() -> configureStore()로 변경

그리고 마지막으로 가장 중요한 부분이 하나 있습니다. 바로 state.push를 사용한 점 입니다. RTK에서는 이러한 문법이 허용되는데 다만 mutate 메서드를 사용할 땐 return을 하지 않아야 합니다. 리듀서 내부의 [addToDo], [deleteToDo] 부분을 비교해보겠습니다.

  // 괄호 안에서 Array.push 후에 return을 하지 않음.
  [addToDo] : (state, action) => {
    state.push({ text : action.payload, id : Date.now() });
  };

  // Array.filter는 새로운 배열을 반환하기에 바로 return 되어도 문제가 없음
  [deleteToDo]: (state, action) => 
    state.filter((toDo) => toDo.id !== action.payload); 

어떤 차이를 가졌는지 이해가 되셨나요? RTK에서는 createReducer, createSlice에서 업데이트 로직을 대폭 단순화하기 위해 내부적으로 immer를 사용하여 상태가 업데이트 되기 때문에 mutate메서드를 사용할 수 있다고 합니다.
개발자가 신경써야하는 부분이 적어져서 좀 더 편리하게 사용할 수 있을 것 같습니다.

createSlice()

이제 마지막으로 createSlice()을 사용하여 리팩토링을 진행해보겠습니다.

store.js

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

const toDos = createSlice({
  name : "toDoReducer",
  initialState: [],
  reducers: {
    add: (state, action) => {
      state.push({ text : action.payload, id : Date.now() });
    },
    remove: (state, action) =>
      state.filter((toDo) => toDo.id !== action.payload);
  },
});

// dispatch 액션 발생을 위한 액션 생성자를 export하여 외부에서 사용
export const { add, remove } = toDos.actions;
export default configureStore({ reducer: toDos.reducer });

createSlice()를 사용하면 위의 코드와 같이 액션 생성자와 타입이 합쳐져있고, 리듀서의 역할 수행도 함수 안에 포함되어 있습니다. 이렇게 작성하면 따로 액션 생성 함수를 만들거나 타입을 지정할 필요가 없습니다.

이제 마지막으로 <Home />, <ToDo /> 두 컴포넌트를 수정하겠습니다.

Home.js

import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import ToDo from "../components/ToDo";
import { add } from "../store";

function Home() {
  const [text, setText] = useState("");
  const toDos = useSelector((state) => state);
  const dispatch = useDispatch();

  function onChange(e) {
    setText(e.target.value);
  }

  function onSubmit(e) {
    e.preventDefault();
    dispatch(add(text));
    setText("");
  }

  return (
    <>
      <h1>To Do</h1>
      <form onSubmit={onSubmit}>
        <input
          type="text"
          placeholder="What To Do?"
          value={text}
          onChange={onChange}
        />
        <button>Add</button>
      </form>
      <ul>
        {toDos.map((todo) => (
          <ToDo {...todo} key={todo.id} />
        ))}
      </ul>
    </>
  );
}

export default Home;

ToDo.js

import { useDispatch } from "react-redux";
import { remove } from "../store";

function ToDo({ text, id }) {
  const dispatch = useDispatch();
  return (
    <li>
      {text}
      <button onClick={() => dispatch(remove(id))}>Del</button>
    </li>
  );
}

export default ToDo;

정리

Redux Toolkit을 사용하여 리팩토링을 완료하였습니다. 이전에 작성 한 코드와 리팩토링 된 코드를 비교해보시면 놀라울정도로 코드가 깔끔하고 간결해졌습니다. 왜 리덕스에서 RTK를 강력하게 권장하는지 체감을 할 정도입니다. ReduxRedux Toolkit에는 아직 소개해드리지 못한 함수나 기능들이 있습니다. 이 부분들은 제가 좀 더 학습을하고나서 글을 작성하도록 하겠습니다.

그럼 두서 없는 이 글을 끝까지 읽어주셔서 감사합니다.
.
.
.
.
.

참고사이트
Redux Toolkit - 공식 문서

profile
습관을 들이도록 노력하자!

0개의 댓글