[React] State Management 상태 관리
안녕하세요.
React를 사용해보신 분들은 useState나 redux를 이용하여 상태 관리를 해본 경험이 있으실 겁니다.
오늘은 상태 관리의 역사, 라이브러리 등등에 대해 알아보도록 하겠습니다.
🔸 상태란?
컴포넌트 안에서 관리되는 변경 가능한 데이터 저장소로, 상태가 변경되면 컴포넌트가 리렌더링 됩니다.
리액트에서는 useState을 사용하여 상태관리를 할 수 있습니다.
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count+1)}>
click me
</button>
</div>
);
}
위 코드에서 count 변수는 상태라고 할 수 있습니다. 버튼 클릭 여부에 따라 동적으로 변할 수 있는 값이고, 동적으로 변경된 값은 결국 브라우저에 반영되어야 하기 때문입니다.
위와 같이 하나의 컴포넌트에서 상태를 관리하는 것은 간단합니다. 하지만 이 상태를 여러 컴포넌트가 접근하고 공유해야 하는 경우엔 어떻게 해야 할까요?
리액트는 단방향 데이터 바인딩을 지원하기 때문에, 부모에서 자식으로만 데이터를 전달할 수 있습니다. 상태를 여러 컴포넌트에 넘겨주기 위해서 리액트에서는 하위 컴포넌트로 prop을 넘겨 해당 컴포넌트까지 전달되도록 Prop Drilling을 합니다.
Prop Drilling이란?
props를 오직 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트를 거쳐 다른 컴포넌트로 데이터를 전달하는 과정이다.
[상위 컴포넌트 > 중간 컴포넌트 > 중간 컴포넌트 ... > 타켓 컴포넌트]
애플리케이션 규모가 커지면 Prop Drilling도 많아지게 되고, prop을 추적하고 관리하기 어려워집니다.
Context API
그래서 등장한 것이 Context API입니다.
Context API를 사용하면 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다.
이 Context API가 도입됨에 따라 Prop Drilling 문제를 해결하였지만, 아직 성능적인 이슈가 존재합니다. 상태가 변경되었을 때 Context를 구독하고 있는 모든 컴포넌트들이 모두 리렌더링됩니다.
이 이슈 때문에 여전히 많은 리액트 개발자들이 상태 관리 라이브러리를 사용하고 있습니다.
🔸 상태 관리의 역사
상태 관리 라이브러리에 대해 알아보기 전, 상태 관리의 역사에 대해 알아보도록 하겠습니다.
기존의 UI 상태 관리는 MVC 패턴을 사용했습니다.
MVC 패턴에서는 모델 상태가 변경되면 뷰가 바뀌고, 뷰에서 변경이 일어나면 모델 상태가 바뀌며, 컨트롤러가 이를 조작합니다. MVC 패턴은 양방향 데이터 흐름을 가지고 있기 때문에 Model과 View의 개수가 많아지면 아래와 같이 복잡도가 증가합니다.


실제로 페이스북에서 읽지 않은 채팅 개수를 표시할 때 이 복잡도 때문에 고생을 한 적이 있습니다.
채팅을 읽었으면 알림이 없어져야 하는데 없어지지 않았고, 처음에 사라졌다고 해도 몇 분 뒤에 알림이 다시 표시되는 에러가 있었습니다.
페이스북이 찾은 근본적인 문제점은 애플리케이션에서의 데이터 흐름이었고, 해결책으로 데이터가 단방향으로만 흐르고, 새로운 데이터를 넣으면 처음부터 흐름이 다시 시작되는 방식인 Flux 패턴을 발표하게 됩니다.
Flux 패턴
Flux 패턴은 단방향으로 데이터가 진행되며, 상태의 전이 현상(뷰와 모델 사이의 데이터 변경이 연결된 수많은 곳을 따라 변경되는 현상)을 없애주고 예측 가능하다는 특징이 있습니다.

- Action
애플리케이션의 상태를 변경하거나 뷰를 업데이트 할때 액션을 발생시킨다.
- Dispatcher
액션이 넘어오면 스토어에 액션을 보내며, 이 처리는 동기적으로 실행된다.
- Store
어플리케이션 내의 모든 상태와 그와 관련된 로직을 가지고 있다. 모든 상태 변경은 반드시 스토어에 의해 결정되어야 하며, 상태 변경을 위한 요청을 스토어에 직접 보낼 수는 없다.
- View
뷰는 상태를 가져와 유저에게 보여주고, 입력받을 화면을 렌더링 하는 역할을 맡는다.
🔸 State Management 라이브러리
뷰 라이브러리의 발전과 함께 상태가 복잡한 애플리케이션들이 생겨났고, 고도화된 전역 상태 관리에 대한 필요성이 생겨났습니다.
Redux
위 Flux 패턴을 구현한 대표적인 라이브러리가 Redux입니다.
Redux를 사용하면 상태를 항상 예측할 수 있습니다. 동일한 상태와 액션이 리듀서로 전달되는 경우, 리듀서는 순수 함수 이기 때문에 동일한 결과를 얻습니다. 또한, Store에 모든 상태를 보관하고 관리하기 때문에 변경 사항을 예측 가능하고 추적 가능한 상태로 유지할 수 있습니다.

MobX
Redux와 비슷한 상태 관리 라이브러리이지만 Redux에 비해 간결하고 깔끔한 구조를 가지고 있다는 평가를 받습니다. 가장 큰 특징은 모든 상태 변화가 일어나는 부분을 자동으로 추적해준다는 것입니다.

MobX는 러닝 커브가 낮고 보일러 플레이트 코드의 양이 적기 때문에 다루기 쉽다는 장점이 있습니다.
하지만 주의해야 할 점도 있습니다. Store가 여러 개일 수 있기 때문에, 한 상태가 변경되었을 때 여러 Store가 영향을 받을 수 있습니다. 또한, Store의 데이터를 액션의 발행 없이 업데이트할 수 있기 때문에 테스트나 유지 보수할 때 예측할 수 없는 결과를 가져올 수 있습니다.
Recoil
페이스북이 내놓은 새로운 라이브러리로, React 스러움을 유지하며 개선하는 방식의 라이브러리입니다.
Recoil을 사용하면 Atom이라는 작은 데이터 조각을 만들어서 해당 상태가 변화할 때 이를 참조하는 컴포넌트만 리렌더 시킵니다.
Atom의 상태 변화는 Selector라고 하는 순수 함수에 의해 일어나는데, Selector는 비동기 처리도 가능하기 때문에 Redux처럼 별도의 비동기 라이브러리에 의존할 필요가 없습니다.

오늘은 상태 관리의 역사와 상태관리 라이브러리에 대해 간단하게 알아보았습니다.
프로젝트 규모가 작은 경우엔 굳이 적용할 필요는 없지만, 규모가 커질 때는 각 라이브러리의 특징을 고려하여 프로젝트에 알맞은 라이브러리를 적요하는 것이 중요할 것 같습니다.
글 틀린 부분이 있거나 궁금한 것이 있으면, 댓글 부탁드립니다.
감사합니다.
출처 :
https://watermelonlike.tistory.com/m/entry/리액트에서-상태관리란flux-사용-이유
https://www.stevy.dev/react-state-management-guide/
https://velog.io/@danmin20/상태관리-라이브러리-뭘-쓸까
https://bestalign.github.io/translation/cartoon-guide-to-flux/
https://www.youtube.com/watch?v=LRUQfJLuPA8
https://velog.io/@yrnana/Context-API가-존재하지만-여전히-사람들이-redux와-전역-상태관리-라이브러리를-쓰는-이유
https://velog.io/@longroadhome/FE-리액트-상태관리-1부