Context API는 Props drilling 없이 컴포넌트 트리 전체에 데이터를 전달하는 React 내장 기능입니다.

인증 상태, 테마, 언어 설정처럼 여러 화면에서 공유해야 하는 데이터가 있습니다. Props로 하나하나 전달하면 코드가 지저분해지는데, Context가 이 문제를 해결합니다.


기본 사용법

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

// 1. Context 생성
interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  isLoading: boolean;
}

const AuthContext = createContext<AuthContextType | null>(null);

// 2. Provider 컴포넌트
function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  const login = async (email: string, password: string) => {
    setIsLoading(true);
    try {
      const userData = await loginAPI(email, password);
      setUser(userData);
    } finally {
      setIsLoading(false);
    }
  };

  const logout = () => {
    setUser(null);
  };

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

// 3. 커스텀 Hook (타입 안전성 확보)
function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth는 AuthProvider 안에서 사용해야 합니다');
  }
  return context;
}

적용

TSX
// App.tsx
export default function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <NavigationContainer>
          <RootNavigator />
        </NavigationContainer>
      </ThemeProvider>
    </AuthProvider>
  );
}

// 어떤 화면에서든 사용
function ProfileScreen() {
  const { user, logout } = useAuth();

  return (
    <View>
      <Text>{user?.name}님 환영합니다</Text>
      <Pressable onPress={logout}>
        <Text>로그아웃</Text>
      </Pressable>
    </View>
  );
}

테마 Context 예제

TSX
interface Theme {
  colors: {
    background: string;
    text: string;
    primary: string;
    card: string;
  };
}

const lightTheme: Theme = {
  colors: {
    background: '#ffffff',
    text: '#1a1a1a',
    primary: '#007AFF',
    card: '#f5f5f5',
  },
};

const darkTheme: Theme = {
  colors: {
    background: '#1a1a1a',
    text: '#ffffff',
    primary: '#0A84FF',
    card: '#2c2c2e',
  },
};

interface ThemeContextType {
  theme: Theme;
  isDark: boolean;
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | null>(null);

function ThemeProvider({ children }: { children: ReactNode }) {
  const [isDark, setIsDark] = useState(false);
  const theme = isDark ? darkTheme : lightTheme;

  const toggleTheme = () => setIsDark((prev) => !prev);

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

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error('useTheme은 ThemeProvider 안에서 사용해야 합니다');
  return context;
}

// 사용
function Card({ title }: { title: string }) {
  const { theme } = useTheme();

  return (
    <View style={{ backgroundColor: theme.colors.card, padding: 16 }}>
      <Text style={{ color: theme.colors.text }}>{title}</Text>
    </View>
  );
}

Context의 성능 문제

TSX
// 문제: value가 변경되면 모든 Consumer가 리렌더링됨
function AppProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');
  const [locale, setLocale] = useState('ko');

  // 이 객체는 리렌더링마다 새로 생성 → 모든 하위 컴포넌트 리렌더링
  return (
    <AppContext.Provider value={{ user, theme, locale, setUser, setTheme, setLocale }}>
      {children}
    </AppContext.Provider>
  );
}

해결 방법 1: Context 분리

TSX
// 관심사별로 Context를 분리
function App() {
  return (
    <AuthProvider>        {/* 인증만 */}
      <ThemeProvider>     {/* 테마만 */}
        <LocaleProvider>  {/* 언어만 */}
          <MainApp />
        </LocaleProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

해결 방법 2: useMemo로 value 안정화

TSX
function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  // value 객체를 메모이제이션
  const value = useMemo(() => ({
    user,
    login: async (email: string, password: string) => {
      const userData = await loginAPI(email, password);
      setUser(userData);
    },
    logout: () => setUser(null),
  }), [user]); // user가 변경될 때만 새 객체 생성

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
}

Context 값이 자주 변하는 데이터(카운터, 실시간 위치 등)에는 Context가 비효율적입니다. 이런 경우 Zustand 같은 외부 상태관리 라이브러리가 더 적합합니다.


Context vs 외부 상태관리

기준Context APIZustand/Redux
설치불필요 (내장)패키지 필요
학습 비용낮음중간
성능 최적화수동 (분리, useMemo)자동 (selector)
미들웨어없음persist, devtools 등
적합한 데이터자주 안 변하는 설정값자주 변하는 앱 상태

정리

  • Context API는 Props drilling을 해결 하는 React 내장 기능입니다
  • 커스텀 Hook 으로 감싸면 타입 안전성과 에러 처리를 확보할 수 있습니다
  • 성능을 위해 Context를 관심사별로 분리 하세요
  • 자주 변하는 데이터에는 Context보다 외부 상태관리 라이브러리 가 적합합니다
  • 인증, 테마, 언어 같은 앱 전체 설정 에 가장 적합합니다
댓글 로딩 중...