5.1 상태 관리는 왜 필요한가?
웹 애플리케이션을 개발할 때의 '상태'란 어떤 의미를 지녔으며 애플리케이션 시나리오에 따라 지속적으로 변경될 수 있는 값을 의미합니다.
UI(다크/라이트), URL(쿼리 파라미터의 값), 폼(로딩, 제출 여부, 접근 가능 여부, 유효한 값인지 여부), 서버에서 가져온 값 등이 상태가 될 수 있습니다.
5.1.1 리액트 상태 관리의 역사
Flux 패턴의 등장
리액트에서 전역 상태 관리는 어떻게 이루어졌을까요? 전역 상태를 주입할 수 있는 Context API는 16.3 버전, useContext는 16.8 버전에서 나왔습니다. 그 전까지 리덕스가 나오기 전까진 유명한 상태 관리 라이브러리는 없었습니다.
2014년 경, Flux 패턴과 함께 이를 기반으로 한 라이브러리인 Flux가 소개됩니다. 당시는 웹 애플리케이션이 비대해지고 상태(데이터)도 많아지며 상태의 추적이 어려웠습니다. 기존의 MVC 패턴은 모델과 뷰가 많아질 수록 복잡도가 증가하는 단점이 있었습니다.
페이스북 팀은 양방향 데이터 바인딩이 문제의 원인으로 생각했습니다. 뷰(HTML)가 모델(JS)을 변경할 수 있고, 그 반대도 가능합니다. 코드 작성에는 간단하지만, 코드가 복잡해질 수록 관리가 어렵습니다. 이에 단방향 데이터 흐름으로의 변경을 제안하게 되는데 이것이 Flux 패턴입니다.
Flux 패턴의 기본적인 흐름: Action -> Dispatcher -> Model -> View
- 액션: 어떤 작업을 처리할 액션과 그 액션 발생 시 함께 포함시킬 데이터를 의미합니다. 액션 타입과 데이터(payload)를 각각 정의해 디스패처로 보낸다.
- 디스패처: 액션을 스토어로 보냅니다. 콜백 함수 형태로 앞서 액션이 정의한 타입과 데이터를 모두 스토어로 보냅니다.
- 스토어: 실제 상태에 따른 값과 상태를 변경할 수 있는 메서드를 가지고 있습니다. 액션 타입에 따라 어떻게 변경할지 정의되어 있습니다.
- 뷰: 리액트의 컴포넌트에 해당하는 부분으로, 스토어에서 만들어진 데이터를 가져와 화면을 렌더링합니다. 뷰에서도 사용자의 입력이나 행위에 따라 상태를 업데이트합니다. 이 경우, 뷰에서 액션을 호출하게 됩니다.
type StoreState = {
count: number,
};
type Action = { type: "add", payload: number };
function reducer(prevState: StoreState, action: Action) {
const { type: ActionType } = action;
if (ActionType === "add") {
return {
count: prevState.count + action.payload,
};
}
throw new Error(`Unexpected Action [${ActionType}]`);
}
export default function App() {
const [state, dispatcher] = useReducer(reducer, { count: 0 });
function handleClick() {
dispatcher({ type: "add", payload: 1 });
}
return (
<div>
<h1>{state.count}</h1>
<button onClick={handleClick}>+</button>
</div>
);
}
단방향 데이터 흐름의 단점도 있습니다. 사용자의 입력에 따라 데이터를 갱신하고 화면을 어떻게 업데이트할지 코드로 작성해야 하므로 코드의 양이 많아집니다.
리덕스의 등장
처음엔 Flux 패턴을 구현하기 위해 만들어진 라이브러리 중 하나였으나, 여기에 Elm 아키텍처를 도입했다는 특징이 있습니다. Elm은 웹페이지를 선언적으로 작성하기 위한 언어입니다. Elm은 모델, 뷰, 업데이트의 세 가지 요소로 데이터가 흐르게 됩니다. 모델은 상태, 뷰는 모델을 받아 표현하는 HTML, 업데이트는 모델을 수정하는 방식입니다.
Flux 패턴과 유사하게 세 가지로 데이터 흐름을 분류하고, 단방향으로 데이터가 흐르게 합니다. 리덕스는 Elm 아키텍처의 영향을 받아 개발되었습니다.
리덕스를 통해, 글로벌 상태 객체를 어디서나 접근할 수 있게 되어, prop를 깊게 전파할 때 발생하는 문제를 해결할 수 있게 되었습니다.
그렇지만, 단순히 하나의 상태를 바꾸기 위해 많은 보일러플레이트 코드가 생기는 단점이 있습니다. 그럼에도 뚜렷한 대안이 존재하지 않았기에 리액트와 리덕스의 조합은 일종의 표준처럼 굳어져 사용되어 왔습니다.
Context API와 useContext
props drilling을 해결하기 위해 단순히 전역 상태를 주입하기 위한 Context API가 나왔습니다. 당시에도 리덕스가 있었지만 리덕스는 보일러플레이트 코드가 많아 단순 상태 참조에는 적합하지 않았습니다.
16.3 버전 이전에는 context와 이를 다루는 getChildContext가 있었습니다. 하지만, 상위 컴포넌트가 렌더링되면 getChildContext도 호출됨과 동시에 shouldComponentUpdate가 항상 true를 반환해 불필요하게 렌더링이 일어나고, getChildContext를 사용하려면 context를 인수로 받아야 하는데 이 때문에 컴포넌트와 결합도가 높아지는 등의 단점이 있었습니다. 이러한 단점을 해결하기 위해 16.3 버전에서 새로운 context(Context API)가 나왔습니다.
훅의 탄생, React Query, SWR
16.8 버전부터 훅이 탄생하며, state 관리가 쉬워졌고, 이를 기반으로 React Query와 SWR이 등장하게 됩니다. 두 라이브러리는 HTTP 요청에 특화된 상태 관리 라이브러리입니다.
Recoil, Zustand, Jotai, Valtio에 이르기까지
훅과 함께 HTTP 요청에 국한되지 않는, 범용적인 상태 관리 라이브러리도 변화가 있었습니다. 리덕스와는 달리 훅을 활용해 작은 크기의 상태를 효율적으로 관리할 수 있게 되었습니다.
Valtio: JS의
Proxy객체를 활용한 상태 관리 라이브러리로, Redux나 Recoil에 비해 코드 양이 적습니다.
Proxy 객체: 원본 객체를 감시하거나 조작할 수 있게 해주는 중간자 역할을 수행하는 객체로, 가로채는 함수인 trap을 통해 원본 객체의 접근이나 변경을 감지해 커스텀 로직을 추가할 수 있습니다.
5.2 리액트 훅으로 시작하는 상태 관리
5.2.1 useState, useReducer
가장 간단하게 상태를 관리할 수 있는 방식입니다. 지역 상태를 관리할 수 있습니다.
5.2.2 useState의 상태를 바깥으로 분리하기
5.2.3 useState와 Context를 동시에 사용해 보기
5.2.4 Recoil, Jotai, Zustand 살펴보기
- Recoil: Context와 Provider, 훅을 기반으로 가능한 작은 상태를 관리하는 데 초점을 맞춘 상태 관리 라이브러리입니다. 주요 함수로
RecoilRoot,atom,selector,useRecoilState,useRecoilValue등이 있습니다. 2025년 1월 이후로 개발이 중단되며 Jotai로 넘어가는 분위기입니다. - Jotai: Recoil의 영향을 받았습니다. 작은 단위의 상태를 위로 전파할 수 있는 구조를 취하고 있습니다. 개발자가 메모이제이션하지 않아도 Context의 문제점인 불필요한 리렌더링이 발생하지 않도록 최적화되어 있습니다. 주요 함수로
atom,useAtom,useAtomValue등이 있습니다. - Zustand: Redux의 영향을 받았습니다. 다른 상태 관리 라이브러리에 비해 코드가 굉장히 가볍습니다. 하나의 스토어에서 상태를 관리하고 변경합니다. 주요 함수로
createStore,create등이 있습니다.persist,immer와 같은 유용한 미들웨어를 지원합니다.
'라이브러리 > React' 카테고리의 다른 글
| [모던 리액트 Deep Dive] 7장 크롬 개발자 도구를 활용한 애플리케이션 분석 (0) | 2025.06.25 |
|---|---|
| [모던 리액트 Deep Dive] 6장 리액트 개발 도구로 디버깅하기 (0) | 2025.06.25 |
| [모던 리액트 Deep Dive] 4장 서버 사이드 렌더링 (1) | 2025.06.10 |
| [모던 리액트 Deep Dive] 3장 리액트의 훅 깊게 살펴보기 (0) | 2025.06.08 |
| [모던 리액트 Deep Dive] 2장 리액트 핵심 요소 깊게 살펴보기 (2) | 2025.06.04 |