상태관리 라이브러리/Redux

[Redux] 사용법부터 이론까지 살펴보는 Redux: 4. Redux-toolkit

tea-tea 2024. 3. 18. 18:51

리덕스는 전역 상태관리 라이브러리이자 중앙집중식 스토어로서 유용하다. 하지만, 리덕스에는 세 가지 불편함이 존재했다.

  • store, reducer, actionCreater 등의 초기 세팅을 위해 반복적으로 많은 코드를 작성해야 한다.
  • reducer는 불변성의 법칙에 따라 store를 변형하면 안된다. 따라서, 복잡한 state를 가공하기 위해 spread 연산자 등의 기법을 사용해서 긴 코드를 작성해야 한다. 
  • reducer을 잘 사용하기 위해 여러 라이브러리를 직접 설치해줘야 한다.(immer, reselect ...)
  • 타입스크립트가 제대로 지원되지 않는다.

 

이런 불편함에 대한 대안으로서 등장한 redux-toolkit은 다음 이점을 가지고 있다.

  • 반복적으로 작성하던 세팅을 간소하게 작성 가능
  • 스토어 변경 과정에서 immer 라이브러리를 이용한 비불변성 업데이트 가능
  • 다양한 미들웨어와 라이브러리를 기본으로 세팅
  • 타입스크립트로 store의 타입 명시, action, reducer 등을 잘 지원함.
  • dev-tool이라는 강력한 디버깅 툴 지원

 

이 글에서는 redux-toolkit을 이용해서 기존 리덕스 코드를 줄이는 방법을 정리한다.

 

세팅

npm install react-redux

npm install @reduxjs/toolkit

 

 

createAction


목적

기존의 리덕스에서는 일정한 형식의 action 객체를 생성하기 위해서 action creator를 직접 정의해야 했다. createAction은 이 과정을 간소화한다.

 

기존의 action

const addTodo = (text) => ({ type: ADD, payload: {text} });
const deleteTodo = (id) => ({ type: DELETE, payload: {id }});

export const actionCreators = {
addTodo,
deleteTodo,
};

//@ dispatch로 액션을 보내는 경우
dispatch(actionCreators.addTodo("hello"))

 

createAction을 통한 축약

// 액션의 타입 정의
const addTodo = createAction("add");
const deleteTodo = createAction("delete");

// "add"
console.log(addTodo.type)

//객체를 반환함
//{type:"add",payload:{text:"hello"} }
addTodo({text:"hello"})

 

  • 주의: 타입 이름 겹침 금지 | 서로 다른 객체 간에 타입명이 겹치면 안됨. 겹칠 시, 하나의 액션이 중복되는 다른 액션에도 적용되는 오류

 

callback 설정

위 방식은 액션 객체의 type 프로퍼티만 자동 설정해준다. 그 외의 프로퍼티까지 설정하고 싶으면 콜백 함수를 설정해주자.

// 액션의 타입 정의
const addTodo = createAction("add", (payload) => ({ payload: {text:payload } }));
const deleteTodo = createAction("delete", (payload) => ({ payload: {id:payload } }));


//객체를 반환함
//{type:"add",payload:{text:"hello"} }
addTodo("hello")

 

createReducer


목적

리듀서의 swith 분기처리 로직을 간소화하고  immer 라이브러리를 이용하여 기존 상태값의 mutate를 허용한다.

 

기존의 리듀서

const countReducer = function (state = [], action) {
	switch (action.type) {
        case addTodo.type:
            {
                return [action.value, ...state];
            }

        case deleteTodo.type:
            {
                return state.filter((element) => element.id != action.value);
            }

        default:
         return state;
	}
};
// 기존 리듀서는 스위칭문이 필요하고, 기존 state를 mutate해서는 안됨

 

 

creatReducer

const addTodo = createAction("add", (payload) => ({ payload: {text:payload } }));
const deleteTodo = createAction("delete", (payload) => ({ payload: {id:payload } }));


const RootReducer = createReducer(state,
	(builder) => {
        builder.addCase(actionCreator.addTodo, (state, action) => {
			state.todo.unshift({ id: Date.now(), text: action.payload });
			});
            //기존 state의 불변성을 무시하고 업데이트 가능

        builder.addCase(actionCreator.deleteTodo, (state, action) => (
		{...state, todo: state.todo.filter((e) => e.id !== action.payload)})
		
        builder.addDefaultCase((state, action) => (state))
        });
	}
);

 

  • 선택적 mutate 가능 | 새 state를 반환하거나 기존 statemutate 하거나 선택 가능하다. 이는 리덕스 툴킷이 immer 같은 라이브러리를 이용해서 실제로는 새 스테이트를 반환해줌

 

configureStore


목적

createStore의 대체 도구로, 다양한 옵션 설정과 편리한 개발자 툴 사용이 가능하다.

const countReducer = createReducer(///)
const countStore = configureStore({ reducer: countReducer });

 

개발자 툴 | Redux DevTools (구글 확장 프로그램)

구글의 확장프로그램으로 설치가능한 툴이다. 현재의 리덕스 스테이트, 액션, dispatch가 작동하는 과정을 보여주기 때문에 디버깅에 용이하다. 이 툴은 리덕스 configurestore을 쓰는 최신 사이트라면 사용가능하다. 개발자 도구 탭에서 사용가능하다.

 

확장프로그램 링크

https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko

 

createSlice


목적

reduceraction creator을 동시에 생성하고 관리할 수 있게 해준다. 내부적으로는 createAction createReducer를 사용하므로 Immer를 사용하여 "mutating" 불변 업데이트도 가능하다.

const countReducer = createSlice({
    name: "todo",
    initialState: [],
    reducers: {
        add:
        // 액션 타입 설정: add
            (state, action) => {
                state.push({ id:action.payload.id  });
                },
           	 },

        delete:
        // 액션 타입
		(state, action) => state.filter((element) => element.id != action.payload),

});

const countStore = configureStore({ reducer: todo });

 

https://redux-toolkit.js.org/api/createSlice

 

참고로 이런 훅을 사용할 시, 기존에 컴포넌트로 전달되던 state의 형태에 변화가 있을 수 있다. 그러니 주의해야 한다.