React 상태 관리 (Redux, Mobx, Recoil)

Kyungs·2022년 4월 19일
2

React

목록 보기
6/8

React에서 상태란 컴포넌트에서 관리되는 동적인 정보(데이터)이다. 상태에 따라 View가 렌더링되며, 상태가 변경되면 해당 컴포넌트의 View도 재렌더링되어 바뀌게 된다.

React 에서의 데이터 흐름은 단방향이기 때문에, 컴포넌트가 관리하는 데이터를 부모 컴포넌트에서 자식 컴포넌트에게 전달하는 것은 가능하지만, 자식 컴포넌트 => 부모 컴포넌트 또는 자식 컴포넌트 => 자식 컴포넌트 방식으로 전달하는 것은 불가능하다.

위와 같은 방식으로 상태 및 데이터를 전달할 때, 자식 컴포넌트의 depth가 커질 경우 상태 관리가 복잡해질 수밖에 없으며, props drilling과 같은 이슈가 생긴다. 이러한 이슈를 해결하기 위해 등장한 것이 상태 관리 툴이며, 아래 목록은 대표적으로 사용되는 툴 예시이다.

  • React Context
  • Redux
  • Mobx
  • Recoil

React Context

React 공식 문서에서 context에 대해 아래와 같이 설명하고 있다.

context의 주된 용도는 다양한 레벨에 네스팅된 많은 컴포넌트에게 데이터를 전달하는 것입니다.

다양한 레벨, 즉 많은 자식 컴포넌트에게 데이터를 전달하는 방식으로 props drilling을 방지하는 것이 주 목적이다. 계속해서 데이터를 넘겨주는 대신, 전역적으로 데이터를 공유하도록 하여 해당 데이터를 필요로하는 컴포넌트만 데이터를 찾아 이용하면 된다. 중간 레벨의 컴포넌트들은 해당 데이터에 대해 알고 있어야 할 필요가 없다.

위와 같은 장점으로 인해 props drilling을 회피할 수 있고 더 깔끔한 코드를 작성할 수 있지만, React context에는 렌더링 관련 치명적인 단점이 있다. 아래는 그 예시이다.

// root 컴포넌트
const App = () => {
  return (
    <AppProvider>
      <ComponentA />
      <ComponentB />
    </AppProvider>
  );
}

// 상태를 변경하는 컴포넌트
const ComponentA = () => {
  const [, setData] = useContext(AppContext);
  const increase = () => {
  	setData((prevState) => prevState + 1);
  }
  return <button onClick={increase} />
}

// 상태를 가져와 렌더링하는 컴포넌트
const ComponentB = () => {
  const [data] = useContext(AppContext);
  return <div>{`increased by: ${data}`}</div>
}

React context를 사용하려면 위와 같이 root 컴포넌트에 Provider로 감싸야 하는데, AppContext의 상태가 바뀌면 해당 Provider로 감싼 모든 컴포넌트가 리렌더링이 된다. 즉, ComponentA는 상태를 변경만 할 뿐 리렌더링이 불필요한 컴포넌트이지만, 상태가 바뀜에 따라 ComponentB와 함께 불필요하게 리렌더링이 되는 것이다. 위 예시에서는 컴포넌트가 두 개 뿐이지만, context를 구독하고 있는 컴포넌트의 갯수가 많아질수록 리렌더링 이슈는 심각해진다.

Redux

Redux는 전세계 리액트 개발자들이 가장 많이 사용하는 상태 관리 라이브러리이다. Redux는 컴포넌트 바깥에서 전역적으로 상태를 관리한다는 점은 React context와 유사하다. 다만, 아래와 같은 점에서 context와 다르다.

  • React 뿐만 아니라 Vanilla JS나 Angular, Vue와 같은 프레임워크에서도 사용이 가능하다.
  • context는 여러 개가 존재할 수 있지만, Redux store는 하나만 존재할 수 있다.
  • 상태에 변경이 일어나도 모든 컴포넌트에 리렌더링이 일어나지 않는다.
  • redux-saga, redux-thunk, redux-devtools와 같은 추가 기능을 제공하는 라이브러리를 사용할 수 있다.

Mobx

Mobx 역시도 Redux와 같이 리액트에서 사용할 수 있는 상태 관리 라이브러리이며, 클래스형 컴포넌트를 기준으로 만들어져 객체 지향 프로그래밍 방식으로 코드를 작성할 수 있다. 이 때문에 Mobx와 리액트의 함수형 컴포넌트의 Hooks를 함께 사용하면 오류가 발생할 수 있다.

  • Mobx의 observer API가 클래스형 컴포넌트를 리턴하기 때문에, Hooks는 함수형 컴포넌트에서만 사용할 수 있다는 내용의 오류가 발생한다.
  • mobx-react v6 또는 mobx-react-lite를 사용하면 위 오류 없이 Hooks를 사용할 수 있다.
  • 보일러 플레이트 코드 등 작성할 것이 많은 Redux에 비해 가볍다.
  • Mobx store를 전역 객체로 활용하여 각 컴포넌트에서 import해서 사용할 수 있지만, 전역 데이터를 필요로하지 않는 컴포넌트에서도 접근을 하게 되기 때문에 unit testing에 부적합하다. 따라서 context API를 활용하여 각 컴포넌트에서 데이터에 접근하는 방식을 Mobx 공식 문서에서 권장하고 있다.

Recoil

Recoil은 Redux, Mobx와는 다르게 React에서 제공하는 상태 관리 라이브러리이다. Redux와 Mobx는 React의 라이브러리가 아니기 때문에 React 내부 스케쥴러에 접근이 어렵다. 리액트는 최근 동시성 모드를 제공하기 위해 실험중인데, Recoil은 리액트의 상태를 사용하기 때문에 해당 기능도 지원하는 것이 가능하다는 설명이 있다.

  • 동시성 모드: React가 각 컴포넌트에서 실행되는 비동기 요청 등을 보고 판단하여 렌더링 우선 순위를 정해 렌더링 최적화를 하는 모드. 렌더링 될 준비가 완료되어야 렌더링을 시작한다.
  • Suspense: 컴포넌트가 렌더링 될 준비가 되지 않으면 렌더링을 하지 않고 suspense 상태로 둔다. 아래와 같이 fallback 속성을 전달하면 대신 렌더링 할 컴포넌트를 지정할 수 있다.
const Page = React.lazy(
  () =>  import('./Page);
);

<Suspense fallback={<WaitForPage />}>
  <Page />
</Suspense>

Recoil은 타 상태 관리 라이브러리보다 가벼운 편이고 React context와 유사하게 사용할 수 있으나, context의 최대 단점인 불필요한 리렌더링 이슈를 피할 수 있다는 장점이 있다. React를 만든 Facebook에서 Recoil을 만들었기 때문에, 추후엔 더 다양한 기능들이 지원되지 않을까 싶다.

참고 자료

[React 공식 문서] Context
[React 공식 문서] Suspense for Data Fetching (Experimental)
상태 관리 도구(State Management Tools)의 필요성
React에서 Mobx 경험기 (Redux와 비교기)
Context API가 존재하지만 여전히 사람들이 redux와 전역 상태관리 라이브러리를 쓰는 이유
React 상태 관리 Tool 사용 & 비교 - (Redux VS MobX VS Context API)
Recoil - 또 다른 React 상태 관리 라이브러리?
Recoil 레시피: 비동기 액션

1개의 댓글

comment-user-thumbnail
2023년 8월 14일

A standout attribute of Winlator Download is its easily navigable design, ensuring user-friendliness. Installing it is a straightforward process, akin to adding any other software program.

답글 달기