TestForge | Aidevops | 📊 Plogger ✍️ Blog 📚 Docs
plogger

AI DevOps Korea

AI 서비스 개발, 운영, 성능개선을 하나의 루프로 연결합니다

aidevops.kr에서 LLMOps, RAG, AI Agent, 관측성, 평가, 비용-성능 최적화를 실전 운영 관점으로 정리합니다.

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

· 수정 4월 16일
React Context API로 전역 상태 관리하기 다이어그램
이 글에서 다루는 핵심 흐름, 아키텍처 구조, 주요 판단 포인트를 한눈에 이해할 수 있도록 정리한 그림입니다.
React Context API는 단순히 props drilling을 피하는 도구가 아닙니다. 트리 전반에 걸쳐 공통된 상태를 배포하는 메커니즘이기 때문에, 잘 쓰면 애플리케이션 구조를 단순하게 만들고, 잘못 쓰면 광범위한 리렌더링과 책임 혼합을 불러옵니다.

Context가 잘 맞는 상태와 아닌 상태

Context는 “전역 상태”라는 표현 때문에 모든 상태 관리 문제의 답처럼 보이지만 실제로는 적합한 영역이 꽤 명확합니다.

잘 맞는 경우:

  • 테마, 로케일, 사용자 세션 정보
  • 화면 전반에서 반복적으로 참조하는 설정값
  • 변경 빈도가 낮고 소비자가 넓게 퍼진 데이터

잘 맞지 않는 경우:

  • 자주 바뀌는 폼 입력 상태
  • 서버에서 가져오는 목록과 캐시 데이터
  • 복잡한 업데이트 규칙이 많은 대규모 상태

즉, Context는 “공유 범위가 넓은 값”에는 강하지만 “변경 빈도가 높은 상태 저장소”로 쓰기에는 부담이 있습니다.

Provider는 작고 분명해야 한다

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;
}

좋은 Context는 보통 역할이 하나입니다. AuthContext, ThemeContext, LocaleContext처럼 관심사를 나누면 변경 영향 범위를 줄이기 쉽습니다.

Context의 가장 큰 비용은 리렌더링이다

Provider의 value가 바뀌면 해당 Context를 구독하는 소비자들이 다시 렌더링됩니다. 이것이 Context를 무겁게 만드는 핵심 이유입니다.

실무에서는 아래 전략이 유용합니다.

  • 하나의 거대한 Context 대신 여러 작은 Context로 분리
  • 자주 바뀌는 값과 거의 고정된 값을 분리
  • 객체를 매 렌더마다 새로 만들지 않도록 구조를 단순화
  • 정말 자주 바뀌는 상태는 Zustand, Jotai 같은 도구 고려

인증 Context는 자주 쓰이지만 경계가 중요하다

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',
      headers: { 'Content-Type': 'application/json' },
      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>
  );
}

여기서 중요한 것은 인증 Context가 “로그인 API 호출기” 이상의 역할을 하려고 할 때입니다. 권한 체크, 토큰 갱신, 세션 복원, 라우팅 가드, 프로필 조회를 모두 한 Provider에 넣으면 빠르게 비대해집니다. 인증 관련 책임은 Context, API layer, router guard를 적절히 나누는 편이 좋습니다.

서버 상태는 Context보다 전용 도구가 더 낫다

자주 보는 실수는 API 응답 목록을 Context에 넣고 캐시처럼 사용하는 것입니다. 서버 상태는 만료, 재시도, 동기화, refetch 같은 문제가 함께 따라오므로 TanStack Query 같은 전용 도구가 더 적합합니다.

  • Context: 앱 내부에서 공유하는 값
  • Query 도구: 서버에서 가져와 동기화해야 하는 값

이 경계를 나누기 시작하면 프런트엔드 상태 설계가 훨씬 단순해집니다.

선택 기준

상황더 적합한 선택
테마, 언어, 사용자 세션Context API
비동기 서버 데이터TanStack Query
자주 바뀌는 클라이언트 상태Zustand / Jotai
강한 규칙과 큰 규모의 전역 상태Redux Toolkit

자주 하는 실수

  • 하나의 AppContext에 모든 상태를 집어넣는 방식
  • 서버 데이터를 Context에 캐시처럼 쌓는 패턴
  • Provider value에 매번 새 객체를 만들어 하위 리렌더링을 키우는 것
  • 커스텀 훅 없이 useContext를 직접 남발하는 구조
  • 상태와 액션, 권한 정책, 네트워크 호출을 한 Context에 섞는 방식

정리

React Context API는 작고 공통된 상태를 공유할 때 매우 강력합니다. 하지만 모든 전역 상태를 책임지는 범용 저장소로 쓰기 시작하면 설계와 성능 모두 빠르게 무거워집니다. Context는 “넓게 공유되지만 자주 바뀌지 않는 값”에 집중시키고, 서버 상태와 복잡한 상태 관리 문제는 다른 도구에 맡기는 것이 실무에서 훨씬 안정적입니다.

운영 환경에서 어려워지는 지점

  • Context는 가벼워 보이지만 자주 바뀌는 상태를 담기 시작하면 넓은 provider가 불필요한 렌더링과 책임 은닉을 만든다.
  • 편해서 쓴다는 이유로 context를 남용하면 어디서나 접근 가능하지만 어디서 책임지는지는 흐려진 상태가 된다.
  • context끼리 암묵적으로 의존하면 provider 순서와 초기화 로직이 버그 원인이 된다.

중요한 아키텍처 결정

  • context는 theme, auth 세션 형태, feature flag, dependency injection 같은 비교적 안정적인 횡단 관심사에 주로 쓴다.
  • 자주 바뀌는 서버 유래 데이터는 context보다 전용 server-state 도구에 둔다.
  • 거대한 app context 하나보다 목적이 분명한 여러 provider를 선호한다.

실무 예시

좋은 context는 원시 내부 상태보다 안정적인 인터페이스를 노출한다.

type AuthContextValue = {
  userId: string | null
  isAuthenticated: boolean
  signOut: () => Promise<void>
}

const AuthContext = createContext<AuthContextValue | null>(null)

피해야 할 안티패턴

  • 자주 업데이트되는 리스트나 대시보드 데이터를 전역 context에 넣는 것.
  • context를 아키텍처 경계의 대체물로 쓰는 것.
  • 안전한 접근 훅 없이 nullable context를 직접 export하는 것.

운영 체크리스트

  • provider 주변의 불필요한 재렌더링 지점을 측정한다.
  • provider 트리를 의도적으로 설계하고 문서화한다.
  • 필요한 곳에서는 context 값과 callback 안정성을 유지한다.
  • 해당 context가 वास्तव में server cache, route state, local state여야 하는지 다시 본다.

최종 판단

Context는 안정적인 공용 의존성과 설정 흐름에 매우 좋다. 빠르게 바뀌는 애플리케이션 상태의 기본 저장소로 쓰기에는 적합하지 않다.

Continue Reading

다음으로 읽기 좋은 글

다음 탐색

이 주제를 시스템 관점으로 더 이어서 보기