Zustand에 대해 알아보자.
Zustand
Zustand는 redux에 비해 간단하고 직관적인 상태 관리(Store) 라이브러리입니다.
심플한 API : Redux처럼 복잡한 액션/리듀서 없음
가벼움 : minified + gzip 기준 약 1KB
서브스크립션 최적화 : 필요한 상태만 업데이트됨
미들웨어 지원 : persist, devtools, immer 등 확장 가능
Store
Store는 여러 상태(State)를 중앙에서 관리하는 패턴을 말합니다.
컴포넌트 간 공유할 데이터를 중앙에서 관리하여 전달하는 중간 단계 컴포넌트가 필요치 않으므로, 컴포넌트 간 결합도를 낮추고 유지/보수를 쉽게 만듭니다.
차별점
Redux
Redux에 비해서 간결하고 Provider 필요 없이 상태 변경이 가능합니다.
Valtio
Valtio는 가변 상태 모델을 기반으로 하고 Propertiy aceess를 통해서 랜더링 최적화를 합니다.
Zustand는 불변 상태 모델을 기반, 렌더링 최적화에 selector 사용
Jotai & Recoil
Jotai는 atom 기반으로 해서 복잡한 상태 관리 로직을 구성할 수 있습니다.
Zustand는 single store 기반
create 함수
Store를 생성하는 가장 기본적인 함수입니다.
create() 함수 내부에서 set, get을 사용해서 상태와 액션(함수)들을 정의합니다.
set: 상태를 업데이트하는 함수get: 현재 상태를 읽는 함수
1
2
3
4
5
6
7
import { create } from "zustand";
const useBearStore = create((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}));
Store 생성하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// store/useCounterStore.ts
import { create } from "zustand";
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
export 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 }),
}));
Hook으로 사용하는 방식
React 컴포넌트 내에서만 사용 가능한 방식으로 상태 변경 시 자동으로 리렌더링됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// components/Counter.tsx
import { useCounterStore } from "@/store/useCounterStore";
export default function Counter() {
const { count, increment, decrement, reset } = useCounterStore();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>증가</button>
<button onClick={decrement}>감소</button>
<button onClick={reset}>리셋</button>
</div>
);
}
일부만 사용하기
1 2
const count = useCounterStore((state) => state.count); const increment = useCounterStore((state) => state.increment);
Hook 없이 사용해보기
Zustand는 내부적으로 create() 함수를 통해 store 객체를 반환합니다.
객체에는 메서드가 포함되어 있어 Hook 없이도 사용할 수 있습니다.
React 컴포넌트 외부에서도 자유롭게 사용 가능합니다.
상태 변경 감지 및 자동 리렌더링 없음
getState(): 현재 상태를 반환setState(partialState): 상태를 직접 설정subscribe(listener): 상태 변화가 일어날 때 콜백 실행destroy(): 모든 Store 삭제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useUserStore } from "@/stores/userStore";
// 현재 상태 가져오기
const currentUser = useUserStore.getState().user;
// 상태 직접 설정
useUserStore.setState({ user: { id: "1", name: "길동", email: "" } });
// subscribe 사용
const unsub = useUserStore.subscribe((state) => {
console.log("변경된 유저:", state.user);
});
// Store 삭제
useUserStore.destroy();
상태 분할
Zustand는 각 Store가 독립된 상태 저장소입니다.
즉, Store가 변경되어도 다른 Store를 사용하는 컴포넌트는 리렌더되지 않습니다.
목적에 따라 모듈을 나누면 유지보수성과 가독성이 좋아집니다.
1
2
3
4
5
6
7
8
9
10
11
12
// useUserStore.ts
const useUserStore = create((set) => ({
user: null,
login: (name) => set({ user: { name } }),
logout: () => set({ user: null }),
}));
// useThemeStore.ts
const useThemeStore = create((set) => ({
darkMode: false,
toggle: () => set((s) => ({ darkMode: !s.darkMode })),
}));
Redux DevTools
Redux DevTools Extension은 브라우저 개발자 도구에서 상태 변화의 이력을 시각적으로 추적할 수 있게 해주는 확장입니다.
이 확장은 Zustand에서도 사용할 수 있습니다.
상태 변경 이력 추적
- 상태가 어떻게 바뀌었는지 순서대로 나열
- 이전 상태와 다음 상태를 비교
- 어떤 액션이 상태를 바꿨는지 확인 가능
시간 되감기
- 상태 변경을 되돌릴 수 있음
- 특정 시점으로 “롤백”할 수 있음
상태 수동 조작 가능
- DevTools에서 값을 직접 입력해서 상태 변경 가능
Zustand는 devtools 미들웨어를 통해 Redux DevTools와 연동됩니다.
상태가 크거나 민감한 경우 DevTools 연동은 성능 이슈나 보안상 주의 필요
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// store/useCounterStore.ts
import { create } from "zustand";
import { devtools } from "zustand/middleware";
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
}
export const useCounterStore = create<CounterState>()(
devtools(
(set) => ({
count: 0,
increment: () =>
set((state) => ({ count: state.count + 1 }), false, "increment"),
decrement: () =>
set((state) => ({ count: state.count - 1 }), false, "decrement"),
}),
{ name: "CounterStore" } // DevTools에서 store 이름으로 표시됨
)
);
