09. Redux 데이터 흐름 이해하기
07편에서 Action·Reducer·Store를 각각 만들어봤다면, 이 편은 dispatch() 호출 한 번이 실제로 몇 단계를 거쳐 화면 갱신까지 이어지는지 순서대로 추적합니다. 이 순서를 정확히 알아두면 11~12편(React-Redux 연동)에서 “왜 이 시점에 리렌더되는지"가 자연스럽게 이해됩니다.
학습 목표
dispatch()호출부터 화면 갱신까지의 전체 단계를 순서대로 설명할 수 있다.- 미들웨어가 이 흐름의 어느 지점에 끼어드는지 설명할 수 있다.
- Redux DevTools로 실제 데이터 흐름을 관찰할 수 있다.
동기 데이터 흐름: 5단계
가장 단순한 동기 액션의 흐름은 다섯 단계로 나눌 수 있습니다.
sequenceDiagram participant App as 앱 코드 participant Store as Store participant MW as 미들웨어 체인 participant Reducer as Root Reducer participant Sub as 구독자(UI) App->>Store: "1) dispatch(action)" Store->>MW: "2) 미들웨어 체인 통과" MW->>Reducer: "3) reducer(state, action) 호출" Reducer-->>Store: "4) 새 state 반환, Store 내부 갱신" Store->>Sub: "5) 구독자에게 알림 → 리렌더"
- Action 생성: 사용자 이벤트나 코드가
dispatch({ type: "counter/incremented" })를 호출한다. - 미들웨어 체인 통과: Action은 등록된 미들웨어를 순서대로 거친다(로깅, Thunk 등). 21편에서 자세히 다룬다.
- 루트 리듀서 호출: 최종적으로
rootReducer(state, action)이 호출되어 다음 상태를 계산한다. - Store 내부 상태 갱신: 계산된 새 상태로 Store 내부 값이 교체된다.
- 구독자 알림:
subscribe()로 등록된 콜백들이 실행되고, React-Redux를 쓰는 경우 해당 상태를 구독하는 컴포넌트가 리렌더된다.
| |
미들웨어가 끼어드는 지점
미들웨어는 Action이 리듀서에 도달하기 전에 가로챌 수 있는 함수입니다. 로깅, 비동기 처리, 에러 리포팅 등이 이 지점에서 이뤄집니다.
| |
next(action)을 호출하지 않으면 Action이 리듀서까지 도달하지 못합니다. 이 구조 덕분에 미들웨어는 Action을 가로채서 변형하거나, 아예 막거나, 비동기 작업 후 다른 Action을 대신 dispatch할 수 있습니다. 21편에서 미들웨어를 직접 만들어보고, 22~24편에서 Thunk·Saga·RTK Query가 이 지점을 어떻게 활용하는지 다룹니다.
흔한 오개념 하나: 미들웨어를 여러 개 등록하면 “순서는 상관없다"고 생각하기 쉽지만, 실제로는 등록 순서가 실행 순서를 그대로 결정합니다.
| |
loggerMiddleware가 thunkMiddleware보다 먼저 오면, 콘솔에는 { type: "...", payload: ... } 같은 액션 객체 대신 함수 자체가 찍혀서 디버깅에 도움이 되지 않습니다. 미들웨어를 조합할 때는 “이 미들웨어가 처리하고 넘긴 결과를 다음 미들웨어가 받는다"는 순서를 항상 염두에 둬야 합니다.
비동기 흐름: 세 개의 액션으로 쪼개기
04편에서 리듀서는 비동기를 다룰 수 없다고 했습니다. 그래서 실무의 비동기 흐름은 하나의 요청을 세 개의 동기 액션으로 쪼개 표현합니다.
sequenceDiagram
participant UI as UI
participant MW as Thunk 미들웨어
participant API as 외부 API
participant Store as Store
UI->>MW: "dispatch(fetchUser(1))"
MW->>Store: "dispatch({type: 'user/loadStarted'})"
MW->>API: "fetch('/api/users/1')"
API-->>MW: "응답 도착"
alt "성공"
MW->>Store: "dispatch({type: 'user/loadSucceeded', payload: user})"
else "실패"
MW->>Store: "dispatch({type: 'user/loadFailed', payload: error})"
end
이 흐름에서 리듀서는 여전히 순수하고 동기적인 세 액션(loadStarted, loadSucceeded, loadFailed)만 처리합니다. 실제 네트워크 호출과 그 타이밍은 미들웨어가 전담하고, 리듀서는 “결과가 이미 나왔다"는 사실만 반영합니다. 이 패턴은 22편(Redux Thunk)에서 실제 코드로 구현합니다.
Redux DevTools로 흐름 관찰하기
Redux DevTools 브라우저 확장은 dispatch된 모든 Action과 그로 인한 상태 변화를 시간 순서대로 기록합니다.
- Action 목록: 지금까지 dispatch된 모든 Action이 순서대로 나열된다.
- Diff 탭: 특정 Action이 상태의 어느 부분을 바꿨는지 보여준다.
- 시간여행(Time Travel): 과거의 특정 Action 시점으로 되감아, 그 당시 상태로 UI를 되돌려볼 수 있다.
08편에서 강조했듯, 이 시간여행 기능은 리듀서가 순수하고 상태가 불변으로 관리될 때만 정확히 동작합니다. 상태를 직접 변경하는 코드가 하나라도 섞이면, DevTools가 보여주는 과거 스냅샷이 실제로는 이미 오염된 객체를 가리키게 됩니다.
실무 체크리스트
- Action이 dispatch된 시점부터 화면이 갱신되는 시점까지 5단계를 순서대로 설명할 수 있는가?
- 여러 미들웨어를 등록할 때 순서가 의도한 실행 흐름(예: logger가 실제 액션 객체를 로깅하는지)과 일치하는가?
- 비동기 작업을 리듀서가 아니라 미들웨어에서 처리하고, 리듀서에는 결과를 알리는 동기 액션만 전달하는가?
- Redux DevTools에서 Action 목록과 Diff를 실제로 열어 상태 변화를 확인해본 적이 있는가?
연습 과제
기초(★☆☆)
store.subscribe()콜백 안에console.trace()를 넣어, 어떤 호출 스택을 거쳐 알림이 오는지 관찰해보세요.
중급(★★☆)
loggerMiddleware를 직접 만들어createStore(rootReducer, applyMiddleware(loggerMiddleware))로 적용하고, 콘솔에 찍히는 순서를 확인해보세요.
고급(★★★)
user/loadStarted→user/loadSucceeded/user/loadFailed세 액션을 처리하는 리듀서를 작성하고, 각 상태 전이(idle→loading→succeeded/failed)를 상태 다이어그램으로 그려보세요.
요약
dispatch()한 번은 미들웨어 체인 → 리듀서 → Store 갱신 → 구독자 알림이라는 5단계를 순서대로 거친다.- 미들웨어는 Action이 리듀서에 도달하기 전에 가로채 로깅·비동기 처리 등을 수행할 수 있는 지점이다.
- 비동기 요청은 시작·성공·실패라는 세 개의 순수 동기 액션으로 쪼개 리듀서의 순수성을 지킨다.
참고 문헌 및 출처(추천)
- Redux 공식 문서, “Redux Fundamentals, Part 6: Async Logic and Data Fetching”
- Redux DevTools Extension 공식 문서, “Features”
- Redux 공식 문서, “Middleware”
![Featured image of post [Redux] 09. Redux 데이터 흐름 이해하기](/post/redux/redux-data-flow/wordcloud_hu_217f651caf655ae8.webp)
![[Redux] 07. Redux의 핵심 - Action, Reducer, Store](/post/redux/redux-core-concepts/wordcloud_hu_c656c166a5f38d17.webp)
![[Redux] 08. 불변성의 중요성 - Immutability in Redux](/post/redux/immutability-in-redux/wordcloud_hu_3a5feb2a8eb157f4.webp)
![[Redux] 09. Redux 데이터 흐름 이해하기](/post/redux/redux-data-flow/wordcloud_hu_5d8af85599fe0b0c.webp)
![[Redux] 10. Redux를 사용하는 이유와 적절한 사용 시기](/post/redux/when-to-use-redux/wordcloud_hu_5206c8759804db52.webp)
![[Redux] 11. React-Redux 시작하기 - Provider와 connect](/post/redux/react-redux-basics/wordcloud_hu_ff09d62dce5f2caf.webp)
![[Redux] 00. 커리큘럼 - 학습 로드맵](/post/redux/getting-started-redux/wordcloud_hu_2ed9b2e29c50fe99.webp)
![[Tutorial] Learn Prompting - 프롬프트 엔지니어링 무료 가이드 정리](/post/2022-12-30-learn-prompting/wordcloud_hu_6a9d105de4834753.webp)
![[Hardware] LattePanda Alpha에 Ubuntu 16.04 LTS 설치 가이드](/post/2018-12-06-install-ubuntu-16.04-on-lattepanda/wordcloud_hu_fc536f8de2cbd4bf.webp)
![[Redux] 06. Redux란 무엇인가 - Flux 아키텍처와 상태 관리](/post/redux/what-is-redux/wordcloud_hu_bc06cda637d98c61.webp)