Zustand 세팅
Docs
Zustand는 가볍고 직관적인 전역 상태 관리 라이브러리이다.
주로 클라이언트(UI) 상태를 중앙에서 관리하기 위해 사용한다.
왜 Zustand를 쓸까?
클라이언트 상태의 어려움
React에서 전역 상태를 단순히 Props + Context로만 관리하면 다음 문제가 자주 발생한다.
- 여러 컴포넌트 간 상태 공유가 복잡해짐 (Props Drilling)
- Context 남용 시 불필요한 리렌더링 증가
- 상태 로직이 UI 컴포넌트에 흩어짐
- 상태 구조가 커질수록 관리가 어려워짐
- 디버깅이 어려움 (어디서 무엇이 바뀌었는지 추적이 힘듦)
이런 문제를 간결하고 실용적인 방식으로 해결해 주는 것이 Zustand다.
Zustand를 안 쓰면 무엇을 직접 해야 할까?
아래를 직접 구현해야 한다:
- 전역 상태 저장소 설계
- 상태 변경 로직 중앙화
- 불필요한 리렌더링 방지
- 상태 변경 추적(디버깅)
- 상태 지속(persist) 로직
- 상태 구조 확장 시 리팩토링
가능하지만, 앱이 커질수록 상태 구조가 복잡해지고 유지보수가 어려워진다.
언제 Zustand를 도입하면 좋을까?
다음과 같은 경우 도입을 권장한다:
- 여러 화면에서 공통으로 사용하는 UI 상태가 있는 경우
(로그인 정보, 테마, 사이드바 열림 여부, 모달 상태 등) - Redux가 과하게 무겁다고 느껴질 때
- Context API의 성능 이슈가 걱정될 때
- 상태 구조는 단순하지만 전역 공유가 필요한 경우
- 보일러플레이트 없이 빠르게 상태를 관리하고 싶을 때
설치
bash
npm install zustand기본 설정
실제 프로젝트에서는 src/store/ 폴더에 상태를 모아 관리하는 것이 좋다.
1. 기본 Store 예시
typescript
// src/store/ui.ts
import { create } from 'zustand';
interface UIState {
isSidebarOpen: boolean;
toggleSidebar: () => void;
setSidebar: (open: boolean) => void;
}
export const useUIStore = create<UIState>((set) => ({
isSidebarOpen: false,
toggleSidebar: () =>
set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
setSidebar: (open) => set({ isSidebarOpen: open }),
}));2. 컴포넌트에서 사용
tsx
import { useUIStore } from '@/store/ui';
function SidebarToggle() {
const { isSidebarOpen, toggleSidebar } = useUIStore();
return (
<button onClick={toggleSidebar}>{isSidebarOpen ? '닫기' : '열기'}</button>
);
}3. 필요한 상태만 구독하기 (성능 최적화)
전체 상태를 가져오면 불필요한 리렌더링이 발생할 수 있다.
tsx
// 나쁜 예: 전체 상태 구독 (모든 변경에 리렌더링)
const { isSidebarOpen, toggleSidebar } = useUIStore();
// 좋은 예: 필요한 값만 선택적 구독
const isSidebarOpen = useUIStore((state) => state.isSidebarOpen);권장 방식: selector로 필요한 값만 선택해서 구독한다.
4. Persist(로컬 저장) 설정 (선택)
새로고침 후에도 상태를 유지하고 싶다면:
ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface AuthState {
token: string | null;
setToken: (token: string) => void;
clearToken: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
token: null,
setToken: (token) => set({ token }),
clearToken: () => set({ token: null }),
}),
{
name: 'auth-storage',
}
)
);- 보통 인증 정보, 테마, 사용자 설정 등은 persist를 사용하고, 서버 데이터는 TanStack Query에 맡기는 것이 권장된다.
Zustand의 장단점
장점
- 매우 가볍고 빠름
- 보일러플레이트가 거의 없음
- React와 자연스럽게 결합
- Context보다 성능적으로 유리
- Redux보다 훨씬 단순
단점
- 대규모 상태 구조에서는 설계가 중요
- 상태가 많아지면 폴더 구조 정리가 필요
- 미리 규칙을 정하지 않으면 혼잡해질 수 있음
언제 Zustand를 안 써도 될까?
- 컴포넌트 내부 상태만 필요한 경우 (useState로 충분)
- 상태 공유가 거의 없는 작은 앱
- 서버 상태가 대부분인 프로젝트(TanStack Query로 충분한 경우)
Zustand vs TanStack Query
| Zustand | TanStack Query | |
|---|---|---|
| 용도 | 클라이언트 상태 | 서버 상태 |
| 예시 | UI 상태, 사용자 설정 | API 응답 데이터 |
| 캐싱 | 수동 관리 | 자동 캐싱 |
| 리페칭 | 없음 | 자동 리페칭 |
TanStack Query는 서버 상태, Zustand는 UI 상태를 담당한다.
두 도구를 역할에 맞게 분리하면 코드가 훨씬 단순해진다.
즉, 서버 데이터는 Query, 화면 상태는 Store로 명확히 구분한다.