인턴

Zustand 완전 정복 가이드

choijming21 2025. 4. 15. 11:54

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 메서드 구현 가능