dev.syw

props 없이 깊은 컴포넌트까지 값을 전달하는 Context API.

Context로 전역 상태 공유

테마, 로그인 사용자, 언어 설정처럼 앱 곳곳에서 필요한 값을 props로 일일이 내려보내면 번거롭습니다. Context 는 이런 값을 트리 어디서든 꺼내 쓰게 해 줍니다.

학습 목표

  • props drilling 문제가 무엇인지 안다.
  • createContext, Provider, useContext 의 역할을 안다.
  • 테마 전환 예제를 직접 만들 수 있다.
  • Context를 언제 쓰고 언제 피해야 하는지 판단할 수 있다.

props drilling 문제

중간 컴포넌트들은 값을 쓰지도 않으면서 그저 아래로 전달만 하는 경우가 생깁니다.

function App() {
  const [theme, setTheme] = useState('light');
  return <Page theme={theme} />;
}
function Page({ theme }) {       // 안 쓰는데 받음
  return <Toolbar theme={theme} />;
}
function Toolbar({ theme }) {    // 또 안 쓰는데 받음
  return <Button theme={theme} />;
}
function Button({ theme }) {     // 여기서야 사용
  return <button className={theme}>버튼</button>;
}

이렇게 props가 여러 층을 거쳐 전달되는 걸 props drilling 이라고 합니다. Context로 이 사슬을 끊을 수 있습니다.

createContext · Provider · useContext

3단계로 사용합니다.

import { createContext, useContext, useState } from 'react';

// 1) Context 생성 (기본값 지정 가능)
const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('light');
  // 2) Provider로 감싸 값을 공급
  return (
    <ThemeContext.Provider value={theme}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Button() {
  // 3) useContext로 어디서든 꺼내 쓰기
  const theme = useContext(ThemeContext);
  return <button className={theme}>버튼</button>;
}
단계함수역할
생성createContext(기본값)통로를 만든다
공급<Context.Provider value={...}>값을 트리에 흘려보낸다
소비useContext(Context)가장 가까운 Provider 값을 읽는다

테마 전환 예제

값과 함께 변경 함수도 객체로 묶어 공급하면 어디서든 토글할 수 있습니다.

const ThemeContext = createContext(null);

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const toggle = () => setTheme((t) => (t === 'light' ? 'dark' : 'light'));

  return (
    <ThemeContext.Provider value={{ theme, toggle }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemeButton() {
  const { theme, toggle } = useContext(ThemeContext);
  return (
    <button className={theme} onClick={toggle}>
      현재 테마: {theme}
    </button>
  );
}

function App() {
  return (
    <ThemeProvider>
      <ThemeButton />
    </ThemeProvider>
  );
}

💡 TIP — Provider 로직을 ThemeProvider 컴포넌트로 따로 빼면 App 이 깔끔해지고 재사용도 쉬워집니다.

주의점

Context는 강력하지만 만능은 아닙니다.

  • 리렌더 범위 — Provider의 value 가 바뀌면 그 값을 쓰는 모든 소비자가 다시 렌더됩니다. 자주 바뀌는 큰 값은 분리하거나 useMemo 로 감싸세요.
// value가 매 렌더 새 객체 → 소비자 항상 리렌더
<ThemeContext.Provider value={{ theme, toggle }}>

// useMemo로 참조 안정화
const value = useMemo(() => ({ theme, toggle }), [theme]);
  • 모든 걸 Context로 옮기지 말 것 — 한두 단계만 내려가는 props는 그냥 props가 낫습니다. Context는 "정말 전역적인" 값에만.
  • Provider 밖에서 쓰면 기본값을 받습니다. 기본값을 null 로 두고 커스텀 훅에서 검사하면 실수를 빨리 잡을 수 있습니다.
function useTheme() {
  const ctx = useContext(ThemeContext);
  if (ctx === null) throw new Error('ThemeProvider 안에서만 사용하세요');
  return ctx;
}

⚠️ 주의 — Context는 "전역 상태 관리 도구"라기보다 "값 전달 통로"입니다. 복잡한 상태 로직은 useReducer 와 함께 쓰거나 별도 상태 관리 라이브러리를 고려하세요.

요약

  • props drilling: 중간 컴포넌트가 안 쓰는 props를 계속 전달하는 문제.
  • Context는 createContextProvideruseContext 3단계로 쓴다.
  • 값과 변경 함수를 객체로 묶어 공급하면 전역 토글이 쉽다.
  • value 가 바뀌면 모든 소비자가 리렌더 → useMemo 로 안정화.
  • 한두 단계 전달은 그냥 props. 전역 값에만 Context.

연습문제

  1. 로그인한 사용자 정보({ name, role })를 공급하는 UserContext 를 만들고, 깊은 자식에서 이름을 표시하세요.
  2. 위 테마 예제에 dark 일 때 배경을 검게 만드는 CSS 클래스를 연결해 보세요.
  3. Provider 밖에서 useContext 를 호출하면 어떤 값이 나오는지 실험하고 설명하세요.
  4. value 객체를 useMemo 로 감쌌을 때와 안 감쌌을 때 소비자 리렌더 차이를 확인하세요.

힌트 — 3번은 createContext 의 기본값이 답입니다. 4번은 소비자에서 console.log('render') 로 확인.

💡 연습문제 풀이

불러오는 중…

함께 보면 좋은 자료

댓글 0

React.js” 강좌에 대한 댓글입니다.

댓글을 작성하려면 로그인이 필요합니다.