docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거

Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
This commit is contained in:
Antigravity Agent
2026-07-05 00:33:48 +09:00
parent 1cfd3bbb56
commit 9148c358d0
6455 changed files with 1 additions and 86875 deletions
@@ -0,0 +1,422 @@
---
id: frontend-state-management-modern
title: State Management — Zustand / Jotai / Valtio / Redux Toolkit
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [frontend, state, react, vibe-coding]
tech_stack: { language: "TS / React", applicable_to: ["Frontend"] }
applied_in: []
aliases: [Zustand, Jotai, Valtio, Redux Toolkit, RTK, Recoil, MobX, signals, state management]
---
# State Management Modern
> Redux 의 시대 끝. **Zustand (가장 인기), Jotai (atomic), Valtio (proxy), RTK (Redux modern)**. Server state = TanStack Query. Local state = useState.
## 📖 핵심 개념
- Server state ≠ client state.
- Server state = TanStack Query / SWR.
- Client state = Zustand / Jotai / Context.
- Form state = react-hook-form.
## 💻 코드 패턴
### Zustand (가장 인기)
```ts
import { create } from 'zustand';
interface Store {
count: number;
increment: () => void;
reset: () => void;
}
const useStore = create<Store>((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
reset: () => set({ count: 0 }),
}));
// 사용
function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>{count}</button>;
}
```
→ Simple, hooks-friendly.
### Selector (re-render 줄임)
```ts
const count = useStore((s) => s.count); // count 변경 시 만 re-render
// vs
const { count } = useStore(); // 매 변경 re-render
```
### Persist (localStorage)
```ts
import { persist } from 'zustand/middleware';
const useStore = create(
persist<Store>(
(set) => ({ count: 0, ... }),
{ name: 'counter-store' }
)
);
```
### Immer (mutable syntax)
```ts
import { immer } from 'zustand/middleware/immer';
const useStore = create(
immer<Store>((set) => ({
items: [],
addItem: (item) => set((s) => { s.items.push(item); }),
}))
);
```
→ Mutable syntax, Immer 가 immutable 변경.
### DevTools
```ts
import { devtools } from 'zustand/middleware';
const useStore = create(devtools<Store>(set => ({ ... })));
```
→ Redux DevTools.
### Subscribe (no re-render)
```ts
useStore.subscribe((state) => {
console.log('changed:', state);
});
```
### Jotai (atomic)
```ts
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
```
→ Atom = unit of state. Granular re-render.
### Derived atom
```ts
const doubleAtom = atom((get) => get(countAtom) * 2);
function Display() {
const [double] = useAtom(doubleAtom);
// count 변경 시 만 update
}
```
### Async atom
```ts
const userAtom = atom(async (get) => {
const id = get(userIdAtom);
return fetchUser(id);
});
function User() {
const [user] = useAtom(userAtom); // Suspense 친화
return <Suspense fallback={<Spinner />}>{user.name}</Suspense>;
}
```
### Atom families
```ts
import { atomFamily } from 'jotai/utils';
const userAtomFamily = atomFamily((id: string) => atom(async () => fetchUser(id)));
const [user] = useAtom(userAtomFamily('123'));
```
→ Per-id atom.
### Valtio (proxy)
```ts
import { proxy, useSnapshot } from 'valtio';
const state = proxy({ count: 0 });
function Counter() {
const snap = useSnapshot(state);
return <button onClick={() => state.count++}>{snap.count}</button>;
}
```
→ Mutable syntax. Proxy 가 변경 감지.
### Redux Toolkit (modern Redux)
```ts
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value++; }, // Immer 자동
incrementBy: (state, action) => { state.value += action.payload; },
},
});
export const { increment, incrementBy } = counterSlice.actions;
const store = configureStore({
reducer: { counter: counterSlice.reducer },
});
// 사용
import { useSelector, useDispatch } from 'react-redux';
const value = useSelector((s: RootState) => s.counter.value);
const dispatch = useDispatch();
dispatch(increment());
```
→ Boilerplate ↓. 큰 enterprise 친화.
### RTK Query (built-in fetch)
```ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getUser: builder.query<User, string>({
query: (id) => `users/${id}`,
}),
}),
});
export const { useGetUserQuery } = api;
// Component
const { data, isLoading } = useGetUserQuery('123');
```
→ Redux 의 built-in TanStack Query 식.
### TanStack Query (server state default)
```ts
const { data, isLoading } = useQuery({
queryKey: ['user', id],
queryFn: () => fetchUser(id),
});
```
→ Cache + retry + refetch + stale 자동.
→ [[React_TanStack_Query_Advanced]].
### Context (React built-in)
```tsx
const ThemeContext = createContext<'light' | 'dark'>('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<Component />
</ThemeContext.Provider>
);
}
function Component() {
const theme = useContext(ThemeContext);
return <div className={theme}>...</div>;
}
```
→ 작은 / static state 만. 자주 변경 = re-render 폭발.
### Signals (React 18+ proposal)
```ts
// @preact/signals-react
import { signal, computed } from '@preact/signals-react';
const count = signal(0);
const double = computed(() => count.value * 2);
function Counter() {
return <button onClick={() => count.value++}>{count}</button>;
}
```
→ Signal-based reactivity (Solid / Vue 식).
### MobX
```ts
import { observable, action } from 'mobx';
import { observer } from 'mobx-react-lite';
class Store {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() { this.count++; }
}
const store = new Store();
const Counter = observer(() => {
return <button onClick={() => store.increment()}>{store.count}</button>;
});
```
→ Observable + reaction. OOP 친화.
### Recoil (legacy, 거의 dead)
```
Facebook 의 atomic state.
2024 가 Jotai 가 추월.
```
### Effector (강력)
```ts
import { createStore, createEvent } from 'effector';
const increment = createEvent();
const $count = createStore(0).on(increment, (state) => state + 1);
function Counter() {
const count = useUnit($count);
return <button onClick={() => increment()}>{count}</button>;
}
```
→ Powerful, 작은 ecosystem.
### Library 비교
```
Zustand: 가장 인기, simple.
Jotai: atomic, granular.
Valtio: proxy, mutable.
RTK: Redux modern.
MobX: OOP.
Effector: powerful, niche.
Signals: reactivity primitive.
Context: React built-in (작은).
```
### 의사결정
```
Server state: TanStack Query / SWR / RTK Query.
Form state: react-hook-form / TanStack Form.
URL state: nuqs / TanStack Router search.
Theme / auth: Context (작은).
큰 client state: Zustand / Jotai.
Enterprise / 큰 팀: RTK.
```
### URL state (nuqs)
```ts
import { useQueryState } from 'nuqs';
function Filter() {
const [page, setPage] = useQueryState('page', { defaultValue: '1' });
return <input value={page} onChange={(e) => setPage(e.target.value)} />;
}
```
→ URL 가 source of truth. Bookmarkable / sharable.
### Server-side state (Next.js)
```tsx
// Server Component
export default async function Page() {
const data = await fetchData();
return <Component data={data} />;
}
```
→ Server state 가 client X.
### Combine
```ts
// Zustand for UI state
const useUIStore = create(...);
// TanStack Query for server state
const { data } = useQuery(...);
// Form library
const { register } = useForm();
```
→ 매 state 의 적합 도구.
### Performance
```
Re-render 줄이기:
- Selector (Zustand / Redux).
- Atom split (Jotai).
- Memoize component.
- React Compiler (auto).
```
### 함정
```
- 모든 거 Redux: boilerplate 폭발.
- Server state in client store: stale 자주.
- Context 큰 store: re-render 폭발.
- Zustand 의 entire state subscribe: re-render.
- Jotai atom 가 너무 많음: complexity.
```
### Migration
```
Redux → Zustand: 점진. 1 slice 씩.
Recoil → Jotai: API 비슷.
MobX → Zustand: rewrite.
```
## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| 일반 React | Zustand + TanStack Query |
| Atomic / granular | Jotai |
| Mutable syntax | Valtio |
| 큰 enterprise | RTK |
| OOP | MobX |
| Server state | TanStack Query / SWR |
| URL | nuqs |
| Form | RHF / TanStack Form |
| Theme / static | Context |
## ❌ 안티패턴
- **모든 거 Redux/Zustand**: server state 가 stale.
- **Context 큰 + 자주 변경**: re-render 폭발.
- **Selector 없이 useStore()**: 매 변경 re-render.
- **Server state in store**: cache 깨짐.
- **Form in store**: re-render 폭발.
- **Redux + Zustand 둘 다**: 분리 안 됨.
## 🤖 LLM 활용 힌트
- Zustand 가 default modern.
- TanStack Query 가 server state.
- Jotai 가 granular.
- 매 state 의 적합 도구 (4-5 종 mix).
## 🔗 관련 문서
- [[React_State_Library_Comparison]]
- [[React_TanStack_Query_Advanced]]
- [[Frontend_Form_State_Deep]]