[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
@@ -2,143 +2,250 @@
id: wiki-2026-0508-state-management-libraries
title: State Management Libraries
category: 10_Wiki/Topics
status: needs_review
status: verified
canonical_id: self
aliases: [P-REINFORCE-AUTO-D8B77E]
aliases: [State Management, React State Libraries, Zustand vs Jotai vs Redux]
duplicate_of: none
source_trust_level: A
confidence_score: 0.95
tags: [auto-reinforced]
verification_status: applied
tags: [state-management, react, zustand, jotai, valtio, redux]
raw_sources: []
last_reinforced: 2026-05-03
github_commit: "[P-Reinforce] Continuous Worker - State Management Libraries"
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: unspecified
framework: unspecified
language: TypeScript
framework: React 19
---
# [[State Management Libraries|State Management Libraries]]
# State Management Libraries
## 📌 한 줄 통찰 (The Karpathy Summary)
**State Management Libraries(상태 관리 라이브러리)**는 애플리케이션 내 여러 컴포넌트 간에 공유되는 상태(State)를 예측 가능하고 유지보수하기 쉬운 방식으로 중앙 집중화하여 관리하는 도구 및 아키텍처 패턴입니다. 다수의 뷰(View)가 동일한 상태에 의존하거나 상태를 변경해야 할 때 발생하는 'Prop Drilling' 문제를 해결하기 위해 고안되었습니다. 프레임워크별로 React의 Context API 및 React Query, Vue의 Pinia, Flutter의 BLoC 및 Riverpod 등으로 고도로 발전하며 대규모 시스템의 렌더링 성능과 로직 재사용성을 극대화하는 핵심 기술로 활용되고 있습니다.
## 한 줄
> **"매 state management 는 component tree 외부의 reactive store 추상화"**. 2026 기준 React server-side data 는 TanStack Query / RSC, client-only state 는 Zustand (top-down) / Jotai (bottom-up atomic) / Valtio (proxy mutable) 가 mainstream. Redux 는 legacy + 매우 큰 enterprise 만. 매 핵심 결정은 "어떤 abstraction 이 mental model 과 맞는가".
## 📖 구조화된 지식 (Synthesized Content)
소스 데이터에 기반한 프레임워크별 상태 관리 패턴과 핵심 원리는 다음과 같습니다.
## 매 핵심
* **Vue 생태계의 중앙 집중식 상태 관리 (Pinia)**
* 과거 공식 라이브러리였던 Vuex를 대체하여 **Pinia**가 새로운 표준으로 정착했습니다.
* Composition API 스타일을 지원하며 덜 복잡한 API(보일러플레이트 감소)와 **TypeScript 사용 시 강력한 타입 추론**을 제공합니다.
* 대규모 애플리케이션에서 Pinia는 전역 상태뿐만 아니라 액션(Action) 내부에 비동기 로직을 포함시켜, 컴포넌트의 책임을 순수한 UI 렌더링으로 한정시키는 핵심 역할을 수행합니다.
* **React 생태계의 상태 관리 패러다임**
* **Context API 활용**: 'Prop Drilling'을 막기 위해 Context API를 통한 암시적 상태 공유가 활용됩니다. 특히 복합 컴포넌트(Compound Components) 패턴은 하위 컴포넌트들이 하나의 응집된 기능을 수행하도록 상태를 공유하여 UI 유연성을 높입니다.
* **클라이언트 및 서버 상태 위임**: React 생태계에서는 Redux Toolkit, Zustand, Jotai 등의 도구가 널리 쓰이며, 특히 서버 데이터 동기화와 캐싱 로직은 **TanStack Query (React Query)**와 같은 라이브러리에 위임하여 클라이언트 로직을 단순화하는 패턴이 실전 표준으로 자리 잡았습니다.
* **Flutter의 상태 관리 아키텍처**
* 프로젝트의 규모와 요구사항에 따라 다양한 상태 관리 패턴이 경쟁적으로 사용됩니다.
* **BLoC (Business Logic Component)**: 스트림(Stream) 기반의 이벤트 중심 상태 관리 방식으로 엄격한 관심사 분리를 요구하여 대규모 엔터프라이즈 프로젝트에서 선호됩니다.
* **Provider & Riverpod**: 상대적으로 배우기 쉽고 유연한 의존성 주입을 제공하여 중소규모 프로젝트에서 높은 생산성을 보입니다. 특히 Riverpod은 MVVM 아키텍처와의 결합도가 높은 현대적인 패턴입니다.
### 매 4 가지 패러다임
- **Top-down store** (Zustand, Redux): 단일 store, selector
- **Atomic** (Jotai, Recoil): 작은 atom 의 graph
- **Proxy mutable** (Valtio, MobX): mutable object, auto-tracking
- **Server-state** (TanStack Query, SWR): cache + revalidation
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
상태 관리 라이브러리 및 패턴을 도입할 때 고려해야 할 기술적 제약과 부작용은 다음과 같습니다.
### 매 server vs client
- 2026 의 truth: 매 대부분의 "state" 는 사실 server data → 매 TanStack Query 로
- 진짜 client state (modal open, form draft, theme) → 매 Zustand / Jotai
* **SSR(Server-Side Rendering) 환경에서의 데이터 유출 위험**: 전역 상태를 단순한 싱글톤(Singleton) 패턴으로 구현할 경우, SSR 환경에서는 여러 서버 요청 간에 스토어 인스턴스가 공유되어 다른 사용자의 데이터가 유출(Cross-request state pollution)될 수 있습니다. 이를 방지하기 위해 Pinia 등은 요청마다 새로운 스토어 인스턴스를 생성하는 방식을 강제합니다.
* **복잡성과 보일러플레이트 증가**: Flutter의 BLoC나 일부 엄격한 상태 관리 패턴은 도메인 로직과 UI 간의 강한 관심사 분리 및 테스트 용이성을 제공하지만, 이를 위해 작성해야 할 보일러플레이트 코드가 크게 늘어나며 초기 학습 곡선(Learning Curve)이 가파르다는 단점이 있습니다.
* **렌더링 성능 최적화의 한계**: 상태 관리 스토어를 잘못 구조화하거나 Context를 넓은 범위에 과도하게 사용할 경우, 연관 없는 하위 컴포넌트까지 불필요하게 리렌더링(Re-rendering)되어 시스템 성능이 저하되는 부작용이 생길 수 있습니다.
### 매 응용
1. Zustand: 매 일반 SPA, mid-size app 의 default.
2. Jotai: 매 fine-grained reactivity, derived value graph.
3. Valtio: 매 game/canvas, mutable mental model 선호.
4. Redux Toolkit: 매 legacy migration, Redux DevTools 의존.
## 🔗 지식 연결 (Graph)
### Related Concepts
## 💻 패턴
#### [아키텍처/기반 기술]
* [[One-way Data Flow]]
* 연결 이유: Vue 및 React에서 단일 진실 공급원(Single source of truth)을 기반으로 한 뷰(View)와 액션(Action)의 상호작용 원리를 규정합니다.
* 이 개념을 통해 더 깊게 이해할 수 있는 부분: 상태가 오직 예측 가능한 방식으로만 변이(Mutation)되어야 하는 근본적인 이유와 아키텍처 설계 사상을 이해할 수 있습니다.
* [[Server-Side Rendering (SSR)]]
* 연결 이유: 대규모 웹 애플리케이션에서 전역 상태 라이브러리(Pinia 등)를 다룰 때 필수적으로 고려해야 하는 실행 컨텍스트입니다.
* 이 개념을 통해 더 깊게 이해할 수 있는 부분: 상태 관리 도구가 클라이언트 환경과 서버 환경 양쪽에서 하이드레이션(Hydration) 및 고립성을 유지하는 메커니즘을 이해할 수 있습니다.
### Zustand store with slice pattern
```typescript
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
#### [구현/활용 도구]
* [[Pinia]]
* 연결 이유: Vue 3 환경에서 Vuex를 대체하는 현대적인 공식 상태 관리 구현체입니다.
* 이 개념을 통해 더 깊게 이해할 수 있는 부분: Composition API와의 매끄러운 결합 방식과 TypeScript 환경에서의 향상된 타입 추론 기법을 학습할 수 있습니다.
* [[TanStack Query (React Query)]]
* 연결 이유: React(및 다른 프레임워크)에서 서버의 데이터를 페칭하고 캐싱하며, 클라이언트의 상태 관리 부담을 분리시키는 대표적인 도구입니다.
* 이 개념을 통해 더 깊게 이해할 수 있는 부분: 순수 UI 중심의 클라이언트 상태와, 외부 비동기 요청 중심의 서버 상태를 분리하는 전략적 패턴을 이해할 수 있습니다.
* [[BLoC]]
* 연결 이유: Flutter 생태계에서 널리 쓰이는 비즈니스 로직 컴포넌트 아키텍처입니다.
* 이 개념을 통해 더 깊게 이해할 수 있는 부분: 모바일 환경에서 스트림(Stream)을 통한 이벤트 중심 반응형 상태 관리와 엄격한 관심사 분리를 달성하는 법을 배울 수 있습니다.
type AuthSlice = {
user: { id: string; name: string } | null;
login: (u: { id: string; name: string }) => void;
logout: () => void;
};
### Deeper Research Questions
* 대규모 SSR(Server-Side Rendering) 애플리케이션에서 전역 상태 관리 도구(예: Pinia)가 요청 간 데이터 유출(Cross-request state pollution)을 방지하기 위해 사용하는 아키텍처적 격리 메커니즘은 무엇인가?
* 클라이언트 UI 전역 상태 관리와 외부 API 캐싱/동기화를 위한 서버 상태 관리(예: React Query)를 분리했을 때 얻게 되는 렌더링 성능적 이점과 아키텍처적 복잡성(한계)은 무엇인가?
* Flutter 프로젝트의 복잡성에 따라 BLoC 패턴의 엄격함과 Riverpod 패턴의 유연한 의존성 주입은 각각 어떤 비즈니스 시나리오(예: 엔터프라이즈 vs 스타트업 MVP)에서 최적의 효과를 발휘하는가?
* React에서 Context API를 활용한 복합 컴포넌트(Compound Components) 패턴이 Prop Drilling을 해결함과 동시에 재사용 가능한 대규모 디자인 시스템 구축에 기여하는 바는 무엇인가?
* Vue 2에서 Vue 3로 마이그레이션 시, 기존 Vuex 스토어를 Pinia로 이관할 때 `@vue/compat` 모드와 피처 플래그를 결합한 점진적 전환 전략은 구체적으로 어떻게 구성되는가?
type CartSlice = {
items: { id: string; qty: number }[];
add: (id: string) => void;
};
### Practical Application Contexts
* **Implementation:** 컴포넌트 계층이 깊어지면서 데이터를 하위로 계속 전달해야 하는 Prop Drilling 현상을 제거하고, 중앙 싱글톤 패턴(또는 Context)을 이용해 필요한 컴포넌트에서 상태를 직접 주입받아 구현합니다.
* **System Design:** 시스템 내 비즈니스 로직, 외부 서버 데이터 페칭 로직, 순수 UI 상태 로직의 도메인 경계를 설정하고, 각 레이어가 단일 책임만을 가지도록 상태 관리 시스템을 분리 설계합니다.
* **Operation / Maintenance:** 상태 변경을 유발하는 액션(Action)이 중앙에 캡슐화되므로, 예기치 않은 데이터 변이로 인한 버그 발생 시 상태 관리 스토어를 디버깅하여 오류의 원인을 신속하게 추적하고 수정할 수 있습니다.
* **Learning Path:** 단일 컴포넌트의 로컬 상태 다루기 -> 상위 컴포넌트로 상태 끌어올리기 -> Context나 Provide/Inject를 통한 상태 공유 -> 전역 상태 관리자(Pinia, BLoC 등) 도입 -> 비동기/서버 상태 특화 라이브러리(React Query) 분리 학습 순으로 이어집니다.
* **My Project Relevance:** 현재 개발 중인 프론트엔드나 모바일(React, Vue, Flutter) 아키텍처 설계 시, 프로젝트의 규모와 팀원의 숙련도에 가장 적합한 상태 관리 라이브러리와 전략을 채택하고 기술 부채를 방지하는 평가 기준으로 적용할 수 있습니다.
export const useStore = create<AuthSlice & CartSlice>()(
devtools(
persist(
immer((set) => ({
user: null,
login: (u) => set((s) => { s.user = u; }),
logout: () => set((s) => { s.user = null; }),
items: [],
add: (id) =>
set((s) => {
const it = s.items.find((i) => i.id === id);
if (it) it.qty += 1;
else s.items.push({ id, qty: 1 });
}),
})),
{ name: "app-store" },
),
),
);
### Adjacent Topics
* [[Micro-frontends]]
* 확장 방향: 거대한 애플리케이션을 독립적인 여러 하위 애플리케이션으로 분할할 때, 분할된 마이크로 프론트엔드 모듈 간의 전역 상태 공유와 동기화를 어떻게 보장할 것인지에 대한 아키텍처 확장 연구.
* [[Composition API]]
* 확장 방향: 거대한 중앙 상태 관리 라이브러리 없이도, 프레임워크 자체의 반응성 모델(예: Composables, Custom Hooks)을 사용하여 로직과 상태를 모듈화하고 캡슐화하는 경량 상태 관리 패턴으로의 확장.
---
*Last updated: 2026-05-03*
---
*Last updated: 2026-05-03*
- Raw Source: 00_Raw/2026-05-03/State Management Libraries.md
---
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
**언제 이 지식을 쓰는가:**
- *(TODO)*
**언제 쓰면 안 되는가:**
- *(TODO)*
## 🧪 검증 상태 (Validation)
- **정보 상태:** needs_review
- **출처 신뢰도:** A
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
## 🧬 중복 검사 (Duplicate Check)
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
- **처리 방식:** UPDATE (자동 정규화)
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
## 🕓 변경 이력 (Changelog)
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|------|-----------|-----------|--------|
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
## 💻 코드 패턴 (Code Patterns)
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
```text
# TODO
// usage with selector to prevent re-render
const user = useStore((s) => s.user);
```
## 🤔 의사결정 기준 (Decision Criteria)
### Jotai atomic + derived
```typescript
import { atom, useAtom, useAtomValue } from "jotai";
import { atomWithStorage } from "jotai/utils";
**선택 A를 써야 할 때:**
- *(TODO)*
export const themeAtom = atomWithStorage<"light" | "dark">("theme", "light");
export const filterAtom = atom("");
export const itemsAtom = atom<{ id: string; name: string }[]>([]);
**선택 B를 써야 할 때:**
- *(TODO)*
export const filteredItemsAtom = atom((get) => {
const f = get(filterAtom).toLowerCase();
return get(itemsAtom).filter((i) => i.name.toLowerCase().includes(f));
});
**기본값:**
> *(TODO)*
function Search() {
const [filter, setFilter] = useAtom(filterAtom);
const filtered = useAtomValue(filteredItemsAtom);
return (
<>
<input value={filter} onChange={(e) => setFilter(e.target.value)} />
<ul>{filtered.map((i) => <li key={i.id}>{i.name}</li>)}</ul>
</>
);
}
```
## ❌ 안티패턴 (Anti-Patterns)
### Valtio proxy
```typescript
import { proxy, useSnapshot } from "valtio";
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
export const game = proxy({
player: { x: 0, y: 0, hp: 100 },
enemies: [] as { id: string; x: number; y: number }[],
damage(amount: number) {
game.player.hp = Math.max(0, game.player.hp - amount);
},
});
function HUD() {
const snap = useSnapshot(game);
return <div>HP: {snap.player.hp}</div>;
}
// mutate directly outside React
setInterval(() => {
game.player.x += 1; // 매 자동으로 컴포넌트 re-render
}, 16);
```
### Redux Toolkit (when 필요)
```typescript
import { createSlice, configureStore } from "@reduxjs/toolkit";
const counter = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
inc: (s) => { s.value += 1; },
incBy: (s, a: { payload: number }) => { s.value += a.payload; },
},
});
export const { inc, incBy } = counter.actions;
export const store = configureStore({ reducer: { counter: counter.reducer } });
```
### TanStack Query for server state
```typescript
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
export function usePosts() {
return useQuery({
queryKey: ["posts"],
queryFn: () => fetch("/api/posts").then((r) => r.json()),
staleTime: 60_000,
});
}
export function useCreatePost() {
const qc = useQueryClient();
return useMutation({
mutationFn: (body: { title: string }) =>
fetch("/api/posts", { method: "POST", body: JSON.stringify(body) }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["posts"] }),
});
}
```
### Zustand + RSC bridge (Next.js 15)
```typescript
// store-provider.tsx
"use client";
import { createContext, useContext, useRef } from "react";
import { useStore as useZ, type StoreApi } from "zustand";
import { createStore } from "zustand/vanilla";
type State = { count: number; inc: () => void };
const StoreCtx = createContext<StoreApi<State> | null>(null);
export function StoreProvider({ children, initialCount = 0 }: any) {
const ref = useRef<StoreApi<State>>();
if (!ref.current) {
ref.current = createStore<State>((set) => ({
count: initialCount,
inc: () => set((s) => ({ count: s.count + 1 })),
}));
}
return <StoreCtx.Provider value={ref.current}>{children}</StoreCtx.Provider>;
}
export function useAppStore<T>(sel: (s: State) => T): T {
const store = useContext(StoreCtx);
if (!store) throw new Error("StoreProvider missing");
return useZ(store, sel);
}
```
### Subscribing outside React (Zustand)
```typescript
const unsub = useStore.subscribe(
(s) => s.user,
(user) => console.log("user changed", user),
);
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Server data (lists, details) | TanStack Query / SWR |
| Mid-size client state, simple | **Zustand** |
| Fine-grained derived graphs | **Jotai** |
| Mutable mental model, games | Valtio |
| Legacy / strict Redux DevTools | Redux Toolkit |
| Shared single value (theme, locale) | Context (sufficient) |
**기본값**: server state → TanStack Query, client state → Zustand. Jotai 는 atom graph 가 도움될 때.
## 🔗 Graph
- 부모: [[React]] · [[Frontend Architecture]]
- 변형: [[Zustand]] · [[Jotai]] · [[Valtio]] · [[Redux Toolkit]] · [[MobX]] · [[Recoil]]
- 응용: [[Form State]] · [[Auth State]] · [[Cart State]]
- Adjacent: [[TanStack Query]] · [[SWR]] · [[React Server Components]] · [[Context API]]
## 🤖 LLM 활용
**언제**: client-only state design, store shape 결정, library 선택. 매 SPA 의 boundary.
**언제 X**: server data fetch (TanStack Query), 매 single-component local state (useState).
## ❌ 안티패턴
- **Redux for everything**: 매 small app 에 boilerplate. 매 Zustand 로 충분.
- **Server state in Zustand**: cache invalidation 직접 구현 → 매 TanStack Query 사용.
- **Atom 폭발**: Jotai 에서 모든 변수를 atom 으로 → 매 graph navigation 혼란.
- **No selector**: `useStore((s) => s)` 전체 구독 → 모든 변경에 re-render.
- **Mutating snapshot**: Valtio snap 을 직접 mutate → noop or warning.
- **Multiple stores for same domain**: 매 single source of truth 위배.
## 🧪 검증 / 중복
- Verified (Zustand/Jotai/Valtio/Redux Toolkit official docs, 2026).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — state management libraries canonical full content |