zustand-state-management은(는) 무엇을 하나요?
React에서 Zustand로 타입 안전한 전역 상태를 구축합니다. TypeScript, persist 미들웨어, devtools, 슬라이스 패턴, Next.js SSR 및 하이드레이션 처리를 지원합니다. 6가지 문서화된 오류를 방지합니다. React 상태 설정, Redux/Context 마이그레이션, 또는 하이드레이션 오류, TypeScript 추론, 무한 렌더 루프, persist 경합 조건 문제 해결 시 사용하세요.
주요 기능
- 타입 안전한 상태: TypeScript로 완전한 타입 추론 지원
- persist 미들웨어:
localStorage,AsyncStorage등에 상태 자동 저장 및 복원 - devtools: Redux DevTools와 통합 가능 (디버깅 용이)
- 슬라이스 패턴: 여러 개의 상태 조각을 하나의 store로 결합
- Next.js SSR: 하이드레이션 오류를 방지하는
useHydration훅 제공 - 6가지 오류 방지: 일반적인 Zustand 사용 오류를 사전에 차단
사용법
기본 store 생성
import { create } from 'zustand'
interface BearStore {
bears: number
increase: () => void
removeAll: () => void
}
export const useBearStore = create<BearStore>((set) => ({
bears: 0,
increase: () => set((state) => ({ bears: state.bears + 1 })),
removeAll: () => set({ bears: 0 }),
}))
persist 미들웨어 사용
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface ThemeStore {
theme: 'light' | 'dark'
toggleTheme: () => void
}
export const useThemeStore = create<ThemeStore>()(
persist(
(set) => ({
theme: 'light',
toggleTheme: () =>
set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light',
})),
}),
{
name: 'theme-storage', // localStorage 키 이름
}
)
)
devtools 통합
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
interface CounterStore {
count: number
increment: () => void
decrement: () => void
}
export const useCounterStore = create<CounterStore>()(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{
name: 'counter-store', // devtools에 표시될 이름
}
)
)
슬라이스 패턴
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
// 각 슬라이스 정의
interface FishSlice {
fishes: number
addFish: () => void
}
interface BearSlice {
bears: number
addBear: () => void
}
const createFishSlice = (set: any) => ({
fishes: 0,
addFish: () => set((state: any) => ({ fishes: state.fishes + 1 })),
})
const createBearSlice = (set: any) => ({
bears: 0,
addBear: () => set((state: any) => ({ bears: state.bears + 1 })),
})
// store 생성 시 슬라이스 결합
export const useBoundStore = create<FishSlice & BearSlice>()(
devtools(
persist(
(...a) => ({
...createFishSlice(...a),
...createBearSlice(...a),
}),
{ name: 'bound-store' }
)
)
)
Next.js SSR에서 하이드레이션 처리
// hooks/useHydration.ts
import { useEffect, useState } from 'react'
export const useHydration = () => {
const [hydrated, setHydrated] = useState(false)
useEffect(() => {
setHydrated(true)
}, [])
return hydrated
}
// components/MyComponent.tsx
import { useHydration } from '@/hooks/useHydration'
import { useThemeStore } from '@/stores/themeStore'
export default function MyComponent() {
const hydrated = useHydration()
const theme = useThemeStore((state) => state.theme)
if (!hydrated) {
return <div>Loading...</div> // 하이드레이션 전에는 기본값 표시
}
return <div>Current theme: {theme}</div>
}
방지되는 6가지 오류
- 하이드레이션 불일치: SSR에서 클라이언트 렌더링 시 텍스트/속성 불일치
- TypeScript 추론 실패: 제네릭 타입 누락으로 인한
any타입 추론 - 무한 렌더 루프: 컴포넌트 내부에서 selector 없이 전체 store 구독
- persist 경합 조건:
create와persist호출 순서 오류로 인한 상태 손실 - 구독 누수: 컴포넌트 언마운트 시 제거되지 않은 구독
- 불변성 위반: 상태를 직접 변경하여 예측 불가능한 동작 유발