2026. 1. 2. 21:58ㆍSTUDY
Zustand란 무엇인가?
Zustand는 발행/구독(Pub/Sub) 모델을 기반으로 하는 작고 빠르며 확장 가능한 상태 관리 라이브러리입니다.
Flux 패턴을 따르되, 보일러플레이트(반복되는 코드)를 최소화하여 개발자 경험을 극대화
- 간결함: Redux처럼 복잡한 설정이나 Provider로 감싸는 과정이 필요 없습니다.
- 성능: 상태가 변경될 때 리렌더링을 최적화하는 기능이 내장되어 있습니다.
- 유연성: 특정 라이브러리에 종속되지 않아 React 없이도 사용할 수 있습니다.
✓ 상태 관리 라이브러리란?
프론트엔드 개발에서 상태(State)는 관리해야하는 모든 동적 데이터를 의미한다. (로그인여부, 입력창의 텍스트 등등)
상태관리라이브러리는 이런 데이터들을 컴포넌트 사이에 효율적으로 전달하고, 데이터가 수정된 경우 화면을 업에트하기 위한 도구
✓ 스토어(Store)의 정의
스토어는 애플리케이션의 중앙 데이터 저장소이자 상태 관리 센터
→ 데이터를 한곳에 모아두고, 아무나 수정하지 못하게 관리하며 데이터가 수정될때마다 필요한 컴포넌트에게 전달
중앙화: 모든 컴포넌트가 부모-자식 관계와 상관없이 직접 스토어에 접근
스토어의 3가지 구성 요소
1) 상태 (State / 변수) -- 기억해야 할 실제 데이터
2) 액션 (Action / 메서드) -- 상태를 변경하는 방법
외부에서 데이터를 직접 수정하는 게 아니라, 반드시 이 '액션'을 통해서만 수정
3) 구독 (Subscription) -- 상태가 바뀌었을 때 화면을 다시 그리도록 연결하는 메커니즘
🧩
스토어는 우리 앱의 정보를 안전하게 보관하고 관리하는 중앙 데이터 센터
Zustand는 이 스토어를 매우 가볍고 직관적으로 만들 수 있게 해주는 툴이다.
✓ 발행/구독(Pub/Sub) 모델
(유튜브 채널 알림 설정같은 느낌)
- 발행(Publish): 정보(상태)를 가지고 있는 주체(Store)가 데이터가 변경되었음을 알리는 것.
- 구독(Subscribe): 특정 데이터가 궁금한 컴포넌트들이 스토에 '데이터 바뀌면 알려줘' 라고 등록해두는것.
e.g. Store(발행자)는 유저 정보를 가지고 있다 → 마이페이지 컴포넌트(구독자)에서 유저정보를 구독하고 있다. → 유저가 닉네임을 변경하면, 스토어는 닉네임이 바뀐걸 발행하고, 구독하던 마이페이지 컴포넌트는 소식을 듣고 화면을 그림
✓ Flux 패턴
Meta(페이스북)이 제안한 [데이터는 무조건 한 방향으로 흐른다] 는 규칙
유저가 직접 store를 수정하는게 아닌, 미리 정의된 login(), logout()같은 Action을 통해서만 상태를 수정 → 데이터 흐름 예측 가능
✓ 리렌더링을 최적화
바뀐 데이터와 상관 있는 컴포넌트만 다시 화면에 그리는 것
Zustand는 Selector(선택자) 기능을 통해 최적화를 할수 있다.
e.g. store에 email, name 정보를 가지고 잇는경우,
유저의 이름만 보여주는 컴포넌트는 store의 이메일이 아무리 바껴도 name이 바뀌지 않았으므로 리런더링 되지 않음
const UserName = () => { // selector를 사용해 name만 '선택'해서 가져옴 const name = useAuthStore((state) => state.user?.name); return <div>{name}</div>; };
핵심 개념 및 옵션
1) create()
상태(State)와 상태를 변경하는 액션(Action)을 정의하는 저장소(Store)를 만들수 있다.
2) set()
상태를 업데이트하는 함수
이전 상태를 인자로 받아 새로운 상태로 병합
Store 정의 (`useAuthStore.ts`)
import { create } from 'zustand';
// 1. 데이터 모델 정의
interface User {
id: number;
name: string;
nickname: string;
email: string;
}
// 2. 스토어 상태 및 액션의 인터페이스 정의
interface AuthState {
user: User | null;
isLoggedIn: boolean;
login: (userData: User) => void;
logout: () => void;
updateNickname: (newNickname: string) => void;
}
// 3. 순수 Zustand 스토어 생성 (미들웨어가 생략)
export const useAuthStore = create<AuthState>((set) => ({
// 초기 상태
user: null,
isLoggedIn: false,
// 로그인: 전체 객체를 교체
login: (userData) => set({
user: userData,
isLoggedIn: true
}),
// 로그아웃: 초기값으로 리셋
logout: () => set({
user: null,
isLoggedIn: false
}),
// 닉네임 수정: 현재 상태(state)를 참조하여 일부만 변경
updateNickname: (newNickname) => set((state) => ({
user: state.user
? { ...state.user, nickname: newNickname }
: null
})),
}));
3) get()
현재 스토어에 저장된 상태를 조회하는 함수
- 액션함수 내부에서 현재 상태를 참조하는 경우
- 비동기 작업 (API호출 등)을 수행할 때, 현재 유저정보나 토큰이 필요한 경우
get 예시
- 액션을 실행하기 전에 현재 상태를 확인 하는 용도
export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
isLoggedIn: false,
updateUserIfChanged: (newUserData: User) => {
const currentUser = get().user;
if (currentUser?.id === newUserData.id) {
console.log("변경사항이 없어 업데이트를 생략합니다.");
return;
}
set({ user: newUserData });
}
}));
- 여러 상태 값을 조합하여 하나의 결과를 도출
export const useAuthStore = create<AuthState>((set, get) => ({
user: { name: '홍길동', role: 'ADMIN' },
isLoggedIn: true,
checkAdminPrivilege: () => {
const { user, isLoggedIn } = get();
if (isLoggedIn && user?.role === 'ADMIN') {
return "관리자 권한이 확인되었습니다.";
}
return "권한이 없습니다.";
}
}));
4) Middleware 미들웨어
• persist: localStorage나 sessionStorage에 자동으로 저장하여 새로고침해도 데이터가 유지
• devtools: 크롬 개발자 도구에서 상태변화를 모니터링
→ 실제 배포 시, 개발환경에서만 활성화 되도록 설정하거나 그대로 두어도 성능상 큰 지장을 주지 않지만, 디버깅용으로는 필수
✓ 브라우저 저장소: localStorage vs sessionStorage
구분 localStorage sessionStorage 유지기간 브라우저를 닫아도 영구적으로 유지 탭이나 창이 닫으면 즉시 삭제 공유범위 같은 도메인의 모든 창/탭 현재 탭 내부에서만 유지 (다른탭과 공유 불가) 용량 약 5MB~10MB 15MB 주요 용도 자동 로그인 토큰, 사용자 설정(다크모드) 일회성 입력폼데이터, 비회원 장바구니
컴포넌트에서 store 사용 예시
로그인 컴포넌트 (LoginComponent.tsx)
import React from 'react';
import { useAuthStore } from './useAuthStore';
export default funciton LoginComponent(){
// 스토어에서 필요한 상태와 함수를 가져옴
const { user, isLoggedIn, login, logout, updateNickname } = useAuthStore();
const handleLogin = () => {
// 실제 환경에서는 API 호출 결과값을 넣게 됨
const mockUser = {
id: 1,
name: '홍길동',
nickname: '길동이',
email: 'gildong@example.com'
};
login(mockUser);
};
const handleChangeNickname = (e: React.ChangeEvent<HTMLInputElement>) => {
updateNickname(e.target.value);
};
if (!isLoggedIn || !user) {
return (
<div>
<p>로그인이 필요합니다.</p>
<button onClick={handleLogin}> 로그인 테스트 </button>
</div>
);
}
return (
<div>
<h2>환영합니다, {user.name}님!</h2>
<p>현재 닉네임: {user.nickname}</p>
<div className="mt-4 space-y-2">
<input type="text" placeholder="새 닉네임 입력" onChange={handleChangeNickname}/>
<button onClick={logout}>로그아웃</button>
</div>
</div>
);
};
💡나는 주로 어떻게 사용했지?
지금 하고 개발하고 있는 프로젝트에선 useUserStore.ts와 useLoadingStore.ts 두 개의 스토어를 먼저 구축하여 관리하고 있습니다.
- useUserStore.ts: 유저 상태 및 정보
- useLoadingStore.ts: 페이지, API 호출, 액션 단위의 전역 로딩 상태 관리
아직 진행중이기 때문에 어떤 파일이 더 생겨날진 봐야겠지만 :-)
리액트 배우면서 zustand를 처음사용했고 지금도 사용하고 있기때문에 다른 라이브러리랑 비교하긴 어렵지만, 로직이 간단해서 만족하며 사용하고 있습니다.
_
Nextjs로 프로젝트를 진행할때,
"서버에서 가져온 데이터를 어떻게 클라이언트 스토어에 안전하게 동기화(Hydration)할 것인가?" 였습니다.
그 부분은 다음 포스팅에서 작성해야겠습니다. 🤯
Zustand와 Next.js, 안전한 데이터 동기화(Hydration)
Next.js의 서버 사이드 렌더링(SSR) 환경으로 넘어오면 한 가지 고민이 생깁니다."서버에서 받아온 데이터를 어떻게 클라이언트의 Zustand 스토어에 안전하게 전달할 것인가?"useEffect로 넣기엔 깜빡임
radan.tistory.com
💻 공식문서 확인
https://zustand.docs.pmnd.rs/getting-started/introduction
Introduction - Zustand
How to use Zustand
zustand.docs.pmnd.rs

'STUDY' 카테고리의 다른 글
| Next.js(App Router) URL의 파라미터(id)를 가져오는 방식 (서버 컴포넌트와 클라이언트 컴포넌트) (0) | 2026.01.12 |
|---|---|
| SEO를 위한 시맨틱 마크업 (0) | 2026.01.10 |
| Zustand와 Next.js, 안전한 데이터 동기화(Hydration) (0) | 2026.01.04 |
| React Router DOM부터 App Router까지 (0) | 2026.01.03 |
| React Hooks 정리 (0) | 2026.01.01 |