Redux-Toolkit

제리·2023년 10월 5일
0

마이버추얼트립

목록 보기
8/9
post-thumbnail

Redux-Toolkit을 적용해보자.


Review

Redux는 가장 먼저 접하고 사용했던 상태 관리 라이브러리이다. 당시 saga와 함께 쓰면서 어마어마한 보일러 플레이트를 경험했었다. 흔히들 말하는 리덕스의 최대 단점이기도 한데 한번 익숙해지고 나서는 나름 체계적인 것 같기도 하고 크게 불편함을 느끼지 못했다. 게다가 이를 보완한 Redux-Toolkit은 확실히 간편해졌다. 물론, 아무리 간편해져도 Context나 Recoil에 비하면 여전히 번거롭기는 하다. 그래도 첫인상이 좋았던 덕인지, 익숙해져서인지 리덕스로 상태 관리를 했을 때 전체적인 상태 관계들이 더 잘보이는 것 같아서 장점이 더 크게 느껴진다.


Structure

Redux-Toolkit의 실행 구조를 그림으로 확인해보자.

  1. store는 slice들을 갖는다.
  2. slice는 initialState와 action들을 갖는다.
  3. RTK 클라우드 하위 모든 컴포넌트들은 store에 접근할 수 있다.

ContextAPI와 비교해보면 RTK의 slice는 useState가 적용된 Context Provider 같다. Store는 여러개의 클라우드를 가지고 있다. 사용할 때, 어떤 클라우드를 사용할 것인지만 선택하면 된다.


Note

✳︎ Redux-Toolkit 사용법

1. store 생성

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

const store = configureStore({
  reducer: {
    슬라이스이름: 생성한슬라이스.reducer,
  },
});

export default store;

store는 1개이다. 프로젝트의 규모가 커질수록 slice를 분리하고, slice들을 1개의 store에 저장한다. store의 reducer는 쪼개진 슬라이스들이 가지고 있는 reducer를 불러온다.


2. slice 생성

const sectionSlice = createSlice({
  name: "section", 
  initialState: sectionList,
  reducers: {
    addSection: (state, action) => {
      const { inputType, name, title } = action.payload;
		//state 변경
    },
    ...
  },
});

slice는 name, initialState, reducers들을 갖는다. 컴포넌트에서 action 함수로부터 전달 받은 인자들은 action.payload 안에 담겨 있다. 1개 이상일 경우 구조분해하면 깔끔하다.

✨ RTK는 자체적으로 immer를 적용하고 있어 불변성을 고려하지 않아도 된다.


3. Provider 적용하기

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

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

이제 모든 컴포넌트들은 매번 Provider를 만들 필요 없이 1개의 store에서 자유롭게 데이터를 꺼내서 사용할 수 있다. Context와 달리 최상위 컴포넌트를 1개의 클라우드로 감싸주면 되는 부분이 깔끔하게 느껴진다.

4. state 불러오기

///CheckList.jsx
const sectionList = useSelector((state) => state.section);

useSelector는 리덕스 스토어의 상태를 조회한다. 스토어에 저장된 상태들을 받아와서 그 중에서 section이라는 slice의 state값을 리턴한다. 여러개의 슬라이스가 있다면 그 중에서 원하는 state를 갖고 있는 슬라이스를 선택하는 것이다.

useSelector가 조회하는 slice의 이름은 store의 reducer에 지정한 이름이다.

위 코드를 풀어서 작성하면 아래와 같다.

const sectionList = useSelector((state) => {
  return state.section;

state(sectionList)가 객체일 때, 구조분해로 가져올 경우 불필요한 리렌더링(Context의 리렌더링 문제와 비슷한)이 발생한다. 이는 원하는 값마다 useSelector로 불러오는 것으로 해결할 수 있다.


5. action 불러오기

const dispatch = useDispatch();

//호출
dispatch(sectionSlice.actions.addSection({ inputType, name, title }));

slice의 reducers들은 actions에 담겨있다. 이를 사용하기 위해서는 useDispatch를 이용한다. 작성 방식은 useReducer의 dispatch와 동일하다. 이 코드는 slice에서 export 할 때 더 간단하게 할 수 있다.

//sectionSlice.js
export const { addItem } = sectionSlice.actions;

reducers의 action 함수만 구조분해를 이용해 export하고 컴포넌트에서는 함수를 바로 호출한다.


✳︎ Redux와 비교하기

//초기값
const initialState = {
  	count: 0
}

//Action
export const incrementAction = (count) => {
  return {
    type: 'increment',
    count
  }
}

//Reducer
export default (state = initialState, action) => {
  switch (action.type) {
    case 'increment': {
      return state + action.count
    }
      
//Component에서 값 사용
const count = useSelector((state) => state.count)

//Component에서 값 수정
dispatch(incrementAction(1))

Redux의 Store는 프로젝트에서 관리되는 모든 reducer들을 하나의 객체로 가지고 있다.

실행순서

  1. dispatch 함수를 호출하면서 action 함수를 실행한다.
  2. action 함수는 인자를 받아 {type: 'increment', count}를 반환한다.
  3. dispatch 함수의 호출은 reducer 호출과 같다.
    2번의 반환값을 store에 저장된 reducer가 받아 type에 일치하는 함수를 실행한다.

✳︎ Redux-Toolkit로 대체하기

1.createAction

export const increment = createAction(type, prepareAction?); //type : counter/increase
                                      
console.log(increment()) // {type: counter/increase}
console.log(increment(1)) // {type: counter/increase, payload: 1}

외부에서 호출되는 action 함수를 생성해준다. 이때 type은 필수값이지만, 선택적으로 두번째 인자에 콜백함수를 전달할 수 있다.

createAction으로 생성되는 것은 함수로 호출하여 사용한다.

2.createReducer

const initialState = { 
  count: 0 
}

const counterReducer = createReducer(initialState, (builder) => {
  builder
    .addCase(increment, (state, action) => {
		state + action.payload
    })
})

Redux에서 reducer type 구분을 위한 switch문을 대체한다. addCase의 첫번째 인자는 1번에서 만든 action함수이다. 컴포넌트에서의 사용은 useSelector와 dispatch를 이용한다는 점은 동일하다.

createSlice는 createAction + createReducer와 같다.

별도 action을 생성하지 않아도 slice에 설정한 reducer 함수가 곧 외부에서 사용되는 액션 함수가 된다.

profile
DOM과 친해지기

0개의 댓글

관련 채용 정보