리액트로 프로젝트를 하다 보면 상태 관리가 점점 복잡해져서 props drilling
때문에 골치가 아픈 순간이 와요 😅
저도 팀 프로젝트에서 전역 상태 관리가 필요해져서, 가볍고 사용하기 쉬운 Zustand
를 도입해봤습니다.
이번 글에서는 Zustand의 기본적인 사용법과 직접 사용하면서 느낀 점을 정리해볼게요!
🧩 Zustand 설치
1
2
3
npm install zustand
# 또는
yarn add zustand
🗂️ 기본 스토어 만들기
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;
increase: () => void;
decrease: () => void;
reset: () => void;
}
export const useCounterStore = create<CounterState>((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
- create: Zustand에서 스토어를 만드는 함수
- count: 전역 상태로 관리할 값
- increase / decrease / reset: 상태를 업데이트하는 액션 함수
⚡ 컴포넌트에서 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//components/Counter.tsx
import { useCounterStore } from "@/store/useCounterStore";
export default function Counter() {
const { count, increase, decrease, reset } = useCounterStore();
return (
<div className="flex flex-col items-center gap-3 p-4">
<h2 className="text-xl font-bold">카운터: {count}</h2>
<div className="flex gap-2">
<button
onClick={increase}
className="rounded-lg bg-blue-500 px-4 py-2 text-white"
> +
</button>
<button
onClick={decrease}
className="rounded-lg bg-red-500 px-4 py-2 text-white"
> -
</button>
<button
onClick={reset}
className="rounded-lg bg-gray-500 px-4 py-2 text-white"
>
reset
</button>
</div>
</div>
);
}
이제 어디서든 useCounterStore()
를 호출해서 같은 상태를 공유할 수 있어요. 특히 Context API
보다 보일러플레이트가 적고, 리렌더링 최적화도 자동으로 해줘서 정말 편리합니다 👍
🔑 상태 선택적 구독
Zustand의 또 다른 장점은 필요한 상태만 구독할 수 있다는 점이에요.
1
const count = useCounterStore((state) => state.count);
이렇게 쓰면 count 값이 변경될 때만 컴포넌트가 리렌더링돼서 성능적으로도 효율적이에요 🚀
📝 사용 후기
팀 프로젝트에서 Zustand를 적용하면서 가장 좋았던 점은, 정말 가볍고 간단하다는 거였어요. Redux처럼 보일러플레이트가 많지 않고, Context API처럼 리렌더링 이슈로 고생할 일도 줄어들었죠. 특히 useXXXStore 형태로 훅을 만들어 쓰니, 유지보수도 훨씬 편했습니다.