Zustand - 간단한 React 상태 관리


Zustand는 React를 위한 경량 상태 관리 라이브러리입니다. Redux보다 훨씬 간단하고, Context API의 리렌더링 문제를 해결합니다. 보일러플레이트 없이 직관적인 API를 제공합니다.



언제 사용하나요?



  • 전역 상태가 필요하지만 Redux는 과한 경우

  • 여러 컴포넌트에서 공유하는 상태 관리

  • Context의 불필요한 리렌더링 방지

  • 비동기 액션이 빈번한 경우



설치 및 기본 사용


npm install zustand

// store.js
import { create } from "zustand";

const useStore = create((set) => ({
// 상태
count: 0,
user: null,

// 액션
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
setUser: (user) => set({ user }),
reset: () => set({ count: 0, user: null }),
}));

export default useStore;


컴포넌트에서 사용


import useStore from "./store";

function Counter() {
// 필요한 상태만 구독 (선택적 리렌더링)
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);

return (
<div>
<span>{count}</span>
<button onClick={increment}>+1</button>
</div>
);
}

// 여러 상태 한번에 가져오기
function Profile() {
const { user, setUser } = useStore((state) => ({
user: state.user,
setUser: state.setUser,
}));

return <div>{user?.name}</div>;
}


비동기 액션


const useStore = create((set, get) => ({
users: [],
loading: false,
error: null,

fetchUsers: async () => {
set({ loading: true, error: null });
try {
const res = await fetch("/api/users");
const users = await res.json();
set({ users, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},

// get()으로 현재 상태 접근
addUser: async (user) => {
const { users } = get();
await api.createUser(user);
set({ users: [...users, user] });
},
}));


미들웨어 - persist (로컬 스토리지)


import { create } from "zustand";
import { persist } from "zustand/middleware";

const useStore = create(
persist(
(set) => ({
theme: "light",
setTheme: (theme) => set({ theme }),
}),
{
name: "app-storage", // localStorage 키
partialize: (state) => ({ theme: state.theme }), // 일부만 저장
}
)
);


미들웨어 - devtools


import { devtools } from "zustand/middleware";

const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set(
(state) => ({ count: state.count + 1 }),
false,
"increment" // 액션 이름 (DevTools에 표시)
),
}),
{ name: "MyStore" }
)
);


슬라이스 패턴 (대규모 스토어)


// userSlice.js
const createUserSlice = (set) => ({
user: null,
login: (user) => set({ user }),
logout: () => set({ user: null }),
});

// cartSlice.js
const createCartSlice = (set) => ({
items: [],
addItem: (item) => set((s) => ({
items: [...s.items, item]
})),
});

// store.js
const useStore = create((...a) => ({
...createUserSlice(...a),
...createCartSlice(...a),
}));


컴포넌트 외부에서 사용


// API 호출 등에서 직접 사용
const { getState, setState, subscribe } = useStore;

// 현재 상태 조회
const currentUser = getState().user;

// 상태 변경
setState({ count: 10 });

// 상태 변화 구독
const unsubscribe = subscribe(
(state) => state.user,
(user) => console.log("User changed:", user)
);


Redux vs Zustand 비교









항목ReduxZustand
보일러플레이트많음최소
러닝커브높음낮음
번들 크기~12KB~1KB
Provider 필요필수불필요
미들웨어풍부기본 제공