
グローバルなstate値(あたい)を新(あたら)しく生成(せいせい)、修正(しゅうせい)できるように管理(かんり)することです。
ユーザー認証情報(にんしょうじょうほう)、テーマ情報(じょうほう)、カート情報(じょうほう)などを管理(かんり)します。
글로벌한 state 값을 새롭게 생성, 수정할 수 있도록 관리하는 것입니다.
사용자 인증 정보, 테마 정보, 장바구니 정보 등을 관리합니다.
propsを使(つか)って一段階(いちだんかい)ずつ下(した)に渡(わた)すと、Props Drilling問題(もんだい)が発生(はっせい)します。
この問題(もんだい)を解決(かいけつ)するためにContext API、Redux、Zustandなどがあります。
props를 사용해서 한 단계씩 아래로 전달하면 Props Drilling 문제가 발생합니다.
이 문제를 해결하기 위해 Context API, Redux, Zustand 등이 있습니다.
// Props Drilling 예시
<App>
<Parent user={user}>
<Child user={user}>
<GrandChild user={user}>
{/* 3단계를 거쳐 전달 */}
</GrandChild>
</Child>
</Parent>
</App>
| 状態管理(じょうたいかんり)ツール | 特徴(とくちょう) | 단점 |
|---|---|---|
| Context API | React 내장(ないぞう) | Provider 리렌더링 시 모든 하위 컴포넌트 리렌더링 |
| Redux | 강력(きょうりょく)한 기능(きのう) | 복잡한 설정 |
| Zustand | 가볍고 직관적(ちょっかんてき) | - |
Context API 한계: Provider가 리렌더링될 때마다 모든 하위 컴포넌트가 리렌더링 → 극소적인 데이터 공유에만 적합
多(おお)くの人(ひと)が使(つか)っており、容量(ようりょう)が非常(ひじょう)に軽(かる)く、直観的(ちょっかんてき)で学(まな)びやすいです。
TypeScriptとの統合(とうごう)も簡単(かんたん)で、ボイラープレートコードが少(すく)ないです。
많은 사람이 사용하고 있으며, 용량이 매우 가볍고, 직관적이어서 배우기 쉽습니다.
TypeScript와의 통합도 쉽고 보일러플레이트 코드가 적습니다.
npm i zustand
store/count.ts
import { create } from "zustand"; // store을 생성할 수 있음
type Store = {
count: number;
increase: () => void;
decrease: () => void;
};
export const useCountStore = create<Store>((set, get) => ({
count: 0,
increase: () => {
set((store) => ({ count: store.count + 1 }));
},
decrease: () => {
set((store) => ({ count: store.count - 1 }));
},
}));
| 要素(ようそ) | 役割(やくわり) | 설명 |
|---|---|---|
create | ストア生成(せいせい) | 전역 상태 저장소 생성 |
set | 状態(じょうたい)更新(こうしん) | 상태 업데이트 함수 |
get | 状態(じょうたい)取得(しゅとく) | 현재 상태 가져오기 |
pages/counter-page.tsx
import { Button } from "@/components/ui/button";
import { useCountStore } from "@/store/count";
export default function CounterPage() {
const store = useCountStore();
const { count, increase, decrease } = store;
return (
<div>
<h1 className="text-2xl font-bold">Counter</h1>
<div>{count}</div>
<div>
<Button onClick={decrease}>-</Button>
<Button onClick={increase}>+</Button>
</div>
</div>
);
}
객체 구조 분해:
const { count, increase, decrease } = store;로 필요한 값만 추출
一(ひと)つのコンポーネントでストア全体(ぜんたい)を使(つか)うと、不要(ふよう)なリレンダリングが発生(はっせい)します。
Selector関数(かんすう)を使(つか)って必要(ひつよう)な値(あたい)だけを選択(せんたく)します。
하나의 컴포넌트에서 스토어 전체를 사용하면 불필요한 리렌더링이 발생합니다.
Selector 함수를 사용해서 필요한 값만 선택합니다.
pages/counter-page.tsx
import Controller from "@/components/counter/controller";
import Viewer from "@/components/counter/viewer";
export default function CounterPage() {
return (
<div>
<h1 className="text-2xl font-bold">Counter</h1>
<Viewer />
<Controller />
</div>
);
}
components/counter/viewer.tsx
import { useCount } from "@/store/count";
export default function Viewer() {
const count = useCount();
return <div>{count}</div>;
}
components/counter/controller.tsx
import { Button } from "@/components/ui/button";
import { useDecreaseCount, useIncreaseCount } from "@/store/count";
export default function Controller() {
const increase = useIncreaseCount();
const decrease = useDecreaseCount();
return (
<div>
<Button onClick={increase}>-</Button>
<Button onClick={decrease}>+</Button>
</div>
);
}
개발자 도구 팁: React DevTools → Components → "Highlight updates when components render" 체크하면 리렌더링 확인 가능
store/count.ts
import { create } from "zustand";
type Store = {
count: number;
actions: {
increaseOne: () => void;
decreaseOne: () => void;
};
};
export const useCountStore = create<Store>((set, get) => ({
count: 0,
actions: {
increaseOne: () => {
set((store) => ({
count: store.count + 1,
}));
},
decreaseOne: () => {
set((store) => ({
count: store.count - 1,
}));
},
},
}));
// 커스텀 훅
export const useCount = () => {
const count = useCountStore((store) => store.count);
return count;
};
export const useIncreaseCount = () => {
const increase = useCountStore((store) => store.actions.increaseOne);
return increase;
};
export const useDecreaseCount = () => {
const decrease = useCountStore((store) => store.actions.decreaseOne);
return decrease;
};
| パターン | リレンダリング | 설명 |
|---|---|---|
useCountStore() | 전체(ぜんたい) | 모든 값 변경 시 리렌더링 |
useCountStore((s) => s.count) | count만 | count 변경 시만 리렌더링 |
| カスタムフック | 선택적(せんたくてき) | 안정적이고 재사용 가능 |
Selector 콜백: 원하는 값만 선택하여 불필요한 리렌더링 방지
特定(とくてい)のロジックに中間(ちゅうかん)処理(しょり)を追加(ついか)する道具(どうぐ)です。
ログ出力(しゅつりょく)、重複(じゅうふく)確認(かくにん)などができます。
특정 로직에 중간 처리를 추가하는 도구입니다.
로그 출력, 중복 확인 등을 할 수 있습니다.
stateとactionを分離(ぶんり)して型(かた)推論(すいろん)を簡単(かんたん)にします。
自動(じどう)で型(かた)を推論(すいろん)してくれます。
state와 action을 분리해서 타입 추론을 쉽게 합니다.
자동으로 타입을 추론해줍니다.
import { create } from "zustand";
import { combine } from "zustand/middleware";
export const useCountStore = create(
combine({ count: 0 }, (set, get) => ({
actions: {
increaseOne: () => {
set((state) => ({ count: state.count + 1 }));
},
decreaseOne: () => {
set((state) => ({ count: state.count - 1 }));
},
},
})),
);
combine 장점: state는 state끼리, action은 action끼리 묶어서 관리 → 자동 타입 추론
不変性(ふへんせい)を自動(じどう)で管理(かんり)してくれます。
複雑(ふくざつ)な状態(じょうたい)更新(こうしん)を簡単(かんたん)に作成(さくせい)できます。
불변성을 자동으로 관리해줍니다.
복잡한 상태 업데이트를 쉽게 작성할 수 있습니다.
npm i immer
import { create } from "zustand";
import { combine } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
export const useCountStore = create(
immer(
combine({ count: 0 }, (set, get) => ({
actions: {
increaseOne: () => {
set((state) => {
state.count += 1; // 불변성 관리 불필요!
});
},
decreaseOne: () => {
set((state) => {
state.count -= 1;
});
},
},
})),
),
);
export const useCount = () => {
const count = useCountStore((store) => store.count);
return count;
};
export const useIncreaseCount = () => {
const increase = useCountStore((store) => store.actions.increaseOne);
return increase;
};
export const useDecreaseCount = () => {
const decrease = useCountStore((store) => store.actions.decreaseOne);
return decrease;
};
| ミドルウェア | 機能(きのう) | 설명 |
|---|---|---|
combine | state/action 分離(ぶんり) | 타입 추론 자동화 |
immer | 不変性(ふへんせい)管理(かんり) | 직접 값 수정 가능 |
devtools | デバッグ | Redux DevTools 연동 |
persist | 永続化(えいぞくか) | localStorage 저장 |
subscribeWithSelector | 選択的(せんたくてき)구독(こうどく) | 특정 값만 감시 |
Zustandは軽量(けいりょう)で直観的(ちょっかんてき)な全域状態管理(ぜんいきじょうたいかんり)ライブラリです。
Selectorとミドルウェアで最適化(さいてきか)された状態管理(じょうたいかんり)ができます。
Zustand는 가볍고 직관적인 전역 상태 관리 라이브러리입니다.
Selector와 미들웨어로 최적화된 상태 관리를 할 수 있습니다.
핵심: 기본 스토어 생성 → 커스텀 훅으로 Selector 구현 → combine + immer로 편리한 상태 관리