React 상태 관리의 가벼운 대안인 Zustand는 Redux보다 훨씬 간결하고, Context API보다 더 강력한 상태 공유를 제공합니다.
🐻 Zustand란?
- 독일어로 "상태(state)"를 뜻함
- Minimal한 API, boilerplate 없는 사용성
- 글로벌 상태를 말들되, context나 provider없이도 동작함
- 비동기 로직, 미들웨어, 퍼포먼스 최적화도 지원
1. 📦 Zustand 기본 사용법
설치
npm install zustand
스토어 만들기
import { create } from 'zustand'
interface CountState {
count: number;
increase: () => void;
reset: () => void;
};
const useStore = create<CountState>((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
reset: () => set({ count: 0 }),
}));
사용 방법 (컴포넌트에서)
import React from 'react'
import { useStore } from './store'
function Counter() {
const count = useStore((state) => state.count)
const increase = useStore((state) => state.increase)
return (
<div>
<h1>{count}</h1>
<button onClick={increase}>+</button>
</div>
)
}
✅ 팁: 원하는 상태만 선택적으로 가져오면 불필요한 리렌더를 줄일 수 있어 성능적으로 매우 유리합니다!
2. 🔄 상태 선택자 & 최적화
useStore 안에서 부분 상태만 구독 (개별 구독)
- 분리된 개별 구독
- 각 상태가 독립적으로 구독되어 해당 상태만 변경될 때 리렌더링됨
const count = useStore((state) => state.count);
const increase = useStore((state) => state.increase);
이 방식은 필요없는 상태 변화로 인한 리렌더링을 방지합니다.
useShallow 사용 (그룹 구독)
Zustand의 최신 버전에서는 shallow보다 useShallow를 사용하는 것이 권장됩니다. useShallow는 Zustand 내에서 상태 변화를 감지할 때 얕은 비교(shallow comparison)를 수행하는 특별한 훅입니다.
- shallow => useShallow 권장
- 얕은 비교를 수행하는 특별한 훅
- 그룹 내 어느 하나라도 변경되면 컴포넌트가 리렌더링됨
- 하지만 그룹 내의 값들 간에는 얕은 비교를 통해 최적화
// 이전 shallow 방식
import shallow from 'zustand/shallow'
const { count, increase } = useStore(
(state) => ({ count: state.count, increase: state.increase }),
shallow
)
// 새로운 권장 방식
import { useShallow } from 'zustand/react/shallow'
const { count, increase } = useStore(
useShallow((state) => ({ count: state.count, increase: state.increase }))
)
따라서 성능 최적화를 고려할 때:
- 함께 자주 변경되는 상태들은 useShallow 로 그룹화
- 독립적으로 변경되는 상태들은 개별 호출로 분리
이렇게 상황에 맞게 구독 방식을 선택하는 것이 좋습니다.
3. 🧩 Middleware 사용하기
Zustand는 middleware로 sotre 기능을 확장할 수 있습니다. 대표적인 미들웨어는 다음과 같습니다:
✅ devtools 미들웨어
npm install zustand-middleware
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
}))
)
⭐팁: Chrome Redux DevTools에서 Zustand 상태 추적 가능!
✅ persist미들웨어 (로컬스토리지 저장)
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
const useStore = create(
persist((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
}),
{
name: 'my-app-storage',
}
)
)
⭐팁: 로컬스토리지 외에도 sessionStorage, AsyncStorage등 다양한 storage 엔진으로 교체 가능
4. 🆚 Jotai와 Zustand 차이점
항목 | Zustand | Jotai |
상태 구조 | 단일 store (object 형태) | 여러 atom으로 구성 |
업데이트 방식 | set 함수로 상태 직접 업데이트 | useAtom 훅으로 직접 읽고 쓰기 가능 |
컴포넌트 리렌더링 | 필요한 상태만 선택해서 리렌더 최소화 가능 | atom 단위로 최적화, 기본적으로 고성능 |
미들 웨어 | devtools, persist 등 다양하게 지원 | 공식 미들웨어가 적고 가벼움 |
러닝 커브 | React 경험자에게 익숙함 | Recoil과 비슷한 atom 기반, 조금 생소할 수 있음 |
SSR | 지원함 | 더친화적인 방식으로 SSR 지원 |
✅ 팁: jotai는 각 상태 단위를 atom으로 쪼개서 관리하기 때문에 복잡한 상태로직에는 분리할 수 있지만, 단순한 상태 공유에는 매우 적합합니다. 반면 Zustand는 하나의 store에서 로직을 통합 관리할 수 있어 앱 규모가 커질수록 더 저거
5. 💡 마무리 요약
항목 | 설명 |
기본생성 | create 함수로 store 생성 |
선택자 | (state) => state.someValue 사용으로 리렌더 최적화 |
useShallow | 객체/배열 등 shallow compare로 리렌더 줄이기 |
미들웨어 | devtools, persist, immer 등 다양한 기능 추가 가능 |
상태 초기화 | 초기 상태를 변수로 두고 reset 메서드 구현 가능 |
'인턴' 카테고리의 다른 글
[ Troubleshooting🛠️ ] 모바일 Swiper에서 absolute 요소가 깜박이는 현상 해결 (0) | 2025.04.28 |
---|---|
[ Troubleshooting🛠️ ] sticky 포지션과 overflow: hidden 충돌 해결하기 (0) | 2025.04.21 |
[성능 최적화⚙️] Set 활용해보기 (1) | 2025.04.14 |
[ Troubleshooting🛠️ ] Tailwind 다크모드 깜박임 현상 해결하기 (0) | 2025.04.14 |
자주 쓰는 Docker 명령어 정리 (0) | 2025.04.14 |