plogger

React Context API로 전역 상태 관리하기

Context API란?

컴포넌트 트리 깊은 곳에 있는 컴포넌트에 props를 일일이 전달하지 않고 데이터를 공유할 수 있는 방법입니다.

기본 사용법

1. Context 생성

// context/ThemeContext.js
import { createContext, useContext, useState } from 'react'

const ThemeContext = createContext(null)

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light')

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

export function useTheme() {
  const context = useContext(ThemeContext)
  if (!context) throw new Error('useTheme must be used within ThemeProvider')
  return context
}

2. Provider로 감싸기

// App.jsx
import { ThemeProvider } from './context/ThemeContext'

function App() {
  return (
    <ThemeProvider>
      <Header />
      <Main />
    </ThemeProvider>
  )
}

3. 어느 컴포넌트에서나 사용

function Header() {
  const { theme, setTheme } = useTheme()

  return (
    <header className={theme === 'dark' ? 'bg-gray-900' : 'bg-white'}>
      <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
        테마 전환
      </button>
    </header>
  )
}

실전 예제 — 인증 Context

// context/AuthContext.js
import { createContext, useContext, useState } from 'react'

const AuthContext = createContext(null)

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null)

  async function login(email, password) {
    const res = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    })
    const data = await res.json()
    setUser(data.user)
  }

  function logout() {
    setUser(null)
  }

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  )
}

export const useAuth = () => useContext(AuthContext)

Context vs Zustand 선택 기준

상황선택
테마, 로케일 등 변경이 적은 데이터Context API
자주 변경되는 복잡한 상태Zustand / Jotai
서버 데이터 캐싱TanStack Query
대규모 앱 전역 상태Redux Toolkit

성능 최적화 — memo와 함께 사용

Context 값이 변경되면 하위 모든 컴포넌트가 리렌더링됩니다. memo로 불필요한 렌더링을 방지합니다.

import { memo } from 'react'

const ExpensiveChild = memo(function ExpensiveChild({ name }) {
  return <div>{name}</div>
})