Redux 기초 개념 소개
Redux가 관리하는 상태를 하나의 Store에 두고 action·reducer·selector로 이어지는 단방향 흐름과 현대 Redux Toolkit 경계를 이해합니다.
Redux는 여러 화면이 함께 사용하는 client state를 예측 가능한 규칙으로 관리하는 독립적인 JavaScript 라이브러리입니다.
React에서는 공식 바인딩인 React-Redux와 함께 자주 사용하지만 Redux 자체가 React에 종속되지는 않습니다.
Redux의 핵심은 애플리케이션의 모든 상태를 한곳에 밀어 넣는 것이 아니라, Redux가 관리하기로 정한 상태를 하나의 store와 action 기반 전이 경계에서 다루는 것입니다.
컴포넌트에만 필요한 UI state는 로컬 state로, 주소로 표현할 상태는 URL로, 서버에서 가져온 데이터는 적절한 loader나 cache로 관리할 수 있습니다.
Context와 useReducer도 유용하지만 Context는 Provider 하위에 값을 전달하는 범위이고 reducer는 다음 상태를 계산하는 함수입니다. 두 기능을 함께 쓰면 그 트리에서 state와 dispatch를 공유할 수 있지만, Redux식 외부 store 객체, middleware, DevTools action 기록, selector별 동등성 비교가 자동으로 생기지는 않습니다.
Zustand나 Jotai 같은 외부 store 선택지도 있으므로, Redux는 이름이나 앱 크기보다 필요한 소유권·구독·추적 계약으로 비교해야 합니다.
Redux는 왜 필요한가?
앱 규모만으로 Redux 사용 여부가 정해지지는 않습니다.
다음 신호가 함께 나타나고 action 기반 추적과 개발 도구의 이득이 추가 비용보다 클 때 Redux를 검토합니다.
- 공유 범위: 같은 client state를 서로 멀리 떨어진 여러 화면과 기능이 읽습니다.
- 갱신 빈도: 그 상태가 시간에 따라 자주 바뀝니다.
- 전이 복잡성: 여러 사건이 관련 필드와 불변 조건을 함께 바꿉니다.
- 협업과 관찰: 변경 이유를 action 단위로 추적하고 동일한 규칙을 팀이 공유할 가치가 큽니다.
- 확장 지점: 미들웨어, DevTools, 테스트 가능한 reducer 같은 표준 경계가 필요합니다.
Redux는 action을 dispatch하는 공통 입구를 제공하지만 action 이력을 자동으로 영구 저장하지는 않습니다.
Redux DevTools 연동이 활성화되고 Extension 같은 모니터가 연결됐거나 로깅 미들웨어를 구성했으며 state와 action을 관찰 가능한 직렬화 형태로 유지했을 때 action과 전후 state를 디버깅 증거로 확인할 수 있습니다.
Redux의 세 가지 핵심 원칙
일반적인 Redux 앱은 하나의 store를 사용하며, 그 store는 Redux가 관리하는 전체 state tree를 보관합니다.
이는 모든 컴포넌트 로컬 state, URL state, 서버 cache까지 Redux에 넣으라는 뜻이 아닙니다.
store state를 직접 쓰지 않고 action을 dispatch해 변경 의도를 전달합니다.
action은 type 필드를 가진 평범한 객체이며, 필요한 경우 payload로 사건의 데이터를 담습니다.
reducer는 이전 state와 action을 받아 다음 state를 동기적으로 계산합니다.
같은 입력에 같은 결과를 내고, 이전 state를 직접 바꾸거나 API 호출·타이머·랜덤 값 생성 같은 부수 효과를 실행하지 않아야 합니다.
Redux Toolkit의 createSlice에서는 Immer 덕분에 변경처럼 보이는 문법을 쓸 수 있지만, 이전 state 자체를 바꾸지 않는 불변 갱신 계약은 유지됩니다.
Redux의 핵심 구성 요소
Redux가 관리하는 현재 state tree를 보관합니다. dispatch(action)으로 업데이트를 요청하고, getState()로 현재 snapshot을 읽으며, subscribe(listener)로 업데이트 알림을 받을 수 있습니다.
일반적인 앱은 하나의 store를 사용하고 새 코드는 Redux Toolkit의 configureStore로 설정합니다.
애플리케이션에서 무슨 일이 있었는지를 설명하는 평범한 객체입니다.
type은 사건 종류를 식별하고 payload는 reducer가 전이를 계산하는 데 필요한 최소 데이터를 담습니다. action 자체가 state를 변경하지는 않습니다.
reducer(previousState, action) 형태의 순수 함수입니다.
현재 state와 action으로 다음 state를 계산하며 비동기 작업과 부수 효과는 reducer 밖에 둡니다.
action을 store에 전달하는 함수입니다.
store는 root reducer를 실행해 다음 state를 저장한 뒤 구독자에게 업데이트를 알립니다. 미들웨어는 dispatch 경계에서 로깅이나 비동기 로직 같은 기능을 추가할 수 있습니다.
selector는 store state에서 컴포넌트가 필요한 값이나 파생 값을 읽습니다.
React-Redux의 useSelector는 store 업데이트 뒤 selector를 다시 실행하고, 기본적으로 이전 선택 결과와 새 결과를 ===로 비교합니다. Redux 업데이트로 인한 컴포넌트 렌더는 선택 결과가 달라질 때 발생하지만, 부모 렌더 같은 별도 경로는 여전히 존재합니다.
Redux의 데이터 흐름 (단방향)
Redux는 “무슨 일이 있었는가”와 “그 사건으로 상태가 어떻게 바뀌는가”를 한 방향으로 연결합니다.
Event → action → state → view
Redux는 “무슨 일이 있었는가”와 “상태가 어떻게 바뀌는가”를 분리합니다. action을 dispatch하면 reducer가 다음 snapshot을 계산하고, 구독자는 선택한 결과가 달라졌는지 확인해 화면 갱신을 결정합니다.
| 단계 | 책임 | 확인할 계약 |
|---|---|---|
| 1 · UI 사건 | 사용자 입력이나 비동기 작업 완료가 상태 변경 의도를 만듭니다. | 사건을 먼저 설명하고 다음 값을 UI에서 직접 쓰지 않습니다. |
| 2 · Action / dispatch | dispatch(action)으로 type과 필요한 payload를 store에 보냅니다. |
action은 “무슨 일이 있었는가”를 기록하는 평범한 객체입니다. |
| 3 · Reducer | 현재 state와 action만으로 다음 state를 동기적으로 계산합니다. | 이전 state를 바꾸지 않고 API·타이머·랜덤 같은 부수 효과를 실행하지 않습니다. |
| 4 · Store snapshot | root reducer의 반환값을 현재 state로 저장하고 구독자에게 업데이트를 알립니다. | getState()는 이 dispatch가 끝난 뒤의 snapshot을 반환합니다. |
| 5 · Selector / subscriber | selector가 필요한 조각이나 파생 값을 읽고 이전 선택 결과와 비교합니다. | useSelector는 기본적으로 선택 결과의 === 참조 동일성을 사용합니다. |
| 6 · UI render | Redux 업데이트로 선택 결과가 달라진 컴포넌트가 새 값을 화면에 반영합니다. | 부모 렌더 같은 별도 경로는 남으므로 Redux 구독만으로 모든 렌더가 결정되지는 않습니다. |
UI 사건
사용자 입력이나 비동기 완료가 변경 의도를 만듭니다.
Action을 dispatch
type과 필요한 payload로 “무슨 일이 있었는가”를 store에 보냅니다.Reducer 계산
현재 state와 action만으로 순수하고 불변인 다음 state를 만듭니다.
Store snapshot
root reducer 반환값을 저장하고 구독자에게 업데이트를 알립니다.
Selector 확인
useSelector가 선택 결과를 다시 계산하고 기본===비교를 수행합니다.UI 반영
선택 결과가 달라진 Redux 구독 경로가 새 화면을 렌더합니다. 별도 부모 렌더 경로는 남습니다.
Redux가 관리하는 state tree
일반적인 Redux 앱은 store 하나를 사용합니다. 컴포넌트 로컬 state, URL, 서버 캐시까지 모두 넣어야 한다는 뜻은 아닙니다.
변경 입구는 dispatch
store state를 직접 쓰지 않고 action을 dispatch합니다. 이 경계가 action 기반 관찰과 미들웨어 확장을 가능하게 합니다.
전이는 순수하고 불변
같은 state와 action에는 같은 결과를 내고 부수 효과는 밖에 둡니다. Redux Toolkit의 mutation처럼 보이는 문법도 이전 state 자체를 바꾸는 계약은 아닙니다.
action 이력은 Redux가 자동으로 영구 보관하는 데이터가 아닙니다. DevTools 연동과 모니터 또는 로깅을 구성하고 state·action을 관찰 가능한 직렬화 형태로 유지했을 때 전후 값을 디버깅 증거로 볼 수 있습니다.
UI 사건 또는 비동기 완료: 사용자 입력이나 middleware의 비동기 작업 완료가 변경 의도를 만듭니다.
Action dispatch: 컴포넌트나 middleware가 action을 store에 전달합니다.
Reducer 실행: store가 현재 state와 action으로 root reducer를 실행합니다.
새 snapshot 저장: reducer 반환값이 store의 현재 state가 되고 구독자에게 업데이트가 알려집니다.
Selector 비교: React-Redux 구독자는 필요한 값을 다시 선택하고 이전 선택 결과와 비교합니다.
UI 반영: 해당 Redux 구독 경로에서 선택 결과가 바뀌었다면 컴포넌트가 새 값으로 렌더됩니다.
비동기 작업과 부수 효과는 reducer 밖에서 수행한 뒤 필요한 action을 dispatch합니다.
Redux Toolkit과의 관계
Redux Toolkit은 현대 Redux 로직을 작성하는 공식 권장 경로입니다.
configureStore: root reducer, 표준 middleware, 기본 활성화된 Redux DevTools 연동을 포함한 권장 store 설정을 제공하며devTools옵션으로 연동을 제어할 수 있습니다.createSlice: action type·creator와 case reducer를 함께 만들고 Immer 기반 불변 갱신을 간결하게 작성하게 합니다.createAsyncThunk: 사용자 정의 Promise 작업에 대해pending,fulfilled,rejected생명주기 action을 만들고condition과AbortSignal기반 취소 지점을 제공합니다. 재시도·자동 중복 제거·cache 정책까지 제공하는 일반 서버 데이터 해법은 아닙니다.- RTK Query: Redux 앱에서 client data fetching과 cache를 관리할 때 공식 문서가 기본 접근으로 가르치는 도구입니다. 요청 상태와 cache 무효화를 제공하며, 서버 렌더나 route loader가 소유하는 데이터는 해당 프레임워크 경계를 따릅니다.
- React-Redux:
useSelector,useDispatch등으로 React 컴포넌트를 store에 연결하는 별도 공식 패키지입니다.
Principle → standard API → boundary
현대 Redux 코드는 Redux Toolkit을 표준 경로로 사용합니다. 각 API가 반복 코드를 줄여도 store에 둘 상태, 비동기 책임, 서버 캐시, React 렌더 경계까지 자동으로 결정해 주지는 않습니다.
| 역할 | 권장 API · 제공하는 것 | 직접 결정할 것 |
|---|---|---|
| Store | configureStoreroot reducer 결합, 표준 미들웨어와 기본 활성화된 Redux DevTools 연동을 포함한 권장 설정; devTools 옵션으로 제어 |
어떤 client state를 Redux가 소유할지와 slice 경계 |
| Action + reducer | createSliceaction type·creator와 case reducer 생성, Immer 기반의 불변 갱신 작성 경로 |
action 어휘, state shape, 순수한 전이 규칙과 불변 조건 |
| Custom async | createAsyncThunkPromise 생명주기 action과 condition·AbortSignal 취소 지점 제공 |
취소 시점·재시도·자동 중복 제거·캐시 정책; 일반 서버 캐시의 기본 해법은 아님 |
| Server data | RTK QueryRedux 앱의 client data fetching과 캐시, 요청 상태, 무효화 도구 |
서버 렌더·라우트 로더의 데이터 경계와 API의 권한·일관성 계약 |
| React binding | useSelectorReact-Redux가 store를 구독하고 selector 결과를 컴포넌트에 연결 |
선택 범위, memoization, 참조 안정성과 별도 부모 렌더 비용 |
Store · configureStore
권장 store 설정과 표준 미들웨어·기본 활성화된 DevTools 연동을 제공합니다. 연동 옵션과 Redux가 소유할 state 범위는 직접 정합니다.
Action + reducer · createSlice
action과 case reducer를 함께 만들고 Immer로 불변 갱신을 간결하게 씁니다. action 어휘와 전이 규칙은 설계 책임입니다.
Custom async · createAsyncThunk
Promise 생명주기 action과 condition·AbortSignal을 제공합니다. 취소 시점·재시도·중복 제거·캐시 정책은 직접 정합니다.
Server data · RTK Query
Redux 앱의 client data fetching과 캐시에 권장되는 기본 경로입니다. 서버 렌더와 라우트 데이터는 해당 프레임워크 경계를 따릅니다.
React binding · useSelector
React-Redux가 selector 결과를 화면에 연결합니다. 선택 범위와 참조 안정성, 부모 렌더는 별도로 판단합니다.
공유·빈도·복잡성·추적 가치
많은 화면이 같은 client state를 읽고, 갱신이 잦고, 전이 규칙이 복잡하며, 팀이 action 기반 추적과 도구의 이득을 얻을 때 검토합니다.
규모만으로 자동 선택하지 않는다
로컬 UI state는 컴포넌트에, 주소 상태는 URL에 둘 수 있습니다. Context+reducer는 Provider 트리의 공유 도구이며 외부 store·middleware·DevTools·selector 비교를 자동 제공하지 않습니다.
createStore는 학습과 기존 코드에서 볼 수 있지만, 새 Redux 로직은 공식 권장인 Redux Toolkit의 configureStore에서 시작합니다.
이 장에서는 Redux가 관리하는 state의 단일 store, dispatch를 통한 읽기 전용 변경 경계, 순수 reducer, selector 기반 구독을 살펴보았습니다.
Redux는 규모가 큰 앱의 자동 정답이 아니며 local UI state, URL, server cache와 Redux client state의 소유권을 먼저 나눠야 합니다.
다음 절에서는 useReducer와 Context를 결합한 구조를 살펴보고, 컴포넌트 state owner·reducer 계산 책임·Provider 공급 범위와 외부 store의 차이를 비교합니다.