Redux 핵심, 1부: Redux 개요
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
- Redux란 무엇이며 사용해야 하는 이유
- Redux 애플리케이션을 구성하는 기본 요소들
소개
Redux 핵심 튜토리얼에 오신 것을 환영합니다! 이 튜토리얼은 Redux 사용의 핵심 개념, 원칙, 패턴을 소개합니다. 학습을 마치면 Redux 애플리케이션을 구성하는 다양한 요소들, Redux 사용 시 데이터 흐름 방식, 그리고 Redux 애플리케이션 구축을 위한 권장 패턴을 이해하게 될 것입니다.
튜토리얼 1부에서는 동작하는 최소한의 Redux 앱 예시를 간단히 살펴보며 구성 요소들을 확인하고, 2부: Redux 개념과 데이터 흐름에서는 이러한 요소들과 Redux 애플리케이션의 데이터 흐름을 더 자세히 살펴볼 것입니다.
3부: 상태, 액션, 리듀서부터는 이러한 지식을 바탕으로 구성 요소들이 어떻게 조화를 이루는지 보여주는 작은 예제 앱을 구축하며 실제로 Redux가 어떻게 작동하는지 설명할 것입니다. 정확히 어떤 일이 발생하는지 이해할 수 있도록 "수동으로" 동작하는 예제 앱을 구축한 후, 일반적으로 Redux와 함께 사용되는 표준 패턴과 추상화에 대해 이야기할 것입니다. 마지막으로 이러한 저수준 예시들이 실제 애플리케이션에서 권장되는 고수준 패턴으로 어떻게 전환되는지 확인할 것입니다.
튜토리얼 활용 방법
이 튜토리얼은 "Redux가 어떻게 작동하는지" 그리고 왜 이러한 패턴들이 존재하는지 가르칠 것입니다.
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
참고: 이 튜토리얼은 Redux의 원리와 개념을 설명하기 위해 의도적으로 오래된 스타일의 Redux 로직 패턴을 보여줍니다. 이 패턴은 오늘날 우리가 Redux 앱 구축을 위한 올바른 접근법으로 가르치는 '현대적인 Redux(modern Redux)' 패턴(Redux Toolkit 사용)보다 더 많은 코드가 필요합니다. 이 튜토리얼은 프로덕션 환경에서 바로 사용할 수 있는 프로젝트가 아닙니다.
'현대적인 Redux'와 Redux Toolkit 사용법을 배우려면 다음 페이지를 참조하세요:
- 전체 "Redux Essentials" 튜토리얼: 실제 애플리케이션을 위한 Redux Toolkit을 사용한 "올바른 Redux 사용법"을 가르칩니다. 모든 Redux 학습자는 'Essentials' 튜토리얼을 필독할 것을 권장합니다!
- Redux Fundamentals, Part 8: Redux Toolkit을 사용한 현대적인 Redux: 이전 섹션의 저수준 예제를 현대적인 Redux Toolkit 방식으로 변환하는 방법을 보여줍니다
모든 것이 어떻게 조화를 이루는지 이해한 후에는 Redux Toolkit을 사용해 작업을 단순화하는 방법을 살펴볼 것입니다. Redux Toolkit은 Redux로 프로덕션 앱을 구축할 때 권장되는 방식이며, 이 튜토리얼 전체에서 살펴볼 모든 개념을 기반으로 구축되었습니다. 여기서 다루는 핵심 개념을 이해하면 Redux Toolkit을 더 효율적으로 사용하는 방법을 알게 될 것입니다.
초보자도 이해하기 쉽게 설명하려 노력했지만, Redux 자체 설명에 집중하기 위해 여러분이 이미 알고 있을 것이라고 가정한 몇 가지 사항이 있습니다. 이 튜토리얼은 여러분이 다음 사항들을 알고 있다고 가정합니다:
- HTML & CSS에 대한 이해
- ES2015 구문 및 기능에 대한 숙지
- 배열 및 객체 전개 연산자 이해
- React 용어에 대한 지식: JSX, 함수 컴포넌트, Props, 상태(State), 훅(Hooks)
- 비동기 JavaScript 및 HTTP 요청 생성에 대한 이해
해당 주제들에 익숙하지 않다면, 먼저 충분히 숙지한 후 Redux 학습을 재개할 것을 권장합니다. 준비되면 다시 만나요!
마지막으로 브라우저에 React 및 Redux DevTools 확장 프로그램이 설치되어 있는지 확인하세요:
-
React DevTools 확장 프로그램:
-
Redux DevTools 확장 프로그램:
Redux란?
"Redux"가 무엇인지 기본적인 이해가 필요합니다. 어떤 역할을 하며 어떤 문제를 해결해주나요? 왜 사용해야 할까요?
Redux는 전역 애플리케이션 상태 관리를 위한 패턴과 라이브러리로, UI가 발생한 사건을 설명하는 "액션" 이벤트를 트리거하면 별도의 업데이트 로직인 "리듀서"가 응답으로 상태를 업데이트합니다. 애플리케이션 전체에서 사용해야 하는 상태를 위한 중앙 저장소 역할을 하며, 상태가 예측 가능한 방식으로만 업데이트되도록 보장하는 규칙을 제공합니다.
왜 Redux를 사용해야 하나요?
Redux는 애플리케이션의 여러 부분에서 필요한 "전역" 상태 관리를 도와줍니다.
Redux가 제공하는 패턴과 도구는 애플리케이션 상태가 언제, 어디서, 왜, 어떻게 업데이트되는지, 그리고 변경 발생 시 애플리케이션 로직이 어떻게 동작할지 이해하기 쉽게 합니다. Redux는 예측 가능하고 테스트 가능한 코드 작성을 유도하여 애플리케이션이 예상대로 작동할 것이라는 확신을 줍니다.
언제 Redux를 사용해야 할까요?
Redux는 공유 상태 관리를 도와주지만, 다른 도구와 마찬가지로 장단점이 있습니다. 배워야 할 개념이 더 많고 작성해야 할 코드도 더 많습니다. 또한 코드에 약간의 간접성을 추가하며 특정 제약을 따르도록 요구합니다. 이는 단기 생산성과 장기 생산성 사이의 균형 문제입니다.
Redux는 다음과 같은 경우에 더 유용합니다:
-
애플리케이션의 여러 부분에서 필요한 대량의 상태가 존재할 때
-
애플리케이션 상태가 시간에 따라 자주 업데이트될 때
-
상태 업데이트 로직이 복잡할 때
-
중간 규모 이상의 코드베이스를 가진 애플리케이션이며 여러 사람이 함께 작업할 때
모든 앱이 Redux를 필요로 하는 것은 아닙니다. 여러분이 구축하려는 앱의 종류를 생각해보고, 해결하려는 문제에 가장 적합한 도구가 무엇인지 결정하는 시간을 가지세요.
Redux가 여러분의 앱에 적합한 선택인지 확실하지 않다면, 다음 자료들이 추가적인 지침을 제공합니다:
Redux 라이브러리와 도구
Redux는 작고 독립적인 JS 라이브러리입니다. 그러나 일반적으로 다음과 같은 여러 패키지와 함께 사용됩니다:
Redux Toolkit
Redux Toolkit은 Redux 로직 작성을 위해 권장하는 접근 방식입니다. Redux 앱 구축에 필수적이라고 생각하는 패키지와 함수들을 포함하고 있습니다. Redux Toolkit은 권장하는 모범 사례를 내장하고, 대부분의 Redux 작업을 단순화하며, 일반적인 실수를 방지하고, Redux 애플리케이션 작성을 더 쉽게 만듭니다.
React-Redux
Redux는 어떤 UI 프레임워크와도 통합될 수 있으며, 가장 빈번하게 React와 함께 사용됩니다. React-Redux는 React 컴포넌트가 상태 일부를 읽고 스토어를 업데이트하기 위한 액션을 디스패치하여 Redux 스토어와 상호작용할 수 있도록 하는 공식 패키지입니다.
Redux DevTools 확장 프로그램
Redux DevTools 확장 프로그램은 시간에 따른 Redux 스토어의 상태 변경 이력을 보여줍니다. 이를 통해 "시간 여행 디버깅"과 같은 강력한 기법을 포함하여 애플리케이션을 효과적으로 디버깅할 수 있습니다.
Redux 기초
이제 Redux가 무엇인지 알았으니, Redux 애플리케이션을 구성하는 요소들과 작동 방식을 간단히 살펴보겠습니다.
이 페이지의 나머지 설명은 Redux 코어 라이브러리(redux 패키지)에만 초점을 맞춥니다. 튜토리얼을 진행하며 다른 Redux 관련 패키지들에 대해서도 다룰 것입니다.
Redux 스토어
모든 Redux 애플리케이션의 중심에는 스토어(store) 가 있습니다. "스토어"는 애플리케이션의 전역 상태(state) 를 보관하는 컨테이너입니다.
스토어는 일반 전역 객체와 구별되는 몇 가지 특별한 함수와 기능을 가진 JavaScript 객체입니다:
-
Redux 스토어 내부의 상태는 직접 수정하거나 변경해서는 안 됩니다.
-
대신, 상태를 업데이트하는 유일한 방법은 애플리케이션에서 "발생한 사건"을 설명하는 일반 액션(action) 객체를 생성한 다음, 해당 액션을 스토어에 디스패치(dispatch) 하여 무슨 일이 발생했는지 알리는 것입니다.
-
액션이 디스패치되면, 스토어는 루트 리듀서(reducer) 함수를 실행하여 이전 상태와 액션을 기반으로 새로운 상태를 계산합니다.
-
마지막으로, 스토어는 상태가 업데이트되었음을 구독자(subscribers) 에게 알려 UI가 새로운 데이터로 업데이트될 수 있도록 합니다.
Redux 코어 예제 앱
Redux 앱의 최소한의 동작 예제인 간단한 카운터 애플리케이션을 살펴보겠습니다:
Redux는 의존성이 없는 독립형 JS 라이브러리이므로, 이 예제는 Redux 라이브러리를 위한 단일 스크립트 태그만 로드하여 작성되었으며 UI에는 기본 JS와 HTML을 사용합니다. 실제로는 일반적으로 NPM에서 Redux 패키지를 설치하여 사용하며, UI는 React와 같은 라이브러리로 생성합니다.
5부: UI와 React에서는 Redux와 React를 함께 사용하는 방법을 보여줍니다.
이 예제를 개별 부분으로 나누어 어떤 일이 발생하는지 살펴보겠습니다.
상태(State), 액션(Actions), 리듀서(Reducers)
애플리케이션을 설명하기 위해 초기 상태(state) 값을 정의하는 것부터 시작합니다:
// Define an initial state value for the app
const initialState = {
value: 0
}
이 앱에서는 카운터의 현재 값을 나타내는 단일 숫자를 추적합니다.
일반적으로 Redux 앱은 상태의 루트 조각으로 JS 객체를 가지며, 그 객체 내부에 다른 값들이 포함됩니다.
다음으로 리듀서(reducer) 함수를 정의합니다. 리듀서는 두 개의 인자, 즉 현재 state와 발생한 사건을 설명하는 action 객체를 받습니다. Redux 앱이 시작될 때는 아직 상태가 없으므로, 이 리듀서의 기본값으로 initialState를 제공합니다:
// Create a "reducer" function that determines what the new state
// should be when something happens in the app
function counterReducer(state = initialState, action) {
// Reducers usually look at the type of action that happened
// to decide how to update the state
switch (action.type) {
case 'counter/incremented':
return { ...state, value: state.value + 1 }
case 'counter/decremented':
return { ...state, value: state.value - 1 }
default:
// If the reducer doesn't care about this action type,
// return the existing state unchanged
return state
}
}
액션 객체는 항상 type 필드를 가지며, 이는 액션의 고유한 이름 역할을 하는 여러분이 제공한 문자열입니다. type은 가독성 좋은 이름이어야 코드를 보는 누구나 그 의미를 이해할 수 있습니다. 이 예제에서는 액션 타입의 첫 부분으로 'counter'를 사용하고, 두 번째 부분은 "무슨 일이 발생했는지"에 대한 설명입니다. 여기서 'counter'는 'incremented'(증가)되었으므로, 액션 타입을 'counter/incremented'로 작성합니다.
액션의 타입에 따라, 우리는 새로운 state 결과가 될 완전히 새로운 객체를 반환하거나, 변경 사항이 없으면 기존 state 객체를 반환해야 합니다. 주의할 점은 기존 상태를 복사하고 복사본을 업데이트하여 상태를 불변적으로(immutably) 업데이트하며, 원본 객체를 직접 수정하지 않는다는 것입니다.
스토어(Store)
이제 리듀서 함수가 있으므로, Redux 라이브러리의 createStore API를 호출하여 스토어(store) 인스턴스를 생성할 수 있습니다.
// Create a new Redux store with the `createStore` function,
// and use the `counterReducer` for the update logic
const store = Redux.createStore(counterReducer)
리듀서 함수를 createStore에 전달하면, 이 함수는 초기 상태를 생성하고 향후 업데이트를 계산하기 위해 리듀서 함수를 사용합니다.
UI
모든 애플리케이션에서 사용자 인터페이스(UI)는 기존 상태를 화면에 표시합니다. 사용자가 어떤 동작을 하면, 앱은 데이터를 업데이트하고 그 값으로 UI를 다시 그립니다.
// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')
// Whenever the store state changes, update the UI by
// reading the latest store state and showing new data
function render() {
const state = store.getState()
valueEl.innerHTML = state.value.toString()
}
// Update the UI with the initial data
render()
// And subscribe to redraw whenever the data changes in the future
store.subscribe(render)
이 간단한 예제에서는 UI로 몇 가지 기본 HTML 요소만 사용하며, 현재 값을 표시하는 단일 <div>를 사용합니다.
따라서, store.getState() 메서드를 사용해 Redux 스토어에서 최신 상태를 가져오는 방법을 알고, 그 값을 가져와 UI를 업데이트하여 표시하는 함수를 작성합니다.
Redux 스토어를 사용하면 store.subscribe()를 호출하여 스토어가 업데이트될 때마다 호출되는 구독자 콜백 함수를 전달할 수 있습니다. 따라서 render 함수를 구독자로 전달하면 스토어가 업데이트될 때마다 최신 값으로 UI를 업데이트할 수 있습니다.
Redux 자체는 어디서든 사용할 수 있는 독립형 라이브러리입니다. 이는 어떤 UI 레이어와도 함께 사용될 수 있음을 의미합니다.
액션 디스패치하기
마지막으로 사용자 입력에 응답하기 위해 발생한 상황을 설명하는 액션 객체를 생성하고 이를 스토어에 디스패치해야 합니다. store.dispatch(action)를 호출하면 스토어는 리듀서를 실행하고, 업데이트된 상태를 계산하며, 구독자를 실행하여 UI를 업데이트합니다.
// Handle user inputs by "dispatching" action objects,
// which should describe "what happened" in the app
document.getElementById('increment').addEventListener('click', function () {
store.dispatch({ type: 'counter/incremented' })
})
document.getElementById('decrement').addEventListener('click', function () {
store.dispatch({ type: 'counter/decremented' })
})
document
.getElementById('incrementIfOdd')
.addEventListener('click', function () {
// We can write logic to decide what to do based on the state
if (store.getState().value % 2 !== 0) {
store.dispatch({ type: 'counter/incremented' })
}
})
document
.getElementById('incrementAsync')
.addEventListener('click', function () {
// We can also write async logic that interacts with the store
setTimeout(function () {
store.dispatch({ type: 'counter/incremented' })
}, 1000)
})
여기서는 현재 카운터 값에 1을 더하거나 빼도록 리듀서를 동작시키는 액션을 디스패치합니다.
특정 조건이 참일 때만 액션을 디스패치하는 코드를 작성하거나, 지연 후에 액션을 디스패치하는 비동기 코드를 작성할 수도 있습니다.
데이터 흐름
다음 다이어그램으로 Redux 앱의 데이터 흐름을 요약할 수 있습니다. 이는 다음과 같은 방식을 나타냅니다:
-
클릭 같은 사용자 상호작용에 대한 응답으로 액션이 디스패치됩니다
-
스토어는 리듀서 함수를 실행하여 새로운 상태를 계산합니다
-
UI는 새로운 값을 표시하기 위해 새로운 상태를 읽습니다
(아직 이 내용이 명확하지 않더라도 걱정하지 마세요! 튜토리얼의 나머지 부분을 진행하면서 이 그림을 기억하면 각 부분이 어떻게 조화를 이루는지 알게 될 것입니다.)

학습 내용 요약
카운터 예제는 작지만 실제 Redux 앱의 모든 작동 부분을 보여줍니다. 다음 섹션에서 다룰 모든 내용은 이러한 기본 구성 요소를 확장한 것입니다.
이를 염두에 두고 지금까지 배운 내용을 복습해 봅시다:
- Redux는 전역 애플리케이션 상태 관리를 위한 라이브러리입니다
- Redux는 일반적으로 React-Redux 라이브러리와 함께 사용되어 Redux와 React 통합
- Redux Toolkit은 Redux 로직 작성을 위한 표준 방식
- Redux 업데이트 패턴은 "무슨 일이 발생했는지"와 "상태가 어떻게 변하는지"를 분리합니다
- 액션(Actions) 은
type필드를 가진 일반 객체로 앱에서 "무슨 일이 발생했는지" 설명 - 리듀서(Reducers) 는 이전 상태와 액션을 기반으로 새 상태 값을 계산하는 함수
- Redux 스토어(store) 는 액션이 디스패치(dispatched) 될 때마다 루트 리듀서 실행
- 액션(Actions) 은
- Redux는 "단방향 데이터 흐름" 앱 구조를 사용합니다
- 상태는 특정 시점의 앱 상태를 나타내며 UI는 이 상태를 기반으로 렌더링
- 앱에서 이벤트 발생 시:
- UI가 액션 디스패치
- 스토어가 리듀서를 실행하고 발생한 사건에 따라 상태 업데이트
- 스토어가 상태 변경을 UI에 알림
- UI가 새 상태를 기반으로 리렌더링
다음 단계
이제 Redux 앱의 기본 구성 요소가 무엇인지 알았으니, 2부: Redux 개념과 데이터 흐름으로 넘어가서 Redux 앱에서 데이터가 어떻게 흐르는지 더 자세히 살펴보겠습니다.