WARNING
이 글은 Mark Erikson의 "Why React Context is Not a 'State Management' Tool (and Why It Doesn't Replace Redux)"을 기반으로 번역 및 보완한 글입니다. 저작권은 원문 저자에게 있습니다.
소개
"Context vs Redux"는 React의 현재 Context API가 출시된 이후 지금까지 React 커뮤니티에서 가장 많이 논쟁되는 주제 중 하나입니다.
아쉽게도 이 “논쟁”의 대부분은 두 도구의 목적과 사용 사례에 대한 혼란에서 비롯됩니다. 저는 인터넷 곳곳에서 수백 번이나 Context와 Redux에 대한 다양한 질문에 답해왔습니다. 하지만 혼란은 오히려 더 커지고 있습니다.
이 주제에 대한 질문이 너무 많이 반복되기 때문에, 이번 글을 통해 Context와 Redux가 실제로 무엇인지, 어떻게 쓰는 게 맞는지, 서로 어떻게 다르며, 언제 어떤 것을 사용해야 하는지 명확하게 설명해보겠습니다.
TL;DR (요약)
- Context와 Redux는 같은 도구인가?
- 아니요. 역할도 다르고, 해결하려는 문제도 다릅니다.
- Context는 “상태 관리 도구”인가?
- 아니요. 단지 값을 전달하는 의존성 주입(Dependency Injection) 메커니즘일 뿐입니다.
- 상태 관리 자체는 useState나 useReducer 같은 React 기능이 담당합니다.
- Context + useReducer가 Redux를 대체할 수 있는가?
- 비슷한 구조는 맞지만, 기능 차이가 큽니다. 특히 규모가 커질수록 한계가 드러납니다.
- 언제 Context를 써야 할까?
- Prop drilling 방지가 필요할 때 (같은 값을 여러 자식에게 내려주고 싶을 때)
- 언제 Context + useReducer 조합을?
- 특정 UI 영역 안에서 적당히 복잡한 상태를 관리할 때
- 언제 Redux를 선택해야 할까?
- 아래 상황이 하나라도 해당하면 Redux가 훨씬 낫습니다:
- 전역에서 쓰이는 상태가 많다
- 상태 변경이 자주 일어난다
- 로직이 복잡하다
- 코드베이스가 크고 협업 인원이 많다
- 상태 변화 "추적(언제/왜/어떻게)"이 필요하다
- 비동기/사이드이펙트/상태 영속화 등 고급 기능이 필요하다
- 아래 상황이 하나라도 해당하면 Redux가 훨씬 낫습니다:
Context 와 Redux 를 이해해야하는 이유
기술을 제대로 사용하려면 먼저
- 이 도구의 목적이 무엇인지
- 어떤 문제를 해결하려고 만든 것인지
- 지금 내 앱이 어떤 문제를 해결해야 하는지
이걸 정확이 알아야 합니다.
"남들이 쓰니깐", "유명하니까" 가 아니라 내 상황에 최적화된 도구를 선택해야 하죠.
Context vs Redux 논쟁이 사라지지 않는 이유도 대부분 둘이 해결하는 문제가 다르다는 사실을 모른 채 비교하기 때문입니다.
React Context 란? - "상태관리" 가 아니다
React 공식문서에서의 Context 정의는 다음과 같습니다:
Context 는 컴포넌트 트리를 통해 데이터를 전달할 수 있는 방법을 제공합니다.
모든 레벨에서 수동으로 props 를 전달할 필요 없이 데이터를 전달 할 수 있습니다.
일반적인 React 앱에서는 데이터가 props 를 통해 부모에서 자식으로 전달되지만, 어떤 종류의 props 들은 이 방식으로 처리하기가 번거로울 수 있습니다. (예. 테마)
Context 는 이러한 값들을 트리의 모든 레벨을 통해 명시적으로 props 를 전달하지 않고도 컴포넌트들 사이에서 공유할 수 있는 방법을 제공합니다.
여기에는 "값을 관리(managing) 한다" 는 내용은 전혀 없다는 점이 중요합니다 - 오직 값을 전달하고, 공유하는것에 대해서만 말하고 있습니다.
현재의 React Context API (React.createContext()) 는 React 16.3에 처음 도입되었습니다.
많은 컴포넌트가 성능 최적화를 위해 shouldComponentUpdate 에 의존했기 때문에, 이것은 단순한 데이터를 전달하는데 어려움을 겪던 기존의 context 기능을 대체하기 위해 만들어졌습니다.
createContext() 는 이 문제를 해결하기 위해 설계되었으며, 따라서 중간의 컴포넌트가 렌더링을 건너뛰더라도 어떤 값의 업데이트라도 자식 컴포넌트에서 접근할 수 있도록 해줍니다.
🧐 Legacy Context 는 어떻게 처리했길래 ...
React 16.3 이전의 legacy context 는 "숨겨진 props 전달" 처럼 동작했습니다.
부모가 context 를 제공하면, 깊은 자식이 그 값을 몰래 가져다 쓰는 형태였습니다.
class Root extends React.Component {
state = { isDarkMode: true };
switchTheme = () => {
this.setState((state) => ({ isDarkMode: !state.isDarkMode }));
};
getChildContext() {
return { isDarkMode: this.state.isDarkMode };
}
render() {
return <Parent/>;
}
}
class Parent extends React.Component {
render() {
return <Child/>;
}
}
class Child extends React.Component {
render() {
const {isDarkMode} = this.context;
return <p>다크모드 : {isDarkMode}</p>;
}
}하지만 중간에 shouldComponentUpdate 에 의해서 Parent 컴포넌트가 렌더링을 건너뛰면, Child 컴포넌트는 isDarkMode 의 변경사항을 감지하지 못했습니다.
class Parent extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
return <Child/>;
}
}React 16.3 이후의 Context API 는 어디서든 업데이트가 발생하면 중간 컴포넌트가 렌더링을 건너뛰더라도 자식 컴포넌트가 최신 값을 받을 수 있도록 개선되었습니다.
즉, ❗️ Legacy Context 는 절대 상태공유에 쓰일 수 없었습니다. ❗️
Context 사용하기
React Context 를 사용하려면 몇가지 단계가 필요합니다:
const MyContext = React.createContext()로 Context 객체를 생성합니다.- 부모 컴포넌트에서
<MyContext.Provider value={...}>로 Context 값을 제공합니다. - 그런 다음, 해당 Provider 내부에 중첩된 어느 컴포넌트에서든
const value = useContext(MyContext)를 사용하여 Context 값을 읽을 수 있습니다. - 부모 컴포넌트가 재렌더링되며 Context Provider 에 새로운 참조값을 전달하게 되면, 해당 Context 값을 읽는 모든 컴포넌트들은 강제로 재렌더링됩니다.
일반적으로 Context 에 전달되는 값은 아래와 같은 방식으로 React state 에서 가져온 값입니다.
function ParentComponent() {
const [counter, setCounter] = useState(0);
const contextValue = { counter, setCounter };
return (
<MyContext.Provider value={contextValue}>
<SomeChildComponent />
</MyContext.Provider>
);
}자식 컴포넌트는 useContext 를 호출해서 값을 읽을 수 있습니다.
function NestedChildComponent() {
const { counter, setCounter } = useContext(MyContext);
}Context 의 목적과 사용사례
위 내용을 기반으로 보면 Context 는 실제로 어떠한것도 관리하지 않습니다.
다만 Context 는 마치 파이프나 웜홀과 같습니다.
<MyContext.Provider> 를 사용하여 어떤 값을 그 파이프의 윗 부분에 넣으면, 그 값은 파이프를 따라 내려가서 useContext(MyContext) 를 호출하는 컴포넌트에서 읽을 수 있게 됩니다.
따라서 Context 를 사용하는 주 목적은 Prop Drilling 방지 에 있습니다.
값이 필요한 컴포넌드들이 트리 깊숙한 곳에 있다고 해도, 레벨마다 props 를 명시적으로 전달하지 않고도, <MyContext.Provider> 내부에 있는 모든 컴포넌트는 useContext(MyContext) 를 통해 필요할 때 값을 가져올 수 있습니다.
이는 불필요한 props 전달 로직을 작성하지 않아도 되므로 코드를 단순화합니다.
Redux 란?
비교를 위해 Redux 문서의 Redux Essentials 튜토리얼에 있는 설명을 살펴보겠습니다.
Redux 는
action이라 불리는 이벤트를 사용하여 애플리케이션 상태를 관리하고 업데이트하기 위한 패턴이자 라이브러리 입니다.
애플리케이션 전체에서 사용될 필요가 잇는 상태를 중앙 집중화된 저장소 (store) 에 보관하며, 상태가 예측 가능한 방식으로만 업데이트될 수 있도록 규칙을 제공합니다.
Redux 는 애플리케이션의 여러 부분에서 필요로 하는 전역 상태를 관리하는데 도움을 줍니다.
Redux 가 제공하는 패턴과 도구들은 애플리케이션의 상태가 언제, 어디서, 왜, 어떻게 업데이트 되는지, 그리고 그 변경이 발생했을 때 애플리케이션 로직이 어떻게 동작할지를 더 쉽게 이해할 수 있도록 합니다.
이 설명에서
- 명확히 상태관리 를 한다고 언급하고 있으며,
- Redux 의 목적은 상태가 시간이 지남에 따라 어떻게 변화하는지 이해하도록 돕는 것입니다.
역사적으로 Redux 는 2014년 React 가 출시된지 1년 후에 Facebook 이 제안했던 Flux 아키텍쳐 를 구현하기 위해 처음 만들어졌습니다.
그 발표 이후 커뮤니티는 다양한 방식의 Flux 개념을 적용한 수십 개의 라이브러리를 만들어 냈습니다.
Redux 는 2015년에 등장했고, 가장 뛰어난 설계, 사람들이 해결하고자 했던 문제와의 높은 적합성, React 와의 뛰어난 호환성 덕분에 "Flux 전쟁" 에서 빠르게 승리했습니다.
아키텍쳐적으로 Redux 는 함수형 프로그래밍 원칙을 강조합니다.
가능한 많은 코드를 예측가능한 Reducer 함수로 작성하도록 하고, "어떤 이벤트가 발생했는가?" 라는 개념과 "그 이벤트가 발생했을 때 상태가 어떻게 업데이트되는가?" 라는 로직을 분리해 둡니다.
또한 Redux 는 미들웨어(middleware) 를 사용하여 사이드 이펙트 처리를 포함한 Redux 저장소의 기능을 확장할 수 있도록 설계되었습니다.
Redux 는 또한 Redux Devtools 를 제공합니다. 애플리케이션에서 시간이 지남에 따라 dispatch 된 액션의 기록과 상태 변화의 이력을 확인할 수 있습니다.
Redux 와 React
Redux 자체는 UI 라이브러리에 종속되지 않습니다. React Angular Vue 등 어떤 UI 와도 함께 사용할 수 있으며, 심지어 UI 없이도 사용할 수 있습니다.
그렇긴 하지만, Redux 는 대부분 React 와 함께 사용됩니다. react-redux 라이브러리는 공식 UI 바인딩 레이어로서, React 컴포넌트가 Redux 상태 값을 읽고 액션을 디스패치하여 Redux 스토어와 상호작용할 수 있게 해줍니다.
그래서 대부분 사람들이 Redux 라고 말할 때 실제로는 Redux Store + react-redux 라이브러리를 함께 사용하는 것을 의미합니다.
react-redux 는 애플리케이션 내 어떤 React 컴포넌트라도 Redux 스토어와 통신할 수 있도록 해줍니다.
이는 react-redux 가 내부적으로 Context 를 사용하기 때문에 가능합니다.
하지만! 여기서 중요한 점은 react-redux 가 현재 상태 값을 전달하지 않고, ‼️ redux store 인스턴스 ‼️ 만 전달합니다.
이는 위에서 언급했던 것 처럼, Context 를 의존성 주입 (Dependency Injection) 용도로 사용하는 좋은 예시입니다.
Redux 에 연결된 React 컴포넌트는 Redux 스토어와 상호작용해야 한다는 것은 알고 있지만, 그 컴포넌트를 정의할 때 어떤 redux 스토어인지는 알 필요도 없고 신경쓸 필요도 없습니다.
실제 Redux Store 는 <Provider/> 컴포넌트를 사용하여 런타임에 트리에 주입됩니다.
이러한 이유로 react-redux 는 내부적으로 Context 를 사용하기 때문에, prop-drilling 을 방지하는 데에도 사용할 수 있습니다.
직접 <MyContext.Provider> 에 새로운 값을 넣는 대신, 그 데이터를 Redux 스토어에 넣고 어디서든 접근할 수 있습니다.
React-Redux 의 목적과 사용 사례
Redux를 사용하는 주된 목적은 Redux 문서에 잘 나와 있습니다.
Redux 가 제공하는 패턴과 도구들은 애플리케이션의 상태가 언제, 어디서, 왜, 어떻게 업데이트 되는지, 그리고 그런 변화가 발생했을 때 애플리케이션 로직이 어떻게 동작할지를 이해하기 쉽게 만들어줍니다.
Redux 를 사용하고 싶을 만한 추가적인 이유들도 있습니다.
그 중 하나가 바로 prop drilling 방지 입니다.
많은 사람들은 초기부터 prop drilling 을 방지하기 위해 Redux 를 사용했습니다.
React 의 Legacy Context 는 고장나 있었고, react-redux는 제대로 동작했기 때문입니다.
또한 다음과 같은 이유들로 redux 를 사용할 수 있습니다
- 상태 관리 로직을 UI 레이어와 완전히 분리하고 싶을 때
- AngularJS 에서 React 로 이관중인 애플리케이션 처럼 서로 다른 UI 레이어 간에 상태 관리 로직을 공유하고 싶을 때
- action 이 dispatch 될 때 middleware 의 강력한 기능으로 추가적인 로직을 실행하고 싶을 때
- redux 상태의 일부를 영구적으로 저장 하고 싶을 때
- 재현 가능한 (replayable) 버그 리포트를 가능하게 하고 싶을 때
- 개발 중에 로직과 UI 를 더 빠르게 디버깅하고 싶을 때
Dan Abramov 는 2016년에 작성한 글 You Might Not Need Redux 에서 이러한 사용 사례들 중 여러 가지를 언급했습니다.
왜 Context 는 "상태 관리 도구" 가 아닌가?
State(상태) 란 애플리케이션의 동작을 설명하는 어떠한 데이터입니다.
상태를 서버 상태, 위치 상태, 통신 상태 같은 범주로 나눌 수 있지만 핵심은 데이터가 저장되고, 읽히고, 업데이트되고, 사용된다 는 점입니다.
XState 라이브러리의 저자이자 상태머신 전문가인 David Khourshid 는
State Management (상태관리) 란 시간이 지남에 따라 상태가 어떻게 변하는지 다루는 것이다
라고 했습니다.
또 State Management (상태관리) 는 다음과 같은 기능을 수행 할 수 있어야 한다고 했습니다
- 초기 값을 저장할 수 있어야 하고
- 현재 값을 읽을 수 있어야 하고
- 값을 업데이트할 수 있어야 하며
- 값이 변경되었음을 알 수 있어야 한다
React 의 useState 와 useReducer 훅은 상태 관리의 좋은 예라고 할 수 있습니다.
두 훅은 모두
- 훅을 호출해서 초기 값을 저장하고
- 값을 읽고
setState나dispatch를 통해 값을 업데이트하며- 컴포넌트가 리렌더링됨으로써 값의 변경을 알 수 있습니다.
마찬가지로 Redux 와 MobX 도 명확히 상태 관리 도구입니다.
- Redux 는 root reducer 를 호출해서 초기 값을 저장하고,
store.getState()로 값을 읽으며,store.dispatch(action)으로 업데이트 하고,store.subscribe(listener)로 업데이트를 구독합니다 - MobX 는 스토어 클래스의 필드에 값을 할당해 초기 값을 저장하고, 필드를 읽거나 다시 값을 할당해 업데이트 하고,
autorun()과computed()를 통해 변경을 알립니다.
React Query, SWR, Apollo, Urql 같은 서버 상태 캐싱 라이브러리도 상태 관리의 정의에 해당한다고 볼 수 있습니다.
이들은 데이터를 가져와 초기 값을 저장하고, 훅을 통해 현재 값을 반환하며, 서버 mutation 을 통한 업데이트를 허용하고, 컴포넌트 리렌더링으로 변경을 알립니다.
그러나 React Context는 이런 기능을 제공하지 않습니다.
‼️ 따라서 Context 는 상태관리도구가 아닙니다 ‼️
앞서 설명한것과 같이 Context 는 스스로 아무것도 저장하지 않고, <MyContext.Provider> 를 렌더링하는 부모 컴포넌트가 어떤 값을 전달할지 결정하며, 그 값은 일반적으로 React state 에서 가져옵니다.
실제 상태 관리는 useState 나 useReducer 에서 일어납니다.
David Khourshid 는
Context 는 이미 존재하는 상태를 컴포넌트 트리 전체에 전달하는 메커니즘일 뿐이다.
Context 자체는 상태 관리와는 관련이 없다.
라고 했고, 최근 트윗에서
Context 는 추상화된 상태라기 보다는 숨겨진 props 에 더 가깝다
고 했습니다.
결국 동일한 useState, useReducer 코드를 작성한 뒤, 데이터와 업데이트 함수를 prop drilling 으로 트리를 따라 아래로 전달할 수 있었던 것을 Context 로 생략 할 수 있게 된 것 뿐입니다.
Context 와 Redux 의 비교
| 구분 | React Context | React + Redux |
|---|---|---|
| 상태 저장/관리 | 아무것도 저장하거나 관리하지 않음 | 하나의 값(일반적으로 객체)을 저장하고 관리 |
| UI 의존성 | React 컴포넌트 안에서만 동작 | 어떤 UI에서도 사용 가능 (React 외부 포함) |
| 값 전달 방식 | 단일 값만 전달 (원시값, 객체, 클래스 등 무엇이든 가능) | 스토어에 저장된 전체 상태 객체 접근 가능 |
| 값 읽기 | 현재 Context 값 읽기 가능 | 현재 스토어 상태 읽기 가능 |
| Prop Drilling 방지 | 가능 | 가능 |
| 상태 업데이트 방식 | 직접 상태 업데이트 기능 없음 | 액션 디스패치 + 리듀서로 상태 업데이트 |
| DevTools 지원 | 현재 값만 표시 변경 이력 추적 불가 | 모든 액션/상태 변화 시간순 이력 추적 가능 |
| 성능 관리 | 값 변경 시 모든 소비자 강제 리렌더 최적화 불가 | 특정 상태 조각만 구독, 필요한 경우에만 리렌더 |
| 사이드 이펙트 처리 | 관련 기능 없음 (렌더링만 담당) | 미들웨어를 통한 사이드 이펙트 처리 지원 |
공통점은 단 하나, 둘 다 prop drilling 을 방지하는 데 사용할 수 있다는 점입니다.
Context 와 useReducer
Context vs Redux 논쟁에서 흔히 발생하는 문제 중 하나는, 사람들이 실제로는 useReducer 로 상태를 관리하고, Context 로 그 값을 전달하고 있다 라는 의미로 말하면서도 그걸 명시하지 않는다는 점입니다.
그냥 Context 를 사용하고 있어요 라고 말하죠.
이것이 제가 흔히 보는 혼란의 주요 원인이고, Context 가 상태를 관리한다는 잘못된 인식을 퍼뜨리는데 큰 역할을 합니다.
그래서 이번에는 Context + useReducer 조합을 구체적으로 살펴보겠습니다.
Context + useReducer 는 Redux + ReactRedux 와 매우 비슷해 보입니다.
두 방식 모두 아래의 기능을 갖고 있습니다.
- 저장된 값
- reducer 함수
- action dispatch 기능
- 그 값을 하위 컴포넌트에서 전달하고 읽는 방법
하지만, Context + useReducer 와 Redux + ReactRedux 기능과 동작방식에는 큰 차이점들이 존재합니다. 제가 앞서 쓴 글 React, Redux 와 Context 의 동작 과 React Rendering 원리 에서 핵심 내용을 다뤘습니다.
요약하자면 다음과 같습니다.
Context + useReducer 는 현재 상태 값 전체를 Context 로 전달합니다.
반면 React Redux 는 Redux 스토어 인스턴스 자체를 Context 로 전달합니다.
즉, useReducer 가 새로운 상태 값을 생성하면, 그 Context 를 구독하는 모든 컴포넌트들은 강제로 재렌더링됩니다.
즉, useReducer 가 새로운 상태 값을 생성하면, 그 Context 를 구독하는 모든 컴포넌트들은 강제로 재렌더링됩니다.
심지어 그 값의 일부만 필요로 하는 컴포넌트들도 재렌더링됩니다.
이는
- 상태 크기가 얼마나 큰지
- 상태를 읽는 컴포넌트의 수
- 상태가 얼마나 자주 변경되는지
에 따라 성능 문제를 유발할 수 있습니다.
반면, React Redux 에서는 컴포넌트가 스토어 상태의 특정 부분만 구독할 수 있고, 해당 부분이 변경될 때만 재렌더링 됩니다.
추가적인 차이점들도 있습니다.
- Context +
useReducer는 리액트의 기능이기 때문에 React 외부에서 사용할 수 없는 반면에, Redux 는 UI 라이브러리와 독립적이어서 React 외부에서도 사용할 수 있습니다. - React Devtools 는 Context 값을 표시할 수 있지만, 값의 변경 이력을 추적할수는 없습니다. Redux Devtools 는 dispatch 된 모든 액션과 상태 변경 이력을 추적할 수 있습니다.
useReducer는 사이드 이펙트 처리를 위한 별도의 middleware 가 존재하지 않습니다.
React Core 팀 아키텍트인 Sebastian Markbage 는 Context 에 대해
Context는 업데이트가 거의 없는 값(locale/theme 등)에 적합합니다. 예전에 Context 가 쓰인 방식과 비슷하게 정적인 값을 위한 용도입니다. Flux 스타일의 상태 전파 전체를 대체할 준비는 되지 않았습니다
많은 글들이 큰 상태를 나누기 위해 여러개의 Context 를 생성하고, useMemo, memo 를 사용하며, 데이터와 업데이트 함수를 위한 Context 를 분리하는 등의 방식을 소개하지만..
그렇게 하면 결국 React-Redux 를 다시 만들게 되는 셈입니다.
그래서, Context + useReducer 는 언뜻 보면 Redux + ReactRedux 를 대체할 수 있어 보이지만, 기능적으로는 결코 완전한 대체제가 될 수 없습니다.
올바른 도구 선택하기
앞서 말했듯이, 어떤 도구가 어떤 문제를 해결하는지 이해하고 해결하려는 문제가 무엇인지 아는것이 문제를 해결할 수 있는 올바른 도구를 선택하는데 필수적입니다.
사용 사례
| 도구 | 사용 사례 |
|---|---|
| Context | Prop-drilling 없이, 중첩된 컴포넌트들에게 값을 전달 |
| useReducer | 리듀서 함수를 사용하는, 적당히 복잡한 React 컴포넌트 상태 관리 |
| Context + useReducer | 리듀서 기반 적당히 복잡한 컴포넌트 상태 관리 + 해당 상태 값을 prop-drilling 없이 전달 |
| Redux | 리듀서 기반 중간 ~ 고도 복잡도 상태 관리 상태가 언제, 왜, 어떻게 변경되었는지 추적 UI 레이어와 완전히 분리된 상태 관리 로직 여러 UI 레이어 간 상태 공유 Redux 미들웨어로 액션 시 추가 로직 처리 Redux 상태 부분적 영속 저장 가능 재현 가능한 버그 리포트 가능 개발 중 더 빠른 디버깅 |
| Redux + React-Redux | Redux의 모든 사용 사례 + React 컴포넌트에서 Redux 스토어 상호작용 |
다시 말하지만, 이것들은 서로 다른 문제를 해결하는 서로 다른 도구들입니다.
추천!
그렇다면, Context / Context + useReducer / Redux + React-Redux 중 어떤 것을 선택해야 할까요?
해결하려는 문제에 가장 잘 맞는 도구를 선택해야 합니다!
- Prop-drilling만 피하면 되는 상황 : Context
- 적당히 복잡한 상태가 있고, 외부 라이브러리를 쓰고 싶지 않다면 : Context + useReducer
- 시간 흐름에 따른 상태 변경 추적, 특정 상태 조각만 리렌더, 강력한 사이드 이펙트 처리 등이 필요하면 : Redux + React-Redux
제 개인적인 의견은, 애플리케이션에서 상태 관련 Context가 2~3개를 초과하기 시작하면, 이미 약화된 React-Redux를 재개발하고 있는 것이므로 그냥 Redux로 전환해야 한다는 것입니다.
"Redux는 보일러플레이트가 많다" 는 말도 있는데, 그런 불만은 이제 매우 구식입니다.
현대의 Redux는 훨씬 쉽습니다: 공식 **Redux Toolkit(RTK)**이 보일러플레이트 문제를 해결하고
React-Redux hooks API는 Redux 사용을 훨씬 단순하게 해줍니다.
최근 어떤 사용자가 이렇게 말했습니다:
우리는 Context + Hooks에서 RTK로 전환했습니다. 이 서비스는 연간 10억 달러 이상 처리합니다. RTK는 팀을 설득할 수 있게 해 준 멋진 도구였습니다. 보일러플레이트를 분석해보니, Context 디스패치 패턴보다 RTK가 더 적은 보일러플레이트였습니다. 데이터 처리도 훨씬 쉬워졌습니다.
물론 RTK와 React-Redux는 Context + useReducer 보다 번들 크기를 조금 증가시킵니다.
하지만 그에 따른 이점인
- 더 나은 상태 변경 추적
- 더 단순하고 예측 가능한 로직
- 개선된 리렌더링 성능
을 고려하면, 충분히 가치 있는 트레이드오프입니다.
또한, 이 도구들은 상호 배타적이지 않습니다! Redux, Context, useReducer를 동시에 사용할 수 있습니다.
- "전역 상태(global state)"는 Redux
- "지역 컴포넌트 상태(local state)"는 React state
- 반정적(semi-static) 값은 Context
한 앱에서도 목적에 따라 함께 사용할 수 있습니다.
명확하게 말하자면,
모든 앱이 Redux를 써야 한다는 말이 아닙니다.
항상 Redux가 더 낫다는 뜻도 아닙니다.
하지만 Redux는 여전히 유효한 선택지이고 많은 경우 그 가치 있는 선택이라는 것입니다.
그리고 Context와 Redux가 전부도 아닙니다!
다양한 상태 관리 도구들이 존재합니다:
- MobX — OOP 및 옵저버블 기반 자동 종속성 업데이트
- Jotai, Recoil, Zustand — 가벼운 로컬 상태 관리 방식
- React Query, SWR, Apollo, Urql — 서버 상태 캐싱 최적화
- RTK Query — Redux Toolkit용 서버 상태 도구
다시 말해:
도구마다 목적과 사용 사례가 다르고, 내 상황에 맞춰 선택하는 것이 중요합니다
결론
솔직히 말해서, 이 글이 "Context vs Redux?!?!?!?!?" 라는 끝이 없어 보이는 논쟁을 멈추게 하지는 못할 것이라는 걸 알고 있습니다. 너무 많은 사람들이 있고, 너무 많은 상반된 생각들이 있으며, 잘못된 소통과 잘못된 정보가 너무 많습니다.
그렇긴 해도, 이 글이 이러한 도구들이 실제로 무엇을 하는지, 그들이 어떻게 다르고, 언제 실제로 사용을 고려해야 하는지에 대해 명확하게 설명하는 데 도움이 되었기를 바랍니다. (그리고 어쩌면 ... 아주 어쩌면 ... 이 글을 읽은 몇몇 분들은 이미 백만 번은 나왔던 동일한 질문을 굳이 다시 올릴 필요성을 느끼지 않을지도 모르겠습니다...)
INFO
다소 의역이 포함되어 있을 수 있습니다. 잘못된 부분이나 오해의 소지가 있는 부분이 있다면 언제든지 알려주세요.