refactor(topics): 멀티 에이전트용 지식 재편 — _Common(공통 기본기) + Domain_* 구조
에이전트 8종(대화형/프로그래머 C·S/디자이너/설계자/기획자/QA/PD/PM)에게 [공통 기본 능력 + 롤별 Specialty] 2층으로 지식을 주입하기 위한 재분류. 문서 내용·포맷은 무수정, 폴더 이동만 (6,372개 문서 수 보존 확인). - Topic_Programming → Domain_Programming (내부 구조 보존) - Topic_Graphic → Domain_Design - Topic_Business → Domain_Product - Topic_General → Domain_General - _Common 신설: Math(구 Topic_Math_Specialty), Reasoning(구 General/From_Thinking & Reasoning), Reasoning_Creativity(구 General/From_창의성), Communication(Poetic_Blog_Writing + From_writing) - 타 도메인의 From_* 폴더는 유지 (출처 표기일 뿐, 이미 도메인에 맞게 분류된 문서) - 빈 폴더 정리 (memory/procedures) - 에이전트→폴더 매핑은 workspace의 .astra/agent-knowledge-map.json (9개 에이전트) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
---
|
||||
id: wiki-2026-0508-one-way-data-flow
|
||||
title: One-way Data Flow
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [One-way Data Flow, Unidirectional Data Flow, Top-down Props]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [react, frontend, architecture, data-flow, state-management]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: TypeScript
|
||||
framework: React
|
||||
---
|
||||
|
||||
# One-way Data Flow
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 state 매 down, event 매 up"**. Unidirectional data flow 매 React (Flux 영감) 의 core mental model — parent props 으로 child 에 데이터 전달, child callback 으로 parent 에 event 통보. 매 reasoning 의 단순화, debugging 의 traceability, time-travel 의 가능성. Vue/Solid/Svelte 도 매 동일 원칙.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 핵심 규칙
|
||||
- State 매 single owner (component or store).
|
||||
- Owner 만 mutate 가능.
|
||||
- Child 매 read-only props 만 받음.
|
||||
- Child 의 변경 요청 매 callback / event / dispatch.
|
||||
|
||||
### 매 왜 unidirectional
|
||||
- **Predictability**: state 변경 source 매 명확.
|
||||
- **Debugging**: render output 매 props 의 함수.
|
||||
- **Time-travel**: state snapshot 만 으로 UI 재현.
|
||||
- **Concurrency**: 매 React Concurrent 매 mutable 2-way 매 deadlock-prone.
|
||||
|
||||
### 매 응용
|
||||
1. React props/callback 패턴.
|
||||
2. Redux / Zustand / Jotai store dispatch.
|
||||
3. Vue `props down, emit up`.
|
||||
4. Form: lift state up.
|
||||
5. Event sourcing / CQRS frontend.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Lifting state up (React)
|
||||
```tsx
|
||||
function Parent() {
|
||||
const [name, setName] = useState("");
|
||||
return (
|
||||
<>
|
||||
<NameInput value={name} onChange={setName} />
|
||||
<Greeting name={name} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function NameInput({ value, onChange }: { value: string; onChange: (v: string) => void }) {
|
||||
return <input value={value} onChange={(e) => onChange(e.target.value)} />;
|
||||
}
|
||||
|
||||
function Greeting({ name }: { name: string }) {
|
||||
return <p>Hello, {name || "stranger"}!</p>;
|
||||
}
|
||||
```
|
||||
|
||||
### Reducer (action up, state down)
|
||||
```tsx
|
||||
type Action = { type: "add"; item: string } | { type: "remove"; idx: number };
|
||||
type State = { items: string[] };
|
||||
|
||||
function reducer(s: State, a: Action): State {
|
||||
switch (a.type) {
|
||||
case "add": return { items: [...s.items, a.item] };
|
||||
case "remove": return { items: s.items.filter((_, i) => i !== a.idx) };
|
||||
}
|
||||
}
|
||||
|
||||
function TodoApp() {
|
||||
const [state, dispatch] = useReducer(reducer, { items: [] });
|
||||
return (
|
||||
<>
|
||||
<AddBox onAdd={(item) => dispatch({ type: "add", item })} />
|
||||
<List items={state.items} onRemove={(idx) => dispatch({ type: "remove", idx })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Zustand (store as single source)
|
||||
```ts
|
||||
import { create } from "zustand";
|
||||
|
||||
interface CartStore {
|
||||
items: { id: string; qty: number }[];
|
||||
add: (id: string) => void;
|
||||
remove: (id: string) => void;
|
||||
}
|
||||
|
||||
export const useCart = create<CartStore>((set) => ({
|
||||
items: [],
|
||||
add: (id) => set((s) => ({ items: [...s.items, { id, qty: 1 }] })),
|
||||
remove: (id) => set((s) => ({ items: s.items.filter((i) => i.id !== id) })),
|
||||
}));
|
||||
|
||||
function ProductCard({ id }: { id: string }) {
|
||||
const add = useCart((s) => s.add);
|
||||
return <button onClick={() => add(id)}>Add</button>;
|
||||
}
|
||||
```
|
||||
|
||||
### Vue 3 props down + emit up
|
||||
```vue
|
||||
<!-- Parent.vue -->
|
||||
<template>
|
||||
<Counter :value="count" @increment="count++" />
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
const count = ref(0);
|
||||
</script>
|
||||
|
||||
<!-- Counter.vue -->
|
||||
<template>
|
||||
<button @click="$emit('increment')">{{ value }}</button>
|
||||
</template>
|
||||
<script setup>
|
||||
defineProps<{ value: number }>();
|
||||
defineEmits<{ increment: [] }>();
|
||||
</script>
|
||||
```
|
||||
|
||||
### Server-driven (React Query + URL state)
|
||||
```tsx
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useSearchParams } from "react-router";
|
||||
|
||||
function ProductList() {
|
||||
const [params, setParams] = useSearchParams();
|
||||
const category = params.get("category") ?? "all";
|
||||
const { data } = useQuery({
|
||||
queryKey: ["products", category],
|
||||
queryFn: () => fetch(`/api/products?cat=${category}`).then((r) => r.json()),
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<CategoryPicker value={category} onChange={(v) => setParams({ category: v })} />
|
||||
{data?.map((p) => <Card key={p.id} product={p} />)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Forbidden 2-way leak (anti-pattern shown)
|
||||
```tsx
|
||||
// ❌ child mutates parent's prop directly via mutable ref
|
||||
function BadChild({ obj }: { obj: { count: number } }) {
|
||||
return <button onClick={() => obj.count++}>+</button>; // parent never re-renders
|
||||
}
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 2-3 components share state | Lift state up |
|
||||
| Cross-tree state | Context / Zustand / Redux |
|
||||
| Server data | React Query / SWR (single source) |
|
||||
| URL state | useSearchParams / Next router |
|
||||
| Form-heavy local | useReducer + dispatch |
|
||||
| Vue | props + emit (or Pinia) |
|
||||
|
||||
**기본값**: state 매 highest common ancestor 또는 매 store. 매 props down + callback up.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[React]]
|
||||
- 변형: [[프론트엔드 및 UIUX 표준|Redux]] · [[Zustand]] · [[Pinia]]
|
||||
- 응용: [[useReducer]] · [[Event Sourcing]]
|
||||
- Adjacent: [[Reactive-Streams]] · [[Signals]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: React/Vue/Solid component design, form lifting, store architecture.
|
||||
**언제 X**: 매 highly local input (e.g. simple text field) — 매 controlled-input over-engineering 회피, uncontrolled OK.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Mutate props in child**: 매 silent re-render miss.
|
||||
- **Shared mutable ref**: 매 React diff 의 invariant 위반.
|
||||
- **Two-way binding in big tree**: 매 cycle / cascade.
|
||||
- **Prop drilling 10층**: 매 Context / store 으로 cut.
|
||||
- **Local form state in 매 sync 매 server**: 매 stale conflict — server 매 source of truth + optimistic update.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (React docs — Sharing State Between Components, Vue docs — Component Basics, Redux principles).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — lifting/reducer/Zustand/Vue emit/server-driven 패턴 |
|
||||
Reference in New Issue
Block a user