
Kim JeongHyeonFrontend Focused Fullstack Developer
Context API의 리렌더 문제와 Zustand의 선택적 구독 패턴을 비교하고, 실전 사용 기준을 정리합니다.
React 상태관리에서 Zustand와 Context API를 언제 쓸지 정리합니다.
Context가 변경되면 하위의 모든 컴포넌트가 리렌더됩니다:
// 문제: count가 바뀌면 theme만 쓰는 컴포넌트도 리렌더
const AppContext = createContext({ count: 0, theme: "dark" });
function ThemeLabel() {
const { theme } = useContext(AppContext); // count 변경 시에도 리렌더!
return <span>{theme}</span>;
}Zustand는 selector 패턴으로 필요한 값만 구독합니다:
import { create } from "zustand";
interface AppStore {
count: number;
theme: string;
increment: () => void;
setTheme: (t: string) => void;
}
const useAppStore = create<AppStore>((set) => ({
count: 0,
theme: "dark",
increment: () => set((s) => ({ count: s.count + 1 })),
setTheme: (theme) => set({ theme }),
}));
// count가 바뀌어도 리렌더 안 됨
function ThemeLabel() {
const theme = useAppStore((s) => s.theme);
return <span>{theme}</span>;
}| 기준 | Context API | Zustand |
|---|---|---|
| 변경 빈도 | 낮음 (테마, 언어) | 높음 (폼, 카운터) |
| 구독자 수 | 적음 | 많음 |
| 컴포넌트 외부 접근 | 불가 | 가능 |
| 번들 크기 | 0 (내장) | ~1KB |
| DevTools | 없음 | 있음 |
| Middleware | 없음 | persist, immer 등 |
이 포트폴리오에서는 테마와 언어는 Context API, 에디터 상태는 Zustand를 사용합니다. 변경 빈도가 낮고 구독자가 명확한 경우 Context가 충분하고, 복잡한 상태 로직이 필요하면 Zustand가 적합합니다.
// Zustand persist middleware — 새로고침해도 상태 유지
const useEditorStore = create(
persist(
(set) => ({
draft: "",
setDraft: (draft: string) => set({ draft }),
}),
{ name: "editor-draft" }
)
);