Context API — 전역 상태 공유의 기본
Context API는 Props drilling 없이 컴포넌트 트리 전체에 데이터를 전달하는 React 내장 기능입니다.
인증 상태, 테마, 언어 설정처럼 여러 화면에서 공유해야 하는 데이터가 있습니다. Props로 하나하나 전달하면 코드가 지저분해지는데, Context가 이 문제를 해결합니다.
기본 사용법
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;
}
적용
// 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 예제
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의 성능 문제
// 문제: 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 분리
// 관심사별로 Context를 분리
function App() {
return (
<AuthProvider> {/* 인증만 */}
<ThemeProvider> {/* 테마만 */}
<LocaleProvider> {/* 언어만 */}
<MainApp />
</LocaleProvider>
</ThemeProvider>
</AuthProvider>
);
}
해결 방법 2: useMemo로 value 안정화
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 API | Zustand/Redux |
|---|---|---|
| 설치 | 불필요 (내장) | 패키지 필요 |
| 학습 비용 | 낮음 | 중간 |
| 성능 최적화 | 수동 (분리, useMemo) | 자동 (selector) |
| 미들웨어 | 없음 | persist, devtools 등 |
| 적합한 데이터 | 자주 안 변하는 설정값 | 자주 변하는 앱 상태 |
정리
- Context API는 Props drilling을 해결 하는 React 내장 기능입니다
- 커스텀 Hook 으로 감싸면 타입 안전성과 에러 처리를 확보할 수 있습니다
- 성능을 위해 Context를 관심사별로 분리 하세요
- 자주 변하는 데이터에는 Context보다 외부 상태관리 라이브러리 가 적합합니다
- 인증, 테마, 언어 같은 앱 전체 설정 에 가장 적합합니다
댓글 로딩 중...