- Published on
2025.04.18
[리액트 딥다이브] - 05. 리액트와 상태 관리 라이브러리(1)
5.1. 상태 관리는 왜 필요한가?
상태: 어떠한 의미를 지닌 값이며 애플리케이션의 시나리오에 따라 지속적으로 변경될 수 있는 값
웹 애플리케이션에서 상태로 분류될 수 있는 것들
- UI: 상호작용되는 모든 요소의 현재 값
- URL: 브라우저에서 관리되는 상태값
- form: 로딩, 제출, 유효 등
- 서버에서 가져온 값: 요청을 통해 서버에서 가져온 값
5.1.1. 리액트 상태 관리의 역사
Flux 패널의 등장
기존 MVC 패턴은 모델과 뷰가 많아질수록 복잡도가 증가했고, 페이스북 팀은 문제의 원인을 양방향 데이터 바인딩으로 보아 단방향 데이터 흐름으로 변경하는 것을 제안하며 Flux 패턴이 시작됨
- action: 어떠한 작업을 처리할 액션과, 액션 발생 시 포함시킬 데이터
- dispatcher: 액션을 스토어에 보내는 역할
- store: 실제 상태에 따른 값과 상태를 변경할 수 있는 메서드가 정의되어 있음
- view: 리액트의 컴포넌트에 해당하는 부분, 스토어에서 만들어진 데이터를 가져와 화면을 렌더링하는 역할
단방향 데이터의 흐름은 추적하기 쉽고 코드를 이해하기가 수월, Flux 패턴은 단방향 데이터 바인딩 기반인 리액트와 잘 맞아 많은 라이브러리들이 등장하기 시작
시장 지배자 리덕스의 등장
Flux 구조를 구현하기 위해 만들어진 라이브러리로, Elm 아키텍처를 도입
Elm 아키텍처의 핵심
- model: 애플리케이션의 상태
- view: 모델을 표현하는 HTML
- update: 모델을 수정하는 방식
Elm은 Flux와 마찬가지로 데이터 흐름을 세 가지로 분류 및 단방향으로 강제하는데 리덕스는 Elm 아키텍처의 영향을 받아 작성됨
리덕스는 하나의 글로벌 상태 객체를 통해 prop 내려주기 문제를 해결하고, 스토어가 필요한 컴포넌트에서 connect를 통해 바로 접근할 수 있게 해줌
그러나 역할에 비해 너무 많은 보일러플레이트 등으로 불편함이 존재했지만 그럼에도 표준처럼 입지가 굳어짐
Context API와 useContext
prop drilling의 불편함이 있고, 단순한 상태 참조를 위해 리덕스의 보일러플레이트를 감수하기는 부담스럽기에 리액트 팀은 리액트 16.3에서 Context API를 출시. props를 넘겨주지 않아도 Context API를 통해 원하는 곳에서 Context Provider가 주입하는 상태를 사용할 수 있게 됨.
그러나 Context API는 상태 관리가 아닌 주입을 도와주는 기능으고, 렌더링을 막아주는 기능이 없으므로 사용 시 주의 필요
훅의 탄생, 그리고 React Query와 SWR
리액트는 16.8에 함수 컴포넌트에서 사용할 수 있는 다양한 훅 API를 추가, 훅과 state의 등장으로 React Query와 SWR 등 새로운 방식의 상태 관리가 등장
Recoil, Zustand, Jotai, Valtio에 이르기까지
기존 리덕스 같은 라이브러리와 달리 훅을 활용해 작은 크기의 상태를 효율적으로 관리하는 라이브러리들이 등장.
기존 상태 관리 라이브러리들의 상태 관리 패러다임에서 벗어나 원하는 만큼의 상태를 지역적으로 관리할 수 있게 해줌.
5.2. 리액트 훅으로 시작하는 상태 관리
5.2.1. 가장 기본적인 방법: useState와 useReducer
useState와 useReducer는 지역 상태 관리를 위해 만들어져 손쉽게 재사용이 가능하지만 모든 필요성과 문제를 해결하진 못함. 지역 상태는 해당 컴포넌트 내에서만 유효한 한계를 지님. 이를 해결하기 위해 컴포넌트 트리를 재설계하는 수고로움이 필요
5.2.2. 지역 상태의 한계를 벗어나보자: useState의 상태를 바깥으로 분리하기
지역 상태의 한계를 벗어나기 위해 다른 곳에서 초기화 후, 상태를 참조하는 유효한 스코프 내부에 객체의 값을 공유하면 상태를 사용할 수 없다. getter와 setter는 작동하고 state도 잘 업데이트 되지만, 컴포넌트가 리렌더링되지 않는다. 함수 외부에서 상태를 참조하고 이를 통해 렌더링까지 일어나려면 다음과 같은 조건을 만족해야 한다.
- 컴포넌트 외부 어딘가에 상태를 두고 여러 컴포넌트가 쓸 수 있어야 함
- 외부 상태를 쓰는 컴포넌트는 상태의 변화를 알아채고 리렌더링이 일어나 최신 상태값 기준으로 컴포넌트를 렌더링해야 함. 이 상태 감지는 상태를 참조하는 모든 컴포넌트에서 동일하게 작동해야 함
- 상태가 원시값이 아닌 객체인 경우, 객체에 감지하지 않는 값이 변한다 해도 리렌더링이 발생해서는 안 됨
5.2.3. useState와 Context를 동시에 사용해보기
현재 리액트 생태계의 많은 상태 관리 라이브러리의 작동 방식은 다음과 같음
- 지역 상태라는 점을 극복하기 위해 외부에 상태를 둠
- 외부의 상태 변경을 감지해 컴포넌트의 렌더링을 일으킴