18. configureStore - Store 설정 자동화
07편에서 createStore와 combineReducers로 Store를 만들었고, 09편에서 미들웨어를 applyMiddleware로 연결하는 개념을 다뤘습니다. 이 편은 이 모든 설정을 configureStore 하나로 대체하고, RTK가 개발 환경에서 자동으로 켜주는 두 가지 검사 미들웨어를 살펴봅니다.
학습 목표
configureStore로 여러 리듀서를 조합하고 DevTools까지 연결하는 Store를 만들 수 있다.- 개발 환경에서 자동으로 켜지는 상태 변경 감지·직렬화 감지 미들웨어의 역할을 설명할 수 있다.
middleware옵션으로 기본 미들웨어를 확장하거나 커스터마이징할 수 있다.
configureStore의 기본 사용법
| |
15편의 순수 Redux 버전과 비교해봅시다.
| |
configureStore는 reducer 옵션에 객체를 전달하면 내부적으로 combineReducers를 자동 호출합니다(단일 리듀서 함수를 바로 전달할 수도 있습니다). 겉보기엔 큰 차이가 없어 보이지만, configureStore는 여기에 더 많은 것을 기본으로 켜줍니다.
자동으로 켜지는 것들
configureStore는 인자를 최소화해도 다음을 자동으로 설정합니다.
- Redux DevTools Extension 연결: 09편에서 다룬 시간 여행 디버깅이 별도 설정 없이 바로 동작한다.
redux-thunk미들웨어: 19편에서 다룰 비동기 로직에 필요한 미들웨어가 기본 포함된다.- 개발 환경 전용 검사 미들웨어 두 가지: 아래에서 자세히 다룬다.
| |
configureStore가 이 조합을 대신해주므로, “DevTools 연결을 깜빡했다"거나 “프로덕션 빌드에 디버깅 코드가 남았다” 같은 실수가 줄어듭니다.
검사 미들웨어 1: 상태 변경 감지(Immutability Check)
08편에서 “리듀서 밖에서 상태를 직접 변경하면 안 된다"는 규칙을 배웠습니다. 개발 환경에서 configureStore는 매 액션 이후 상태 트리를 검사해, 직접 변경이 있었는지 자동으로 감지합니다.
| |
이 검사는 상태 트리 크기에 비례해 비용이 들기 때문에 프로덕션 빌드에서는 자동으로 꺼집니다. 즉 개발 중에만 안전망 역할을 하고, 실제 배포 성능에는 영향을 주지 않습니다.
검사 미들웨어 2: 직렬화 감지(Serializability Check)
Redux 상태와 액션은 원칙적으로 직렬화 가능한 값(객체, 배열, 문자열, 숫자, 불리언, null)만 담아야 합니다. DevTools의 시간 여행 디버깅(09편)이나 상태를 localStorage에 저장하는 기능이 함수·클래스 인스턴스·Promise 같은 직렬화 불가능한 값이 섞이면 깨지기 때문입니다.
| |
이 경고를 없애려면 Date 객체 자체를 상태에 넣지 않고, 직렬화 가능한 형태로 바꿔서 저장하면 됩니다.
| |
이 검사 역시 개발 환경 전용이며, 정말로 직렬화 불가능한 값을 의도적으로 저장해야 하는 드문 경우에는 아래처럼 특정 경로를 예외로 지정할 수 있습니다.
| |
middleware 옵션으로 미들웨어 확장하기
기본 미들웨어(thunk + 두 검사 미들웨어)에 09편에서 만든 것 같은 커스텀 미들웨어를 추가하려면 getDefaultMiddleware()가 반환하는 배열에 이어 붙입니다.
| |
getDefaultMiddleware()가 이미 thunk와 개발용 검사 미들웨어를 포함하고 있으므로, 이 배열을 통째로 교체하지 않고 .concat()으로 이어 붙이는 것이 기본값을 잃지 않는 방법입니다.
preloadedState: Store를 빈 상태가 아닌 값으로 시작하기
실무에서는 Store를 항상 initialState(각 slice의 기본값)로만 시작하지 않습니다. 서버에서 미리 렌더링한 데이터를 그대로 이어받거나(SSR hydration), 이전 세션에서 localStorage에 저장해둔 상태를 복원해야 할 때는 preloadedState 옵션을 씁니다.
| |
preloadedState로 넘긴 값은 각 slice의 initialState를 덮어씁니다. SSR 환경에서는 서버가 이미 계산한 상태를 HTML에 직렬화해 내려보내고, 클라이언트 쪽 configureStore가 이를 preloadedState로 받아 “서버와 클라이언트가 같은 상태에서 시작하는” 하이드레이션을 구현합니다.
enhancers: Store 생성 과정 자체를 확장하기
enhancers 옵션은 middleware보다 더 근본적인 확장 지점으로, Store 생성 함수(createStore) 자체를 감싸는 고차 함수를 추가합니다. 대부분의 프로젝트는 configureStore가 기본 제공하는 DevTools 연결만으로 충분하므로 이 옵션을 직접 쓸 일은 드물지만, 오프라인 지원이나 상태 영속화 같은 라이브러리가 내부적으로 이 지점을 사용합니다.
| |
middleware가 “액션이 리듀서에 도달하기 전"을 확장하는 지점이라면, enhancers는 그보다 바깥쪽인 “Store 자체가 어떻게 만들어지는가"를 확장하는 지점입니다. 이 둘의 관계는 21편에서 미들웨어 구조를 다룰 때 다시 짚습니다.
실무 체크리스트
- 새 프로젝트의 Store를
createStore+applyMiddleware조합이 아니라configureStore로 만들고 있는가? - SSR이나 상태 영속화가 필요한 프로젝트라면
preloadedState로 초기 상태를 올바르게 주입하고 있는가? - 개발 환경 콘솔에 상태 변경 감지·직렬화 감지 경고가 뜨면 무시하지 않고 원인을 찾아 고치고 있는가?
- 커스텀 미들웨어를 추가할 때
getDefaultMiddleware()를 대체하지 않고 확장하는 방식으로 쓰고 있는가?
연습 과제
기초(★☆☆)
- 15편의
store.js를configureStore로 다시 작성하고, Redux DevTools Extension이 별도 설정 없이 연결되는지 확인해보세요.
중급(★★☆)
- 리듀서 안이 아닌 컴포넌트 코드에서
store.getState().todos.push(...)처럼 의도적으로 직접 변경을 실행해, 콘솔에 뜨는 경고 메시지를 관찰해보세요.
고급(★★★)
- 액션 페이로드에
File객체(직렬화 불가능)를 담아 직렬화 감지 경고를 발생시킨 뒤,ignoredActions로 해당 액션 타입만 예외 처리해보세요. localStorage에 상태를 저장하는 코드와, 새로고침 시 그 값을preloadedState로 복원하는 코드를 작성해 상태가 유지되는지 확인해보세요.
요약
configureStore는combineReducers,applyMiddleware, DevTools 연결을 한 번의 호출로 자동 설정한다.- 개발 환경에서는 상태 변경 감지와 직렬화 감지 미들웨어가 기본으로 켜져 흔한 실수를 조기에 발견하게 해준다.
- 커스텀 미들웨어는
getDefaultMiddleware()를 대체하지 않고 이어 붙이는 방식으로 추가하고,preloadedState로 SSR·영속화된 초기 상태를 주입할 수 있다.
참고 문헌 및 출처(추천)
- Redux Toolkit 공식 문서, “configureStore” API 레퍼런스
- Redux Toolkit 공식 문서, “immutableStateInvariantMiddleware”
- Redux Toolkit 공식 문서, “serializableStateInvariantMiddleware”
![Featured image of post [Redux] 18. configureStore - Store 설정 자동화](/post/redux/configure-store/wordcloud_hu_1bdde3afad5d820c.webp)
![[Redux] 16. Redux Toolkit 소개 - 왜 RTK인가?](/post/redux/redux-toolkit-introduction/wordcloud_hu_c57040366cbc5d2c.webp)
![[Redux] 17. createSlice - 간결한 리듀서 작성](/post/redux/create-slice/wordcloud_hu_835b90d6ba21615f.webp)
![[Redux] 18. configureStore - Store 설정 자동화](/post/redux/configure-store/wordcloud_hu_286e52baac5619ca.webp)
![[Redux] 19. createAsyncThunk - 비동기 로직 단순화](/post/redux/create-async-thunk/wordcloud_hu_37ab180eb6db5e9b.webp)
![[Redux] 20. 실습: RTK로 앱 리팩터링](/post/redux/practice-rtk-app/wordcloud_hu_97bfaa8516f2360e.webp)
![[Redux] 21. Redux 미들웨어의 이해](/post/redux/redux-middleware/wordcloud_hu_650a1d7068d6bc0d.webp)
![[Redux] 08. 불변성의 중요성 - Immutability in Redux](/post/redux/immutability-in-redux/wordcloud_hu_3a5feb2a8eb157f4.webp)
![[Redux] 11. React-Redux 시작하기 - Provider와 connect](/post/redux/react-redux-basics/wordcloud_hu_ff09d62dce5f2caf.webp)