[리액트] Context는 상태 관리 도구가 아니다?

navyjeongs·2024년 1월 6일
5

리액트

목록 보기
5/8
post-thumbnail

프로젝트를 시작하기전 항상 상태 관리 도구를 무엇을 쓸까?를 고민한다.

최근 프로젝트에서는 전역으로 관리할 상태가 거의 없으므로 리액트에서 제공하는 Context API를 사용하기로 했다.

이 때 까지만해도 Context가 상태 관리 도구인줄 알았으나 Context는 상태관리 도구가 아니라 의존성을 주입해주는 역할을 하는 것을 알게되었다.

해당 포스팅에서는 Why React Context is Not a "State Management" Tool를 읽고 왜 Context가 상태 관리 도구가 아닌지 정리한다.
또, Context + useReducer와 redux의 차이와 리액트의 상태 관리 라이브러리(redux, jotai, recoil)가 어떻게 구성되어있는지 간단하게 살펴본다.

상태 관리

상태 관리를 한다고 하기 위해서는 다음의 세 가지 역할을 할 수 있어야한다.

  • 초기값 저장
  • 현재값 읽기
  • 값 업데이트

리액트의 useStateuseReducer는 상태 관리의 예시이다. 두 개의 hook은 아래의 역할을 한다.

  • hook을 호출해서 초기값을 저장
  • hook을 호출해서 현재값을 확인
  • setState혹은 dispatch를 호출하여 값을 업데이트
  • 컴포넌트가 리렌더링 되었으므로 값이 업데이트됨을 확인할 수 있음

redux는?

redux는 현재 값을 getState로 확인할 수 있으며 dispatch를 통해 값을 업데이트할 수 있다.
또, subscribe를 통해 해당 state를 구독하고 있는 컴포넌트에 업데이트가 되었음을 알려줄 수 있으므로 상태 관리툴이다.

Context

이제 상태 관리가 무엇인지 알게되었으니 React의 Context에 대해 알아보자.

공식 문서에 의하면 Context는 필요한 정보를 부모로부터 props로 받을 필요 없이 데이터를 받을 수 있게 해주는 것이라고 나온다.

공식 문서에 Context가 상태 관리를 한다라는 문구가 없는 것을 확인할 수 있다.

단순히 값을 전달하는 역할을 할 뿐이다.

공식문서의 Context Hooks 설명 바로가기

Context 사용해보기

App에서 CountContext를 만들고 Provider로 count, setCount를 전달해보자.
이 때, Provider의 value로 하위 컴포넌트로 전달할 값을 설정할 수 있다.

import { createContext, useState } from "react";
import Child from "./Child";

// Context 생성
export const CountContext = createContext();

function App() {
  const [count, setCount] = useState(0);

  const countState = { count, setCount };

  return (
    <>
      <CountContext.Provider value={countState}>
        <Child />
      </CountContext.Provider>
    </>
  );
}

export default App;

그 후, Child 컴포넌트에서 CountContext를 useContext를 통해 가져와 사용해보자.

import { useContext } from "react";
import { CountContext } from "./App";

function Child() {
  // Context 가져오기
  const { count, setCount } = useContext(CountContext);

  return (
    <div>
      {count}
      <button onClick={() => setCount((prev) => prev + 1)}>+ 1</button>
    </div>
  );
}

export default Child;

이제 props로 state를 전달하지 않고도 해당 state를 자식에서 읽거나 업데이트할 수 있다.

Context에서 state의 관리

위 예제를 다시 확인해보면 Context에서는 어떠한 값도 저장하지 않는다.

Context는 아래와 같이 만들어진 state를 단순히 전달할 뿐이다.

const [count, setCount] = useState(0);

결론적으로 state를 관리하는 것은 Context가 아니라 App 컴포넌트의 count State다.

즉, 실제로 상태관리는 useState와 useReducer hook을 통해 이루어진다.

Context + useReducer

많은 사람들이 상태관리를 위해 Context를 사용하고 있다 말한다.

Context를 상태 관리로 하고 있다는 말은 사실 useReducer로 상태를 관리하면서 Context로 value를 전달해주고 있다는 의미다.

useReducer로 상태를 관리하지만 useReducer를 사용한다는 말을 생략하고 "난 Context를 이용해 상태 관리를 해"라고 말하는 것이 Context를 상태 관리 툴로 생각하는 원인 중 하나이다.

Context + useReducer vs react-redux

그렇다면 Context + useReducer와 react-redux를 비교해보자.

공통점은 다음과 같다.

  • 상태를 저장
  • reducer 함수
  • action을 dispatch
  • 값을 전달하고 중첩 컴포넌트에서 값을 읽을 수 있는 방법을 제공

차이점은 다음과 같다.

Context + useReducer

  • Context를 통해 현재 상태 값을 전달하는 데 의존한다.
  • useReducer가 새로운 state를 전달할 때 해당 Context를 구독하고 있는 모든 컴포넌트는 데이터의 일부분에만 관심이 있더라도 무조건 리렌더링한다.

react-redux

  • Context를 통해 현재 Redux store의 인스턴스를 전달한다.
  • react-redux는 컴포넌트가 스토어의 특정 부분만 구독하고 있다면 해당 값이 변경될 때만 리렌더링할 수 있다.

무엇을 사용할까?

단순히 props drilling을 피하기 위해 사용한다면 Context를 사용하자.

특정 컴포넌트만 리렌더링 하도록하거나, side effect를 관리하고 싶다면 다른 상태관리 툴을 이용하자.

상태관리 라이브러리의 내부 구현

우리가 흔히 아는 상태 관리 라이브러리(redux, recoil, jotai)가 Context API를 사용하는 것을 이번에 알게되었다.

redux

react-redux 9.0.4 버전을 기준으로 src -> components -> Provider.ts를 보자.

해당 Provider의 props 타입을 확인해보면 context가 있다.
context는 React의 Context를 사용할 지 선택할 수 있다.

그 다음 Provider 컴포넌트에서 해당 인터페이스를 사용한다.
빨간색 박스에서 React의 context를 사용하는지 Redux의 Context를 사용하는지 확인한다.
그 후, 사용하는 Context를 return한다.

Provider 바로가기

recoil

recoil 0.7.7 버전을 기준으로 packages -> recoil -> core -> Recoil_RecoilRoot.js를 보자.

121 line에서 React.createContext를 이용해 Context를 생성하는 것을 확인할 수 있다.

519 line에서 앞서 만든 Context를 Provider로 값을 지정하는 것을 확인할 수 있다.

Recoil_RecoilRoot.js 바로가기

jotai

jotai 2.6.1 버전을 기준으로 src -> react -> Provider.ts를 보자.

7 line에서 React의 createContext를 사용하고 29 line에서 앞서 만든 Context를 Provider로 값을 지정하는 것을 확인할 수 있다.

Provider.ts 바로가기

참고 자료

Why React Context is Not a "State Management" Tool

Recoil github

Recoil github

react-redux github

profile
Front-End Developer

0개의 댓글