Redux는 React와 함께 많이 사용되는 상태 관리 라이브러리로, 글로벌 상태를 관리하고 앱의 상태 변경을 예측 가능하게 만들어 줌. 그러나 Redux는 설정이 복잡하고 보일러플레이트 코드가 많아 학습 곡선이 높은 편임.
Redux Toolkit은 Redux의 복잡성을 줄이고자 Redux 팀에서 공식적으로 제공하는 툴킷으로, Redux의 사용을 더 간편하게 만들어주는 여러 유틸리티와 함수들을 포함하고 있음. Redux를 더 빠르게 설정하고 관리할 수 있게 해, 복잡한 상태 관리를 쉽게 할 수 있도록 도와줌.
Redux의 가장 큰 단점은 상태 관리를 위한 보일러플레이트 코드가 많다는 점임. 액션 타입, 액션 생성자, 리듀서를 직접 작성해야 하고, 스토어를 구성하는 과정도 복잡함.
Redux Toolkit은 이를 해결하기 위해, configureStore와 createSlice를 제공하여 설정 과정을 간소화함. createSlice를 사용하면, 자동으로 액션 타입과 생성자를 만들어주고 리듀서도 포함되어 코드가 간결해짐.
Redux 코드 예시
// actions.js
export const increment = () => ({ type: 'INCREMENT' });
export const decrement = () => ({ type: 'DECREMENT' });
// reducer.js
const initialState = { value: 0 };
export function counterReducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, value: state.value + 1 };
case 'DECREMENT':
return { ...state, value: state.value - 1 };
default:
return state;
}
}
Redux Toolkit 코드 예시
// counterSlice.js
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 },
decrement: (state) => { state.value -= 1 },
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
Redux에서 비동기 처리를 하려면 redux-thunk 또는 redux-saga 같은 미들웨어를 추가로 설정해야 함. 이 설정은 초보자에게 다소 어려울 수 있음.
Redux Toolkit은 createAsyncThunk라는 유틸리티를 제공해 비동기 작업을 더 쉽게 처리할 수 있도록 지원함. createAsyncThunk는 비동기 작업의 상태(로딩, 성공, 실패)를 자동으로 관리해 주므로, 비동기 코드를 작성하는 과정이 더 단순해짐.
Redux 비동기 작업 처리 예시 (redux-thunk)
// actions.js
export const fetchData = () => async (dispatch) => {
dispatch({ type: 'FETCH_START' });
try {
const response = await fetch('/api/data');
const data = await response.json();
dispatch({ type: 'FETCH_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_ERROR', error });
}
};
Redux Toolkit 비동기 작업 처리 예시
// dataSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchData = createAsyncThunk('data/fetchData', async () => {
const response = await fetch('/api/data');
return response.json();
});
const dataSlice = createSlice({
name: 'data',
initialState: { data: [], loading: false, error: null },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.loading = true;
})
.addCase(fetchData.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchData.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});
export default dataSlice.reducer;
Redux에서는 상태 변경 시 항상 불변성을 유지해야 함. Redux Toolkit은 불변성 관리를 쉽게 해주는 Immer를 내장하고 있어, 상태를 직접 변경하는 것처럼 코드를 작성해도 내부적으로 불변성을 유지해 줌.
Redux 코드 (불변성 관리 필요)
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, value: state.value + 1 };
default:
return state;
}
}
Redux Toolkit 코드 (Immer 내장)
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1 }, // 불변성 유지 자동 관리
},
});
export default counterSlice.reducer;
Redux에서는 스토어를 구성할 때 createStore를 사용하고 미들웨어를 추가로 설정해야 함. 반면, Redux Toolkit의 configureStore는 여러 설정을 기본으로 제공해, 미들웨어와 DevTools 설정이 자동으로 포함됨.
// Redux Toolkit 스토어 설정
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
Redux Toolkit은 Redux의 단점을 개선해 초보자도 쉽게 상태 관리를 할 수 있도록 설계되었음. 보일러플레이트 코드가 줄어들고 비동기 작업 처리가 간단해져, 더 효율적으로 상태 관리를 할 수 있음. 따라서 새 프로젝트나 간편한 상태 관리가 필요한 경우 Redux Toolkit을 사용하는 것이 좋음.