앞에서 살펴봤던 리덕스의 코드들은 복잡하고 반복적인 코드가 많이 필요했습니다.
하나의 상태를 관리하기 위해 해당 액션을 생성하는 액션 생성자 함수를 작성하고, 액션 유형에 대한 액션 타입 상수를 만들고, 초기 상태를 설정하는 리듀서 함수를 작성해야 하는 등 기본적인 코드 양이 많았습니다. 또한, 리덕스를 잘 활용하기 위해서 여러 패키지를 추가해야 했으며, 상태 업데이트 시 불변성을 유지하기 위한 로직이 따로 필요했습니다.
이러한 리덕스의 몇 가지 문제를 개선하기 위해 리덕스 툴킷(Redux Toolkit)이 등장하게 되었습니다.
리덕스 툴킷은 리덕스 팀에서 만들어진 공식적으로 권장되는 헬퍼 라이브러리로서, 리덕스 로직을 작성하기 위한 표준 방식이 되도록 만들어졌습니다. 리덕스만 사용할 때와 비교하여, 리덕스 툴킷을 사용하면 여러 가지 이점이 있습니다. 특히, 액션 타입이나 액션 생성자, 그리고 리듀서 등을 따로 정의할 필요가 없다는 점은 매우 유용합니다.
아직 낯설겠지만 간단하게 코드를 통해 비교해 보겠습니다.
// 액션 타입 정의
const INCREMENT = "INCREMENT";
// 액션 생성자 함수 정의
function increment() {
return { type: INCREMENT };
}
// 초기 상태 정의
const initialState = {
count: 0,
};
// 리듀서 함수 정의
function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
default:
return state;
}
}
// 스토어 생성
const store = createStore(counterReducer);
이 코드는 리덕스를 이용해 카운터 값을 증가시키는 간단한 기능을 구현하고 있습니다. 리덕스만 사용할 때는 액션 타입과 액션 생성자 함수를 따로 정의해야 하며, 리듀서에서 상태를 불변성을 유지하면서 업데이트해야 합니다.
import { createSlice, configureStore } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { count: 0 },
reducers: {
increment(state) {
state.count += 1;
},
},
});
export const { increment } = counterSlice.actions;
const store = configureStore({
reducer: counterSlice.reducer,
});
반면에 리덕스 툴킷에서는 createSlice라는 함수를 통해 액션 타입과 액션 생성자 함수, 그리고 리듀서를 한 번에 정의할 수 있습니다. 또한 내부적으로 Immer 라이브러리가 동작하기 때문에 상태를 마치 직접 수정하는 것처럼 코드를 작성할 수 있습니다. 실제로는 불변성을 지키면서 새로운 상태를 생성해 주기 때문에 안정성도 보장됩니다. 리덕스 툴킷 사용법은 다음 장에서 자세히 다룰 예정이니 지금은 구조적인 차이점만 이해하시면 됩니다.
예시에 등장한 Immer 라이브러리처럼 리덕스 툴킷은 리덕스에서 사용자마다 다르게 설치하던 패키지나 라이브러리를 아예 내장시켜 작업을 단순화시키고 실수를 방지하게끔 해줍니다. 비동기 로직 처리 역시 간단해집니다. 비동기 api 통신을 위한 미들웨어인 redux-thunk가 내장되어 있어 npm이나 yarn으로 따로 설치할 필요가 없이 제공되는 createAsyncThunk라는 함수로 비동기 작업 처리 패턴을 단순화시킬 수 있습니다.
마지막으로 모든 애플리케이션에서 같은 구조와 패턴을 재사용함으로써 유지 보수 및 협업 시 이점을 제공합니다. 리덕스 툴킷 사용이 필수는 아니지만 가져다주는 이점이 분명한 만큼, 리덕스 공식 문서에서 말하듯 리덕스 앱에 리덕스 툴킷을 적용하는 것을 적극 권장합니다! 🎉