icon
7장 : 상태 관리 입문

Redux 기초 개념 소개

리액트 생태계에서 가장 널리 사용되는 전역 상태 관리 라이브러리인 Redux의 기초 개념을 소개하겠습니다.

Redux는 대규모 애플리케이션에서 예측 가능한 상태 관리를 돕기 위해 고안된 강력한 도구입니다. 비록 최근에는 Context API와 useReducer의 조합이나 Recoil, Zustand 같은 다른 상태 관리 라이브러리들도 인기를 얻고 있지만, Redux의 핵심 개념은 여전히 많은 라이브러리의 기반이 되므로 반드시 이해해야 할 중요 지식입니다.


Redux는 왜 필요한가?

Context API와 useReducer의 조합만으로도 특정 수준까지는 전역 상태 관리가 가능합니다. 하지만 애플리케이션의 규모가 더욱 커지고, 관리해야 할 상태가 많아지며, 비동기 작업(서버 통신)이 복잡해질수록 다음과 같은 문제에 직면할 수 있습니다.

  • 상태 변화 추적의 어려움: 어떤 액션에 의해 어떤 상태가 어떻게 변했는지 파악하기 어려워집니다.
  • 상태 일관성 유지: 여러 컴포넌트가 동일한 상태를 동시에 변경하려고 할 때 충돌이 발생할 수 있습니다.
  • 디버깅의 복잡성: 상태 변경이 예측 불가능해지면 버그를 찾기 매우 어려워집니다.
  • 테스트의 어려움: 상태 로직과 컴포넌트 로직이 섞여 있을 때 테스트 코드를 작성하기가 까다로워집니다.

Redux는 이러한 문제들을 해결하기 위해 단일 진실 공급원(Single Source of Truth) 원칙을 바탕으로, 예측 가능한 상태 컨테이너를 제공합니다.


Redux의 세 가지 핵심 원칙

Redux는 세 가지 핵심 원칙을 기반으로 합니다.

  1. 단일 진실 공급원 (Single Source of Truth)

    • 애플리케이션의 모든 상태는 하나의 자바스크립트 객체로 된 하나의 스토어(Store) 안에 저장됩니다.
    • 이는 상태 변화를 예측 가능하게 하고 디버깅을 용이하게 합니다.
  2. 상태는 읽기 전용 (State is Read-only)

    • 상태를 직접 변경할 수 없습니다. 상태를 변경하려면 반드시 액션(Action) 이라는 평범한 자바스크립트 객체를 통해 변경 의도를 표현해야 합니다.
    • 액션은 상태 변경을 위한 유일한 방법이며, 이는 모든 상태 변경이 기록될 수 있도록 하여 디버깅을 돕고 상태 변화의 예측 가능성을 높입니다.
  3. 변경은 순수 함수로만 가능 (Changes are Made with Pure Functions)

    • 상태를 변경하는 로직은 리듀서(Reducer) 라는 순수 함수를 통해서만 이루어져야 합니다.
    • 리듀서는 이전 상태(previous state)와 액션(action)을 받아서 새로운 상태(new state) 를 반환합니다. 이 과정에서 이전 상태를 직접 변경해서는 안 됩니다(불변성 유지).
    • 순수 함수이므로 동일한 입력에 대해 항상 동일한 출력을 보장하여 테스트와 예측이 용이합니다.

Redux의 핵심 구성 요소

이 세 가지 원칙을 바탕으로 Redux는 다음의 주요 구성 요소들로 이루어져 있습니다.

  1. Store (스토어)

    • 애플리케이션의 전체 상태 트리를 저장하는 객체입니다. Redux 애플리케이션 당 하나의 스토어만 존재합니다.
    • 주요 역할
      • 애플리케이션의 상태를 가집니다.
      • dispatch(action)을 통해 상태를 업데이트할 수 있습니다.
      • getState()를 통해 현재 상태를 가져올 수 있습니다.
      • subscribe(listener)를 통해 상태 변화를 구독할 수 있습니다.
    • createStore (레거시) 또는 configureStore (Redux Toolkit) 함수를 사용하여 생성합니다.
  2. Action (액션)

    • 상태에 어떤 변화가 필요한지 나타내는 Plain JavaScript 객체입니다.
    • type 속성은 필수이며, 어떤 종류의 액션인지 식별합니다. (예: { type: 'INCREMENT' }, { type: 'ADD_TODO', payload: '새 할 일' })
    • payload 속성은 액션과 관련된 데이터를 포함할 수 있습니다.
    • 액션은 상태를 직접 변경하지 않고, 단지 "무슨 일이 일어났는지" 를 설명합니다.
  3. Reducer (리듀서)

    • 이전 상태(previous state)와 액션(action)을 받아서 다음 상태(next state)를 반환하는 순수 함수입니다.
    • reducer(state, action) 형태를 가집니다.
    • switch 문을 사용하여 액션 type에 따라 다른 상태 변경 로직을 수행합니다.
    • 절대 이전 상태를 직접 변경(mutate)해서는 안 되며, 항상 새로운 상태 객체를 반환해야 합니다 (불변성).
    • 비동기 로직이나 사이드 이펙트(Side Effects) 는 리듀서 내부에서 수행해서는 안 됩니다. (이것은 미들웨어의 역할)
  4. Dispatch (디스패치)

    • 액션을 스토어에 "보내는(dispatch)" 함수입니다.
    • 컴포넌트에서 상태 변경이 필요할 때 dispatch(action)을 호출합니다.
    • dispatch를 통해 액션이 전달되면, 스토어는 해당 액션을 리듀서에게 전달하여 상태를 업데이트합니다.

Redux의 데이터 흐름 (단방향)

Redux의 데이터 흐름은 다음과 같은 예측 가능한 단방향 사이클을 따릅니다.

  1. UI 상호작용: 사용자가 UI와 상호작용하거나(예: 버튼 클릭), 비동기 작업(API 호출)이 완료됩니다.
  2. Action Dispatch: 컴포넌트(또는 미들웨어)는 상태 변경이 필요하다는 의도를 담은 액션(Action)dispatch 함수를 통해 스토어에 전달합니다.
  3. Reducer 실행: 스토어는 전달받은 액션과 현재 상태를 리듀서(Reducer) 함수에게 전달합니다.
  4. State Update: 리듀서는 액션 타입에 따라 새로운 상태를 계산하여 스토어에 반환합니다. 이때 상태 불변성을 지켜야 합니다.
  5. UI Render: 스토어의 상태가 변경되면, 이 상태를 구독하고 있는 컴포넌트들이 변경된 상태를 기반으로 다시 렌더링됩니다.
       +--------------------+      +--------------------+
       |      React App     |      |       Action       |
       |  (Component)       |      | (Plain JS Object)  |
       +----------+---------+      +----------+---------+
                  |                             ^
                  | 2. dispatch(action)         |
                  v                             |
       +--------------------+       1. User Interaction / Async Op
       |       Store        <-----------------+
       | (Single Source)    |
       +----------+---------+
                  | 3. (state, action) -> reducer
                  v
       +--------------------+
       |      Reducer       |
       | (Pure Function)    |
       +----------+---------+
                  | 4. Return new state
                  v
       +--------------------+
       |       State        |
       |    (Updated)       |
       +----------+---------+
                  | 5. Subscribe & Re-render
                  v
       +--------------------+
       |      React App     |
       |  (Updated UI)      |
       +--------------------+

Redux Toolkit과의 관계

과거 Redux는 보일러플레이트(반복적인 코드)가 많다는 비판을 받았습니다. 이러한 단점을 극복하고 개발 생산성을 높이기 위해 공식적으로 권장되는 도구인 Redux Toolkit이 등장했습니다.

Redux Toolkit은 Redux 개발을 간소화하는 유틸리티 세트입니다. 주요 특징:

  • configureStore: 스토어 설정을 단순화합니다.
  • createSlice: 액션 타입, 액션 생성자, 리듀서를 한 번에 정의할 수 있게 하여 보일러플레이트를 대폭 줄여줍니다.
  • createAsyncThunk: 비동기 로직 처리를 쉽게 할 수 있도록 돕습니다.

오늘날 Redux를 사용한다면 거의 항상 Redux Toolkit과 함께 사용합니다. Redux의 기초 개념은 Redux Toolkit을 이해하는 데 필수적인 배경 지식입니다.


"Redux 기초 개념 소개"는 여기까지입니다. 이 장에서는 왜 Redux와 같은 전역 상태 관리 라이브러리가 필요한지 그 배경을 이해하고, Redux의 세 가지 핵심 원칙 (단일 진실 공급원, 상태는 읽기 전용, 변경은 순수 함수로만 가능)을 학습했습니다. 또한 Store, Action, Reducer, Dispatch와 같은 Redux의 주요 구성 요소와 예측 가능한 단방향 데이터 흐름을 살펴보았습니다.

이제 여러분은 Redux라는 거대한 상태 관리 프레임워크의 기본적인 청사진을 갖게 되었습니다. 다음 장에서는 useReducer와 Context API를 결합하여 Redux와 유사한 구조를 직접 구현해 보면서, Redux의 개념들이 어떻게 실제 코드에 반영되는지 더욱 구체적으로 이해하는 시간을 갖겠습니다. 이 실습은 Redux Toolkit을 배우기 전 훌륭한 워밍업이 될 것입니다.