Zustand란?
Zustand : 전역 상태 관리 라이브러리
ㄴ Redux Toolkit, Context API보다 적은 코드와 직관적인 방식으로 상태 관리 가능
공식 홈페이지
Zustand
zustand-demo.pmnd.rs
Zustand 사용하기
- 패키지 설치
- npm install zustand 명령어를 사용하여 다운로드
- 폴더 구조 설계
- src/store/ 하위에 ts 파일로 생성
- store 파일 생성 및 기본 뼈대 정의
- store : 애플리케이션의 전역 상태(State)와 이를 변경하는 비즈니스 로직(Action)을 한데 모아 관리하는 보관소
- store에서 사용할 데이터와 함수의 타입 설계도를 interface로 정의
- Zustand의 create 훅을 사용하여 컴포넌트들이 구독할 전역 store 생성
- export const use***Store = create((set, get) => ({}));
- set : 스토어 내부의 데이터를 안전하게 변경하기 위해 사용하는 상태 변경 콜백 함수
- get : 특정 액션(함수) 내부에서 현재 스토어에 담긴 최신 상태값들을 동기적으로 참조하기 위해 사용하는 현재 상태 조회 함수

// src/store/countStore.ts
import { create } from "zustand";
type CountStore = {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
// use***Store
export const useCountStore = create<CountStore>((set) => ({
count: 0,
decrement: () => set((state) => ({ count: state.count - 1 })),
increment: () => set((state) => ({ count: state.count + 1 })),
reset: () => set({ count: 0 })
}));
// src/components/CountOutside.tsx
import { useCountStore } from "../store/countStore";
export default function CountOutside() {
const count = useCountStore((state) => state.count);
return (
<>
<h1>CountOutside: {count}</h1>
</>
);
}
// src/components/CountDisplay.tsx
import { useCountStore } from "../store/countStore";
export default function CountDisplay() {
const count = useCountStore((state) => state.count);
return (
<>
<h1>Count: {count}</h1>
</>
);
}
// src/components/CountButton.tsx
import { useCountStore } from "../store/countStore";
export default function CountButton() {
const increment = useCountStore((state) => state.increment);
const decrement = useCountStore((state) => state.decrement);
const reset = useCountStore((state) => state.reset);
return (
<>
<button onClick={decrement}>감소</button>
<button onClick={reset}>리셋</button>
<button onClick={increment}>증가</button>
</>
);
}
Zustand 매개변수
Zustand는 외부에서 매개변수를 전달받거나 비동기 함수를 사용할 때에도 매우 쉽게 처리 가능
매개변수 처리
// src/components/CountButton.tsx
import { useCountStore } from "../store/countStore";
export default function CountButton() {
const increment = useCountStore((state) => state.increment);
const decrement = useCountStore((state) => state.decrement);
const reset = useCountStore((state) => state.reset);
return (
<>
<button onClick={decrement}>감소</button>
<button onClick={reset}>리셋</button>
<button onClick={() => increment(5)}>증가</button>
</>
);
}
^ increment(5) --> 5라는 매개변수 전달
// src/store/countStore.ts
import { create } from "zustand";
type CountStore = {
count: number;
increment: (amount: number) => void;
decrement: () => void;
reset: () => void;
}
export const useCountStore = create<CountStore>((set) => ({
count: 0,
increment: (amount: number) => set((state) => ({ count: state.count + amount })), // 전달받은 값만큼 증가
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}));
^ amount: number
비동기 함수 처리
// src/store/countStore.ts
import { create } from "zustand";
type CountStore = {
count: number;
increment: (amount: number) => void;
decrement: () => void;
reset: () => void;
};
export const useCountStore = create<CountStore>((set) => ({
count: 0,
increment: async (amount: number) => { // async
await new Promise((resolve) => setTimeout(resolve, 1000)); // await new Promise
set((state) => ({ count: state.count + amount }));
},
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
^ async/await를 활용해 1초의 지연 후 상태를 갱신하는 비동기 함수 구현
Zustand persist
Zustand의 상태 데이터는 브라우저를 새로고침하면 모두 초기화됨
상태를 새로고침해도 유지되게 하기 위해서는 local storage, session storage에 상태를 동기화시켜야 함
ㄴ 다른 전역 상태 관리 라이브러리는 그러한 스토리지 작업을 직접 해야 해야 했지만, Zustand는 작업을 대신 처리해주는 persist 미들웨어 기능을 보유
미들웨어(middleware) : 특정 라이브러리의 핵심 동작을 확장하거나 가로채서 추가적인 기능을 수행할 수 있도록 지원하는 소프트웨어 구조
ㄴ 어떤 하나의 기능을 지칭하는 것은 X
Persist 미들웨어 구현 문법 및 사용법
- create 함수의 콜백 함수를 persist() 미들웨어 함수로 감싸줌
- persist()의 두 번째 매개변수로 객체 지정
- storage: createJSONStorage(() => sessionStorage) : 생략 시 기본값은 localStorage
- 해당 객체에 name 속성을 정하여 스토리지에 저장될 고유 Key를 선언
// src/store/countStore.ts
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
type CountStore = {
count: number;
increment: (amount: number) => void;
decrement: () => void;
reset: () => void;
};
export const useCountStore = create<CountStore>()(
persist(
(set) => ({
count: 0,
increment: (amount: number) => {
set((state) => ({ count: state.count + amount }));
},
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}),
{
name: "count-storage",
storage: createJSONStorage(() => sessionStorage) // 세션 스토리지에 저장되도록 설정
},
),
);
Zustand subscribeWithSelector
subscribeWithSelector 미들웨어 : 특정 상태가 변경될 때를 감지해서 특정 로직을 수행할 수 있는 구독 기능을 사용할 수 있게 해주는 미들웨어
subscribeWithSelector 미들웨어 구현 문법 및 사용법
- 스토어를 만들 때 subscribeWithSelector 미들웨어 함수로 감싸줌
- 구독할 변수, 변수값에 변화가 생기면 실행할 함수 설정
// src/store/countStore.ts
import { create } from "zustand";
import {
createJSONStorage,
persist,
subscribeWithSelector,
} from "zustand/middleware";
type CountStore = {
count: number;
increment: (amount: number) => void;
decrement: () => void;
reset: () => void;
};
export const useCountStore = create<CountStore>()(
subscribeWithSelector(
// subscribeWithSelector 미들웨어 적용
persist(
(set) => ({
count: 0,
increment: (amount: number) => {
set((state) => ({ count: state.count + amount }));
},
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}),
{
name: "count-storage",
storage: createJSONStorage(() => sessionStorage),
},
),
),
);
// src/components/Count.tsx
import { useEffect } from "react";
import CountGroup from "./CountGroup";
import { useCountStore } from "../store/countStore";
export default function Count() {
useEffect(() => {
const unsubscibe = useCountStore.subscribe(
(state) => state.count, // count 상태를 구독하고 있다가,
(newCount) => { // 상태에 변화가 생기면 해당 콜백 함수를 실행
console.log("newCount" + newCount);
},
);
return () => {
unsubscibe();
};
}, []);
return (
<>
<CountGroup />
</>
);
}
Zustand immer
immer 미들웨어 : 사용자가 불변성을 고려하지 않아도 자동으로 불변성을 처리해주는 미들웨어
- 패키지 설치
- npm install immer 명령어를 사용하여 다운로드
- 사용하기
- subscribeWithSelector 미들웨어 전체를 감싸는 것 X
- set 함수가 콜백 함수로 전달되는 함수의 영역을 감싸는 것 O
// src/count/countStore.ts
import { create } from "zustand";
import {
createJSONStorage,
persist,
subscribeWithSelector,
} from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
type CountStore = {
count: number;
increment: (amount: number) => void;
decrement: () => void;
reset: () => void;
};
export const useCountStore = create<CountStore>()(
subscribeWithSelector(
persist(
immer((set) => ({ // immer 미들웨어 적용
count: 0,
increment: (amount: number) => {
set((state) => {
state.count += amount;
});
},
decrement: () =>
set((state) => {
state.count -= 1;
}),
reset: () =>
set((state) => {
state.count = 0;
}),
})),
{
name: "count-storage",
storage: createJSONStorage(() => sessionStorage),
},
),
),
);
Zustand devtools
Zustand는 가벼운 상태 관리 라이브러리 --> 자체 GUI 디버깅 도구를 내장하고 있지 X
ㄴ 대신 브라우저 확장 프로그램인 Redux Devtools와 완벽하게 연동되는 미들웨어를 제공
서비스 배포(Production) 환경에서 상태 변화 흐름이 무방비하게 노출되면 보안 취약점이 될 수 있고, 성능 저하를 유발하기도 함
∴ 개발(Development) 환경에서만 DevTools가 켜지도록 조건부 제어하는 것이 필수적
DevTools 자동화 설정 문법
- devtools 미들웨어의 두 번째 매개변수 객체에 설정 주입
- name : 디버거 창 내에서 각 스토어를 식별하기 위한 고유 이름표 지정
- enabled : 환경 변수를 활용하여 현재 빌드 모드가 development일 때만 동적으로 활성화 처리
- { enabled: false } --> 연동되지 않도록 설정
- ㄴ 개발모드에선 true, 그렇지 않을 때에는 false라고 지정하면 됨
- { enabled: import.meta.env.MODE === "development" }
- ㄴ 자동화
// src/store/countStore.ts
import { create } from "zustand";
import {
createJSONStorage,
devtools,
persist,
subscribeWithSelector,
} from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
type CountStore = {
count: number;
increment: (amount: number) => void;
decrement: () => void;
reset: () => void;
};
export const useCountStore = create<CountStore>()(
devtools( // devtools 미들웨어 적용
subscribeWithSelector(
persist(
immer((set) => ({
count: 0,
increment: (amount: number) => {
set((state) => {
state.count += amount;
});
},
decrement: () =>
set((state) => {
state.count -= 1;
}),
reset: () =>
set((state) => {
state.count = 0;
}),
})),
{
name: "count-storage",
storage: createJSONStorage(() => sessionStorage),
},
),
),
),
);
'React > 타입스크립트로 배우는 리액트(React.js) : 기초부터 최신 기술까지' 카테고리의 다른 글
| 섹션 18. React Router v7 (0) | 2026.07.12 |
|---|---|
| 섹션 17. 데이터 통신 심화 (0) | 2026.06.22 |
| 섹션 16. 데이터 통신 (1) | 2026.06.11 |
| 섹션 13. 전역 상태 관리 - Context API (0) | 2026.06.09 |
| 섹션 12. 사이드 이펙트와 컴포넌트 최적화 (0) | 2026.05.22 |