React 프로젝트를 진행하다 보면 컴포넌트 간 상태 공유가 복잡해지는 순간이 온다. props drilling으로 인한 코드의 복잡성, 여러 컴포넌트에서 동일한 상태를 사용해야 하는 상황들이 늘어나면서 전역 상태 관리의 필요성을 느끼게 된다. 이런 문제를 해결하기 위해 등장한 것이 전역 상태 관리 라이브러리이고, 그 중 하나가 Redux이다. 그리고 Redux의 복잡함을 개선한 것이 Redux Toolkit이다.
❓ Redux
- Redux는 JavaScript 애플리케이션을 위한 예측 가능한 상태 관리 라이브러리다. React와 함께 사용되는 경우가 많지만, 실제로는 React에 종속되지 않는 독립적인 라이브러리다. Redux는 애플리케이션의 모든 상태를 하나의 중앙 저장소(Store)에서 관리하며, 상태 변경을 예측 가능하고 추적 가능하게 만든다.
- Redux의 핵심은 단방향 데이터 흐름이다. 사용자의 액션이 발생하면 → 액션이 디스패치되고 → 리듀서가 상태를 변경하고 → 변경된 상태가 컴포넌트에 반영되는 일련의 과정이 항상 동일한 순서로 진행된다.
❓ Redux Toolkit
- Redux Toolkit은 효율적인 Redux 개발을 위한 공식 도구 모음이다. Redux 팀에서 직접 개발한 라이브러리로, Redux의 복잡성을 줄이고 개발자 경험을 개선하기 위해 만들어졌다. Redux Toolkit은 Redux 로직을 작성하기 위한 표준 방식이 되도록 설계되었으며, Redux 공식 문서에서도 사용을 강력히 권장하고 있다.
❗ 사용이유
✅ 복잡한 상태 관리 해결
- 여러 컴포넌트에서 동일한 상태를 공유해야 할 때, Redux를 사용하면 props drilling 없이 어디서든 상태에 접근할 수 있다. 특히 대규모 애플리케이션에서 상태 관리의 복잡성을 크게 줄여준다.
✅ 예측 가능한 상태 변화
- Redux는 정해진 규칙에 따라서만 상태를 변경할 수 있도록 강제한다. 모든 상태 변화는 액션을 통해서만 가능하며, 리듀서라는 순수 함수를 통해 처리된다. 이로 인해 상태 변화를 예측하기 쉽고 디버깅이 용이하다.
✅ 강력한 개발 도구
- Redux DevTools를 통해 상태 변화를 시각적으로 추적할 수 있고, 시간 여행 디버깅도 가능하다. 이는 복잡한 상태 변화를 추적하고 버그를 찾는 데 매우 유용하다.
💥 주요 문제점
1️⃣ 상대적으로 높은 진입 장벽
- Action, Action Creator, Reducer, Store 등 새로운 개념들을 이해해야 한다.
- 간단한 상태 변경에도 많은 보일러플레이트 코드가 필요하다.
2️⃣ 복잡한 초기 설정
- 스토어 설정, 미들웨어 연결, 개발 도구 설정 등 초기 설정이 복잡하다.
- 여러 패키지를 추가로 설치해야 한다(redux-thunk, immer 등)
💠 Redux Toolkit의 주요 기능
1️⃣ configureStore()
- 기존의 createStore를 대체하는 함수로, 더 간단한 설정을 제공한다.
- Redux DevTools, redux-thunk 등이 기본으로 포함되어 있다.
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
2️⃣ createSlice()
- Action Creator와 Reducer를 한 번에 생성하는 함수다.
- 내부적으로 Immer를 사용하여 불변성을 자동으로 관리한다.
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
3️⃣ createAsyncThunk
- 비동기 작업을 처리하는 thunk를 쉽게 생성할 수 있다.
- pending, fulfilled, rejected 상태를 자동으로 관리한다.
4️⃣ RTK Query
- 데이터 페칭과 캐싱을 위한 강력한 도구가 포함되어 있다.
- API 요청 로직을 크게 단순화할 수 있다.
▶ 사용 예시 (Redux와 비교)
기존 Redux 방식
// actions.js
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
export const increment = () => ({ type: INCREMENT })
export const decrement = () => ({ type: DECREMENT })
// reducer.js
const initialState = { value: 0 }
export default 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 폴더를 만들고, 그 안에 store.ts 파일을 생성한다.
// redux/store.ts
import { combineReducers, configureStore } from "@reduxjs/toolkit";
const rootReducer = combineReducers({});
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
});
export type RootState = ReturnType<typeof store.getState>;
export default store;
serializableCheck: false 옵션을 추가하면 non-serializable 타입을 Redux에 저장할 수 있다. 이는 객체 타입을 localStorage에 저장할 때 유용하다.
Redux Toolkit의 createSlice를 사용하여 카운터 기능을 구현한다.
// redux/counterSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0,
};
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
생성한 slice를 store에 연결한다.
// redux/store.ts (수정된 버전)
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
const rootReducer = combineReducers({
counter: counterReducer,
});
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
Provider로 앱을 감싸고 컴포넌트에서 Redux 상태를 사용한다.
// App.tsx
import React from 'react';
import { Provider } from 'react-redux';
import store from './redux/store';
import Counter from './components/Counter';
function App() {
return (
<Provider store={store}>
<div className="App">
<Counter />
</div>
</Provider>
);
}
export default App;
// components/Counter.tsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../redux/store';
import { increment, decrement, incrementByAmount } from '../redux/counterSlice';
const Counter: React.FC = () => {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<h2>카운터: {count}</h2>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
);
};
export default Counter;
Redux는 여전히 강력한 상태 관리 도구지만, 현재는 Redux Toolkit을 사용하는 것이 표준이 되었다. Redux Toolkit은 Redux의 모든 장점을 유지하면서도 개발자 경험을 크게 개선했다. 특히 createSlice와 configureStore 같은 API들은 코드의 가독성과 유지보수성을 크게 향상시킨다.
그런데 Redux나 Redux Toolkit을 도입하기 전에 정말 필요한지 고민해보자. 간단한 애플리케이션이라면 React의 useState나 Context API만으로도 충분할 수 있다.
[참고]
https://ko.redux.js.org/redux-toolkit/overview/
Redux Toolkit: 개요 | Redux
Redux Toolkit은 Redux 로직 작성을 위해 권장하는 방법입니다
ko.redux.js.org
Redux ToolKit 간단하게 파헤치기
Redux와의 차이점은?
blog.toktokhan.dev
https://shawnkim.tistory.com/120
[Redux] 리덕스 툴킷(Redux Toolkit)으로 리덕스 쉽게 사용하기
Redux Toolkit을 사용하는 이유 Redux Toolkit이란 Redux 상태 관리 라이브러리의 공식적인 패키지로, Redux를 보다 간편하고 효율적으로 활용할 수 있도록 도와주는 도구이다. Redux Toolkit이 아닌 Redux를 처
shawnkim.tistory.com
https://velog.io/@katej927/Redux-Redux-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90
Redux 01 | Basics (개념 / 3가지 규칙 / 사용 패턴)
리액트 상태 관리 라이브러리 (가장 많이 사용하는)컴포넌트 상태 업데이트 관련 로직을 다른 파일로 분리 → 더 효율적인 관리 가능컴포넌트끼리 똑같은 상태 공유 필요 시 → 여러 컴포넌트
velog.io
'React' 카테고리의 다른 글
[React] useEffect의 클린업 함수 (2) | 2025.06.20 |
---|---|
[React] 얕은 복사와 불변성 (2) | 2025.05.12 |
[React] 외부 클릭 감지 훅 만들기 (0) | 2025.05.04 |
[React] 이벤트 핸들러에 함수를 전달하는 방식 (0) | 2025.04.28 |
[React] 리액트의 폴더구조 (0) | 2025.03.31 |