반응형
소개
Zustand는 React를 위한 작고 빠른 상태 관리 라이브러리입니다. Redux의 복잡성을 줄이면서도 강력한 기능을 제공하는 Zustand의 사용법을 알아보겠습니다.
설치
# npm
npm install zustand
# yarn
yarn add zustand
# pnpm
pnpm add zustand
기본적인 스토어 생성과 사용
스토어 생성
// stores/useCounterStore.ts
import { create } from 'zustand';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
const useCounterStore = create<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
export default useCounterStore;
컴포넌트에서 사용
// components/Counter.tsx
import useCounterStore from '../stores/useCounterStore';
function Counter() {
const { count, increment, decrement, reset } = useCounterStore();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
}
비동기 액션 처리
// stores/useUserStore.ts
interface UserState {
user: User | null;
loading: boolean;
error: string | null;
fetchUser: (id: string) => Promise<void>;
}
const useUserStore = create<UserState>((set) => ({
user: null,
loading: false,
error: null,
fetchUser: async (id) => {
try {
set({ loading: true, error: null });
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
set({ user, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
}
}));
상태 구독과 선택적 업데이트
// 특정 상태만 구독
function UserName() {
const userName = useUserStore((state) => state.user?.name);
return <div>{userName}</div>;
}
// 여러 상태 구독
function UserStatus() {
const { loading, error } = useUserStore((state) => ({
loading: state.loading,
error: state.error
}));
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return null;
}
미들웨어 활용
로깅 미들웨어
const logMiddleware = (config) => (set, get, api) =>
config(
(...args) => {
console.log('이전 상태:', get());
set(...args);
console.log('다음 상태:', get());
},
get,
api
);
const useStore = create(
logMiddleware((set) => ({
// store 구현
}))
);
영속성 미들웨어
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
// store 구현
}),
{
name: 'storage-key',
getStorage: () => localStorage
}
)
);
타입스크립트와의 통합
interface TodoState {
todos: Todo[];
addTodo: (text: string) => void;
removeTodo: (id: string) => void;
toggleTodo: (id: string) => void;
}
interface Todo {
id: string;
text: string;
completed: boolean;
}
const useTodoStore = create<TodoState>((set) => ({
todos: [],
addTodo: (text) =>
set((state) => ({
todos: [...state.todos, {
id: Date.now().toString(),
text,
completed: false
}]
})),
removeTodo: (id) =>
set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
}))
}));
성능 최적화
메모이제이션 활용
function TodoList() {
const todos = useTodoStore((state) => state.todos);
const addTodo = useTodoStore((state) => state.addTodo);
// 메모이제이션된 필터링
const completedTodos = useMemo(
() => todos.filter(todo => todo.completed),
[todos]
);
return (
<div>
{completedTodos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</div>
);
}
선택적 렌더링
const TodoCount = memo(() => {
const count = useTodoStore((state) => state.todos.length);
return <div>Total todos: {count}</div>;
});
실전 활용 예제
인증 상태 관리
interface AuthState {
user: User | null;
token: string | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
updateUser: (userData: Partial<User>) => void;
}
const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
login: async (credentials) => {
const response = await loginApi(credentials);
set({ user: response.user, token: response.token });
},
logout: () => set({ user: null, token: null }),
updateUser: (userData) =>
set((state) => ({
user: state.user ? { ...state.user, ...userData } : null
}))
}),
{
name: 'auth-storage'
}
)
);
마치며
Zustand는 단순하면서도 강력한 상태 관리 솔루션을 제공합니다. Redux의 보일러플레이트 없이도 복잡한 상태 관리를 할 수 있으며, TypeScript와의 통합도 매우 자연스럽습니다.
사용 시 주의사항
- 상태 업데이트는 항상 불변성을 지켜야 합니다.
- 큰 상태 객체는 적절히 분리하여 관리하세요.
- 성능 최적화를 위해 선택적 구독을 활용하세요.
'TypeScript' 카테고리의 다른 글
[React] memo를 사용한 컴포넌트 최적화 (0) | 2024.12.25 |
---|---|
[React Query] useMutation과 useQuery (0) | 2024.12.24 |
[React Query] QueryClient : 효율적인 상태관리와 데이터 캐싱 (0) | 2024.12.19 |
프론트엔드와 백엔드 간 Enum 동기화 (0) | 2024.12.07 |
React 환경 변수 : .env (1) | 2024.12.05 |