React & TypeScript/React

[React] Context API 정리 (Provider, Consumer, useContext)

yoonjong Park 2025. 1. 10.

개요

대략적인 이해를 하고 사용했던 것 같은데, 좀 더 명확하게 정리하고자 글을 작성한다.

CRA 기반으로 example 코드를 작성하였다.

최적화를 위한 click event hanlder 함수는 일부러 따로 작성하지 않았다. useCallback, useMemo 같은 최적화 함수도 기재하지 않았다. (실전 코드에서는 리렌더링 막기위해서 당연히 써야 한다.)

 

Context API 간단 정의

Context객체에는 Provider, Consumer 컴포넌트가 포함되어 있습니다.

  • Provider: Context를 구독하는 컴포넌트들에게 Context의 변화를 알리는 컴포넌트
  • Consumer: Context 변화를 구독해 변화시 재 렌더링하는 컴포넌트

 

Context API Example

CountPrivder를 만들어서 모든 Router를 감싸도록 했다. ContextAPI를 적용할 범위만큼만 국부적으로 감싸도 된다.

// index.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import About from "./pages/About";
import { BrowserRouter, Routes, Route } from "react-router";
import { CountProvider } from "./CountProvider";
import Consumer from "./pages/Consumer";

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
    <CountProvider>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<App />} />
          <Route path="about" element={<About />} />
          <Route path="consumer" element={<Consumer />} />
        </Routes>
      </BrowserRouter>
    </CountProvider>
);

 

루트 페이지와 About 페이지 부분에서는 useContext 를 사용해서 구현했다. 

// App.js

import { useContext } from "react";
import { CountContext } from "./CountProvider";
import { NavLink } from "react-router";

function App() {
  const { count, setCount } = useContext(CountContext);

  return (
    <>
      <h1>Home</h1>
      <div>카운터 value : {count}</div>
      <button onClick={() => setCount(count + 1)}>+</button>

      <NavLink to="/about">
        <button>/about 로 이동 </button>
      </NavLink>
      <NavLink to="/consumer">
        <button>/consumer 로 이동 </button>
      </NavLink>
    </>
  );
}

export default App;
// About.js

import React, { useContext } from "react";
import { CountContext } from "../CountProvider";
import { NavLink } from "react-router";

const About = () => {
  const { count, setCount } = useContext(CountContext);

  return (
    <>
      <h1>About</h1>
      <div>카운트 : {count}</div>
      <button onClick={() => setCount(count + 1)}>+</button>
      <NavLink to="/">
        <button>Home 이동 </button>
      </NavLink>
    </>
  );
};

export default About;

 

Consumer 페이지 부분에서는 ContextAPI의 Consumer 를 사용해서 구현했다.  

// Consumer.js

import React from "react";
import { CountContext } from "../CountProvider";
import { NavLink } from "react-router";

const Consumer = () => {
  return (
    <CountContext.Consumer>
      {({ count, setCount }) => (
        <>
          <h1>Consumer</h1>
          <div>카운트 : {count}</div>
          <button onClick={() => setCount(count + 1)}>+</button>
          <NavLink to="/">
            <button>Home 이동 </button>
          </NavLink>
        </>
      )}
    </CountContext.Consumer>
  );
};

export default Consumer;

useContext 방식이나 Consumer 방식이나 동일하게 기능한다.

 

useContext vs Consumer 방식의 차이 (feat. GPT)

  • useContext (공식문서에서 추천):
    • only 함수형 컴포넌트
    • Context 값이 변경될 때마다, useContext를 사용하는 컴포넌트 전체가 재렌더링됩니다.
    • 간단한 함수형 컴포넌트에서 더 간결하고 직관적인 코드 작성을 가능하게 합니다. 특히 단일 Context를 사용할 때 코드가 짧고 이해하기 쉽습니다.
  • Consumer:
    • 함수형, 클래스형 컴포넌트 모두 사용
    • render prop 패턴을 사용하여 값을 전달하므로, 구조가 더 명시적입니다. 그러나 중첩이 많아지면 코드가 복잡해질 수 있습니다.
    • Consumer 내부의 렌더 함수가 부분적으로만 재렌더링될 수 있습니다. 이는 React.memo를 활용해 최적화할 수 있습니다.
    • 리액트 공식문서는 더이상 사용하지 말라고 한다.
  • 간단한 함수형 컴포넌트에서는 useContext가 더 적합합니다.
  • 클래스형 컴포넌트복잡한 Context 구조에서는 Consumer가 적절할 수 있습니다.
  • 여러 Context를 동시에 사용할 때는 useContext가 훨씬 편리합니다.
  • 기본적으로 성능 차이는 크지 않음. 그러나 복잡한 Context 구조에서 Consumer는 부분적인 렌더링 최적화가 가능할 수 있습니다. 반면 useContext는 컴포넌트 전체가 재렌더링되므로, 이 점을 고려해야 합니다.

 

결론

최근에는 Hook 스타일로 작성하는 추세이기 때문에 대부분의 경우에 useContext를 사용해서 처리했던 것 같다. 그게 더 간단하기 때문인 듯? 

Context API 는 결국 props Drilling 이라고 하는 계속 props를 하위 컴포넌트로 전달하는 것보다 이게 나으니까 발생된 개념이라고 봐야한다.는 게 자료조사와 실험 끝에 내린 결론이다.

전역상태관리 와의 차이

그럼 또 질문이 나올 수 밖에 없다. redux, recoil, zustand 와 같은 전역상태관리 대비해서 나은 점은 무엇이지? context API 는 그 특성상 useContext를 사용하고 있는 컴포넌트가 포함된 모든 부모컴포넌트를 리렌더링 한다. 그럴 필요가 없는데, 그러기 때문에 전역상태관리 라는 것이 나오게 된 것.

전역상태관리 라이브러리를 사용하면, 상태가 변경된 컴포넌트만 리렌더링 하는 것을 알 수 있다. (Store = 상태, Reducer = 액션)

 

참고

https://ko.react.dev/reference/react/createContext#consumer

 

createContext – React

The library for web and native user interfaces

ko.react.dev

 

https://medium.com/lunit/redux%EA%B0%80-%ED%95%84%EC%9A%94%ED%95%98%EB%8B%A4%EB%8A%94-%EA%B2%83%EC%9D%84-%EC%96%B8%EC%A0%9C-%EC%95%8C-%EC%88%98-%EC%9E%88%EB%82%98%EC%9A%94-426a148da64d

 

Redux가 필요하다는 것을 언제 알 수 있나요?

이 글은 Simon Schwartz의 "When do I know I’m ready for Redux?"를 번역한 글입니다.

medium.com

 

 

댓글