Zustand 상태 설계 가이드
Zustand 상태 설계 가이드
Zustand 상태 설계
정의
- 애플리케이션의 복잡도에 따라 Zustand 스토어를 구조화하고 상태를 효율적으로 관리하기 위한 설계 방식
- 단순한 데이터 저장을 넘어 확장성, 유지보수성, 성능을 고려한 아키텍처를 정의한다.
특징
- 유연한 스토어 구성
- 하나의 거대한 단일 스토어로 관리하거나, 도메인별로 여러 개의 스토어로 분리할 수 있다.
- 슬라이스 패턴(Slice Pattern) 지원
- 여러 개의 작은 상태 조각(Slice)을 정의하고 이를 하나의 메인 스토어로 합성하는 방식을 지원한다.
- 비정형적 구조
- Redux와 달리 특정 폴더 구조나 액션 정의 방식이 강제되지 않아 프로젝트 상황에 맞게 커스터마이징이 가능하다.
장점
- 프로젝트 규모에 맞춰 유연하게 확장 가능하다.
- 슬라이스 패턴을 통해 도메인별로 로직을 분리하여 코드 가독성을 높일 수 있다.
- 불필요한 리렌더링을 방지하기 위한 선택적 구독(Selector) 적용이 용이하다.
단점
- 정해진 규칙이 없으므로 초기 설계 단계에서 팀원 간의 컨벤션 정의가 중요하다.
- 잘못된 설계로 인해 스토어 간 의존성이 복잡해질 경우 추적이 어려울 수 있다.
설계 패턴 및 가이드
1. 단일 스토어 vs 멀티 스토어
- 단일 스토어: 프로젝트 전체의 상태를 하나의 스토어에서 관리한다. 상태 간의 참조가 쉬우나 규모가 커지면 관리가 어렵다.
- 멀티 스토어: 인증(Auth), 사용자 정보(User), 설정(Settings) 등 도메인별로 스토어를 나눈다. 관심사 분리가 명확하지만 스토어 간 데이터를 주고받을 때 추가적인 처리가 필요하다.
2. 슬라이스 패턴 (Slice Pattern)
대규모 프로젝트에서 하나의 스토어를 유지하면서도 코드를 모듈화할 때 사용하는 방식이다.
// counterSlice.ts
export const createCounterSlice = (set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
});
// userSlice.ts
export const createUserSlice = (set) => ({
name: '',
setName: (name) => set({ name }),
});
// store.ts
import { create } from 'zustand';
import { createCounterSlice } from './counterSlice';
import { createUserSlice } from './userSlice';
export const useBoundStore = create((...a) => ({
...createCounterSlice(...a),
...createUserSlice(...a),
}));3. Flat한 상태 구조 유지
상태 객체가 너무 깊게 중첩(Deeply Nested)되면 업데이트 로직이 복잡해지고 실수할 확률이 높다. 가급적 평탄한(Flat) 구조를 유지하는 것이 좋다.
// Bad: 너무 깊은 중첩
const state = {
user: {
profile: {
settings: {
theme: 'dark'
}
}
}
};
// Good: 평탄한 구조
const state = {
userId: 1,
theme: 'dark',
isNotificationEnabled: true
};4. Selector를 통한 최적화
컴포넌트에서 필요한 상태만 골라서 사용함으로써 불필요한 리렌더링을 방지한다.
// 전체 스토어를 구독하지 않고 필요한 것만 선택
const count = useCounterStore((state) => state.count);
const increase = useCounterStore((state) => state.increase);결론
Zustand는 설계의 자유도가 매우 높다. 소규모 프로젝트에서는 단일 스토어로 빠르게 개발하고, 규모가 커짐에 따라 슬라이스 패턴이나 멀티 스토어 방식을 도입하여 구조화하는 것이 효율적이다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.