@wooojin/forgen 0.4.8 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/assets/dev-guide/be/README.md +226 -0
  3. package/assets/dev-guide/be/adapters/build-agents-md.sh +63 -0
  4. package/assets/dev-guide/be/principles/common.md +433 -0
  5. package/assets/dev-guide/be/principles/go.md +469 -0
  6. package/assets/dev-guide/be/principles/node.md +388 -0
  7. package/assets/dev-guide/be/skills/go/be-build/SKILL.md +262 -0
  8. package/assets/dev-guide/be/skills/go/be-perf/SKILL.md +308 -0
  9. package/assets/dev-guide/be/skills/go/be-review/SKILL.md +119 -0
  10. package/assets/dev-guide/be/skills/go/be-security/SKILL.md +362 -0
  11. package/assets/dev-guide/be/skills/node/be-build/SKILL.md +239 -0
  12. package/assets/dev-guide/be/skills/node/be-perf/SKILL.md +272 -0
  13. package/assets/dev-guide/be/skills/node/be-review/SKILL.md +118 -0
  14. package/assets/dev-guide/be/skills/node/be-security/SKILL.md +355 -0
  15. package/assets/dev-guide/be/sources/12factor/INDEX.md +53 -0
  16. package/assets/dev-guide/be/sources/api-design/INDEX.md +56 -0
  17. package/assets/dev-guide/be/sources/ddia/INDEX.md +55 -0
  18. package/assets/dev-guide/be/sources/go-runtime/INDEX.md +62 -0
  19. package/assets/dev-guide/be/sources/node-runtime/INDEX.md +60 -0
  20. package/assets/dev-guide/be/sources/otel/INDEX.md +53 -0
  21. package/assets/dev-guide/be/sources/owasp-api/INDEX.md +52 -0
  22. package/assets/dev-guide/be/sources/postgres/INDEX.md +55 -0
  23. package/assets/dev-guide/be/sources/sre-book/INDEX.md +48 -0
  24. package/assets/dev-guide/fe/README.md +197 -0
  25. package/assets/dev-guide/fe/adapters/build-agents-md.sh +63 -0
  26. package/assets/dev-guide/fe/adapters/refresh.sh +68 -0
  27. package/assets/dev-guide/fe/principles/common.md +160 -0
  28. package/assets/dev-guide/fe/principles/react.md +183 -0
  29. package/assets/dev-guide/fe/principles/vue.md +196 -0
  30. package/assets/dev-guide/fe/skills/react/fe-build/SKILL.md +139 -0
  31. package/assets/dev-guide/fe/skills/react/fe-perf/SKILL.md +179 -0
  32. package/assets/dev-guide/fe/skills/react/fe-review/SKILL.md +141 -0
  33. package/assets/dev-guide/fe/skills/vue/fe-build/SKILL.md +148 -0
  34. package/assets/dev-guide/fe/skills/vue/fe-perf/SKILL.md +163 -0
  35. package/assets/dev-guide/fe/skills/vue/fe-review/SKILL.md +136 -0
  36. package/assets/dev-guide/fe/sources/a11y-dx/INDEX.md +41 -0
  37. package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-memory.md +150 -0
  38. package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-performance.md +99 -0
  39. package/assets/dev-guide/fe/sources/a11y-dx/lighthouse-audits.md +146 -0
  40. package/assets/dev-guide/fe/sources/a11y-dx/react-devtools-profiler.md +128 -0
  41. package/assets/dev-guide/fe/sources/a11y-dx/wcag22-new-criteria.md +174 -0
  42. package/assets/dev-guide/fe/sources/perf/01-core-web-vitals.md +58 -0
  43. package/assets/dev-guide/fe/sources/perf/02-inp.md +83 -0
  44. package/assets/dev-guide/fe/sources/perf/03-lcp-cls.md +130 -0
  45. package/assets/dev-guide/fe/sources/perf/04-speculation-rules.md +148 -0
  46. package/assets/dev-guide/fe/sources/perf/05-view-transitions.md +153 -0
  47. package/assets/dev-guide/fe/sources/perf/06-nextjs-caching.md +188 -0
  48. package/assets/dev-guide/fe/sources/perf/07-server-components.md +181 -0
  49. package/assets/dev-guide/fe/sources/perf/08-ppr.md +133 -0
  50. package/assets/dev-guide/fe/sources/perf/09-nextjs-image.md +200 -0
  51. package/assets/dev-guide/fe/sources/perf/10-optimize-lcp.md +201 -0
  52. package/assets/dev-guide/fe/sources/perf/INDEX.md +88 -0
  53. package/assets/dev-guide/fe/sources/react/INDEX.md +41 -0
  54. package/assets/dev-guide/fe/sources/react/keeping-components-pure.md +135 -0
  55. package/assets/dev-guide/fe/sources/react/no-effect-patterns.md +183 -0
  56. package/assets/dev-guide/fe/sources/react/react-compiler.md +182 -0
  57. package/assets/dev-guide/fe/sources/react/server-components.md +194 -0
  58. package/assets/dev-guide/fe/sources/react/server-functions.md +192 -0
  59. package/assets/dev-guide/fe/sources/react/suspense.md +218 -0
  60. package/assets/dev-guide/fe/sources/react/use-action-state.md +123 -0
  61. package/assets/dev-guide/fe/sources/react/use-form-status.md +158 -0
  62. package/assets/dev-guide/fe/sources/react/use-hook.md +153 -0
  63. package/assets/dev-guide/fe/sources/react/use-optimistic.md +194 -0
  64. package/assets/dev-guide/fe/sources/toss-ff/INDEX.md +58 -0
  65. package/assets/dev-guide/fe/sources/toss-ff/cohesion-code-directory.md +79 -0
  66. package/assets/dev-guide/fe/sources/toss-ff/cohesion-form-fields.md +110 -0
  67. package/assets/dev-guide/fe/sources/toss-ff/cohesion-magic-number.md +47 -0
  68. package/assets/dev-guide/fe/sources/toss-ff/coupling-item-edit-modal.md +124 -0
  69. package/assets/dev-guide/fe/sources/toss-ff/coupling-use-bottom-sheet.md +57 -0
  70. package/assets/dev-guide/fe/sources/toss-ff/coupling-use-page-state.md +71 -0
  71. package/assets/dev-guide/fe/sources/toss-ff/overview-4-principles.md +77 -0
  72. package/assets/dev-guide/fe/sources/toss-ff/predictability-hidden-logic.md +59 -0
  73. package/assets/dev-guide/fe/sources/toss-ff/predictability-http.md +77 -0
  74. package/assets/dev-guide/fe/sources/toss-ff/predictability-use-user.md +110 -0
  75. package/assets/dev-guide/fe/sources/toss-ff/readability-comparison-order.md +52 -0
  76. package/assets/dev-guide/fe/sources/toss-ff/readability-condition-name.md +64 -0
  77. package/assets/dev-guide/fe/sources/toss-ff/readability-login-start-page.md +183 -0
  78. package/assets/dev-guide/fe/sources/toss-ff/readability-magic-number.md +53 -0
  79. package/assets/dev-guide/fe/sources/toss-ff/readability-submit-button.md +73 -0
  80. package/assets/dev-guide/fe/sources/toss-ff/readability-ternary-operator.md +38 -0
  81. package/assets/dev-guide/fe/sources/toss-ff/readability-use-page-state.md +77 -0
  82. package/assets/dev-guide/fe/sources/toss-ff/readability-user-policy.md +98 -0
  83. package/assets/dev-guide/fe/sources/vue/INDEX.md +17 -0
  84. package/assets/dev-guide/fe/sources/vue/composition-api.md +251 -0
  85. package/assets/dev-guide/fe/sources/vue/nuxt-data-fetching.md +232 -0
  86. package/assets/dev-guide/fe/sources/vue/pinia-state-management.md +134 -0
  87. package/assets/dev-guide/fe/sources/vue/reactivity-pitfalls.md +261 -0
  88. package/assets/dev-guide/fe/sources/vue/style-guide-priority-a.md +117 -0
  89. package/assets/dev-guide/fe/sources/vue/style-guide-priority-b.md +231 -0
  90. package/assets/dev-guide/fe/sources/vue/style-guide-priority-c.md +86 -0
  91. package/assets/dev-guide/fe/sources/vue/style-guide-priority-d.md +72 -0
  92. package/dist/cli.js +42 -0
  93. package/dist/core/dashboard-cli.d.ts +12 -0
  94. package/dist/core/dashboard-cli.js +226 -0
  95. package/dist/core/dev-guide-injector.d.ts +26 -0
  96. package/dist/core/dev-guide-injector.js +137 -0
  97. package/dist/core/init.js +53 -0
  98. package/dist/core/lifecycle-classifier.d.ts +23 -0
  99. package/dist/core/lifecycle-classifier.js +104 -0
  100. package/dist/core/observability-backfill.d.ts +31 -0
  101. package/dist/core/observability-backfill.js +178 -0
  102. package/dist/core/observability-store.d.ts +58 -0
  103. package/dist/core/observability-store.js +195 -0
  104. package/dist/core/session-store.js +4 -0
  105. package/dist/core/spawn.d.ts +17 -0
  106. package/dist/core/spawn.js +179 -2
  107. package/dist/core/statusline-cli.js +34 -1
  108. package/dist/engine/compound-extractor.js +39 -0
  109. package/dist/engine/compound-loop.js +6 -0
  110. package/dist/engine/compound-retire.d.ts +20 -0
  111. package/dist/engine/compound-retire.js +85 -0
  112. package/dist/hooks/context-guard.js +25 -1
  113. package/dist/hooks/post-tool-use.js +48 -0
  114. package/dist/hooks/solution-injector.js +93 -0
  115. package/dist/host/install-claude.d.ts +6 -2
  116. package/dist/host/install-claude.js +74 -2
  117. package/dist/host/install-codex.d.ts +4 -0
  118. package/dist/host/install-codex.js +71 -0
  119. package/dist/host/install-orchestrator.js +1 -0
  120. package/package.json +6 -6
  121. package/plugin.json +1 -1
  122. package/scripts/postinstall.js +134 -0
@@ -0,0 +1,183 @@
1
+ ---
2
+ title: React 19 + Next.js 16 원칙
3
+ version: 2026-05-18
4
+ sources:
5
+ - sources/react/
6
+ - sources/perf/06-nextjs-caching.md
7
+ - sources/perf/07-server-components.md
8
+ - sources/perf/08-ppr.md
9
+ ---
10
+
11
+ # React 19 + Next.js 16 원칙
12
+
13
+ > [공통 원칙](./common.md)을 먼저 따르고, 아래는 React/Next 특화.
14
+
15
+ ## R0. 의사결정 우선순위
16
+
17
+ 1. **Server Component 기본, Client는 필요할 때만** (Next.js App Router)
18
+ 2. **React Compiler가 자동 메모이제이션** — `useMemo`/`useCallback` 수동 작성 지양
19
+ 3. **Effect 회피** — `useEffect`는 *외부 시스템 동기화* 용도. 데이터 변환/이벤트 처리에 쓰지 않는다.
20
+ 4. **Action + form 통합** — `<form action={...}>` 와 `useActionState` 우선, 수동 상태 관리 지양
21
+
22
+ ---
23
+
24
+ ## R1. Server vs Client Components
25
+
26
+ 근거: `sources/perf/07-server-components.md`, `sources/react/server-components.md`
27
+
28
+ ### Server Component 기본값
29
+
30
+ - 모든 컴포넌트는 RSC가 기본. `'use client'` 안 붙이면 서버.
31
+ - 서버에서만: `async`/`await` 직접 사용, DB 접근, 환경변수, 대용량 라이브러리(markdown/syntax-highlight 등)
32
+
33
+ ### Client Component 가 필요한 신호
34
+
35
+ - `useState`/`useReducer`/`useEffect`
36
+ - 브라우저 API (`window`, `localStorage`, `IntersectionObserver`)
37
+ - 이벤트 핸들러 (`onClick`, `onChange`)
38
+ - 커스텀 훅 사용
39
+
40
+ ### 패턴
41
+
42
+ - **Client 컴포넌트 안에 Server 컴포넌트를 자식으로 주입**한다 (`children` prop). RSC → Client 역방향 import 금지.
43
+ - **환경 오염 방지**: 서버 전용 코드는 `'server-only'` 패키지로 클라이언트 import 차단.
44
+
45
+ ---
46
+
47
+ ## R2. Effect 회피 (you-might-not-need-an-effect)
48
+
49
+ 근거: `sources/react/no-effect-patterns.md`
50
+
51
+ | 잘못된 Effect | 대체 |
52
+ |---------------|------|
53
+ | props/state 기반 파생 값 계산 | 렌더 중 계산. 비싸면 `useMemo` (Compiler 시 자동) |
54
+ | 이벤트 핸들러 로직을 Effect로 옮김 | 이벤트 핸들러 안에서 처리 |
55
+ | state 변경 시 다른 state 갱신 (체인) | 한 핸들러 안에서 둘 다 갱신 |
56
+ | 상위 변경 알림 (`useEffect(() => onChange(value), [value])`) | 변경 발생 핸들러 안에서 `onChange` 호출 |
57
+ | props 변경 시 state 초기화 | `key` prop 으로 컴포넌트 리마운트 |
58
+ | 데이터 페칭 | React Query/SWR 또는 Suspense + `use()` |
59
+
60
+ **남는 Effect 용도**: 외부 시스템 구독 (WebSocket, IntersectionObserver, 브라우저 API).
61
+
62
+ ---
63
+
64
+ ## R3. React 19 신 API
65
+
66
+ ### R3.1 `use()` — Promise/Context 읽기
67
+
68
+ 근거: `sources/react/use-hook.md`
69
+
70
+ - 조건부 호출 가능 (Hook 규칙 예외)
71
+ - Suspense 경계와 함께 사용
72
+ - Server Component 의 `async`/`await` 가 안되는 Client 컴포넌트에서 데이터 읽기 용도
73
+
74
+ ### R3.2 `<form action>` + `useActionState` + `useFormStatus`
75
+
76
+ 근거: `sources/react/use-action-state.md`, `sources/react/use-form-status.md`
77
+
78
+ ```tsx
79
+ 'use client';
80
+ import { useActionState } from 'react';
81
+ import { useFormStatus } from 'react-dom';
82
+
83
+ function SubmitButton() {
84
+ const { pending } = useFormStatus();
85
+ return <button disabled={pending}>{pending ? '제출 중...' : '제출'}</button>;
86
+ }
87
+
88
+ export function Form({ action }: { action: (state, fd) => Promise<State> }) {
89
+ const [state, formAction] = useActionState(action, { error: null });
90
+ return (
91
+ <form action={formAction}>
92
+ <input name="email" />
93
+ {state.error && <p role="alert">{state.error}</p>}
94
+ <SubmitButton />
95
+ </form>
96
+ );
97
+ }
98
+ ```
99
+
100
+ - 폼 라이브러리 도입 전 우선 검토. 단순 폼은 의존성 0로 가능.
101
+
102
+ ### R3.3 `useOptimistic` — 낙관적 UI
103
+
104
+ 근거: `sources/react/use-optimistic.md`
105
+
106
+ - 서버 응답 전 즉시 UI 반영, 실패 시 자동 롤백
107
+ - 메시지 전송, 좋아요 토글 같은 인터랙션 INP 개선에 결정적
108
+
109
+ ---
110
+
111
+ ## R4. React Compiler
112
+
113
+ 근거: `sources/react/react-compiler.md`
114
+
115
+ - **`useMemo`/`useCallback`/`memo` 를 수동으로 쓰지 마라**. Compiler가 자동 메모이즈한다.
116
+ - 도입 시 ESLint plugin (`eslint-plugin-react-compiler`) 으로 Rules of React 위반 검출.
117
+ - Compiler가 메모이즈 못 하는 코드 → "왜 메모 못 했는지" 진단하고 수정. 수동 useMemo 회피.
118
+ - **예외**: Compiler 미적용 프로젝트는 기존대로 명시적 메모이제이션 유지.
119
+
120
+ ---
121
+
122
+ ## R5. Suspense + Streaming
123
+
124
+ 근거: `sources/react/suspense.md`
125
+
126
+ - Suspense 경계를 **데이터 단위가 아니라 사용자 인식 단위**로 그어라. "이 부분 따로 보여도 되나?" 기준.
127
+ - 중첩 Suspense → 순차 스트리밍. 상위가 늦으면 하위 모두 대기.
128
+ - `startTransition` 으로 후속 인터랙션에서 fallback 깜빡임 방지.
129
+
130
+ ---
131
+
132
+ ## R6. Next.js 16 캐싱 4계층
133
+
134
+ 근거: `sources/perf/06-nextjs-caching.md`
135
+
136
+ | 계층 | 위치 | 무효화 |
137
+ |------|------|--------|
138
+ | **Request Memoization** | 단일 렌더 중 fetch 중복 제거 | 자동 (렌더 종료) |
139
+ | **Data Cache** | 서버, 영구 | `revalidateTag` / `revalidatePath` / time |
140
+ | **Full Route Cache** | 빌드 또는 ISR | `revalidatePath` |
141
+ | **Router Cache** | 클라이언트, 세션 | 네비게이션 / `router.refresh()` |
142
+
143
+ - `fetch(url, { next: { tags: ['posts'], revalidate: 60 } })` 표준
144
+ - `unstable_cache` / `use cache` (Next 16) — 외부 데이터 함수 캐싱
145
+ - **변경 액션 후 반드시 `revalidateTag('posts')` 호출** (캐시 일관성)
146
+
147
+ ---
148
+
149
+ ## R7. PPR (Partial Prerendering)
150
+
151
+ 근거: `sources/perf/08-ppr.md`
152
+
153
+ - 정적 shell + Suspense 경계로 동적 부분 스트리밍
154
+ - Next.js 16의 `cacheComponents` 플래그 (PPR + use cache + dynamicIO 통합)
155
+ - 정적 캐시 가능한 부분이 큰 페이지 (상품 상세, 게시글)에서 TTFB·LCP 결정적 개선
156
+
157
+ ---
158
+
159
+ ## R8. 이미지 (next/image)
160
+
161
+ 근거: `sources/perf/09-nextjs-image.md`
162
+
163
+ - LCP 후보 이미지에 `priority` (`fetchpriority="high"` + preload)
164
+ - `fill` + 컨테이너 `position: relative` 또는 `width`/`height` 명시 (CLS 방지)
165
+ - `sizes` 로 반응형 이미지 정확히 명세 — 미명세 시 1배 이미지가 모바일에 다운로드됨
166
+ - `placeholder="blur"` 는 LCP 미세 개선 + UX 향상
167
+
168
+ ---
169
+
170
+ ## R9. React 안티패턴 카탈로그
171
+
172
+ | 안티패턴 | 픽스 |
173
+ |----------|------|
174
+ | `useEffect` 안에서 `setState` 후 데이터 변환 | 렌더 중 파생 값 계산 |
175
+ | `useState` + `useEffect` 로 fetch | Suspense + `use()` 또는 React Query |
176
+ | Compiler 환경에서 수동 `useMemo` 추가 | 제거. Compiler 못 메모하면 코드 수정 |
177
+ | Client 컴포넌트가 Server 컴포넌트 import | `children` prop 으로 주입 |
178
+ | Server Component 안에 `useState` | `'use client'` 추가 또는 분리 |
179
+ | `'use client'` 파일에 대형 의존성 import | Server 분리 또는 dynamic import |
180
+ | Form 상태를 `useState` 로 수동 관리 | `<form action>` + `useActionState` |
181
+ | `Image` 없이 `<img>` | `next/image` (또는 명시적 `width/height`) |
182
+ | `next/dynamic` 남발 | RSC가 더 적합한지 먼저 검토 |
183
+ | Effect 안에서 `router.push` | 이벤트 핸들러로 이동 |
@@ -0,0 +1,196 @@
1
+ ---
2
+ title: Vue 3 + Nuxt 3 원칙
3
+ version: 2026-05-18
4
+ sources:
5
+ - sources/vue/
6
+ ---
7
+
8
+ # Vue 3 + Nuxt 3 원칙
9
+
10
+ > [공통 원칙](./common.md)을 먼저 따르고, 아래는 Vue/Nuxt 특화.
11
+
12
+ ## V0. 의사결정 우선순위
13
+
14
+ 1. **Composition API + `<script setup>` 우선** (Options API 신규 코드에서 지양)
15
+ 2. **반응성 손실 절대 회피** — props 비구조화, reactive 객체 비구조화는 금지
16
+ 3. **상태는 컴포넌트 → composable → Pinia** 순으로 승격 (필요할 때만)
17
+ 4. **Nuxt: SSR 경계 의식** — `useFetch`/`useAsyncData` 로 hydration 일관성 보장
18
+
19
+ ---
20
+
21
+ ## V1. 스타일 가이드 4단계
22
+
23
+ 근거: `sources/vue/style-guide-priority-a.md` ~ `priority-d.md`
24
+
25
+ ### V1.1 Priority A (Essential — 위반 시 [HIGH])
26
+
27
+ - **다중 단어 컴포넌트 이름**. `Todo` 금지 → `TodoItem` (HTML 표준 충돌 회피)
28
+ - **`v-for` 에 `key` 필수**. `<li v-for="item in list" :key="item.id">`
29
+ - **`v-if` + `v-for` 같이 쓰지 마라**. 컴퓨티드로 필터링 후 `v-for`
30
+ - **컴포넌트 데이터는 함수 반환** (Options API의 경우)
31
+ - **prop 정의는 타입까지 명세** (`defineProps<{...}>()` 또는 객체 형식)
32
+ - **컴포넌트 스타일은 scoped**. 전역 클래스명 충돌 회피
33
+
34
+ ### V1.2 Priority B (Strongly Recommended)
35
+
36
+ - **단일 파일 컴포넌트 (SFC)** 우선
37
+ - **컴포넌트 파일명: PascalCase** (`TodoItem.vue`)
38
+ - **베이스 컴포넌트는 명확한 prefix** (`BaseButton`, `AppLogo`)
39
+ - **싱글톤 컴포넌트는 `The` prefix** (`TheHeader`, `TheSidebar`)
40
+ - **자식 컴포넌트는 부모 이름으로 시작** (`TodoListItem` — 같은 디렉토리 내 응집)
41
+ - **이벤트 이름은 kebab-case** (`@form-submit`)
42
+
43
+ ### V1.3 Priority C (Recommended)
44
+
45
+ - **컴포넌트/인스턴스 옵션 순서** 일관성
46
+ - **속성 순서**: `is` → `v-for` → `v-if` → `v-bind` → `v-on` → `key`/`ref` 등
47
+ - **단순 컴퓨티드 분해**. 한 컴퓨티드에 모든 로직 X → 작은 컴퓨티드 조합
48
+
49
+ ### V1.4 Priority D (Use with Caution)
50
+
51
+ - **`scoped` 안에서 element 셀렉터 지양** — 클래스 셀렉터 사용
52
+ - **암묵적 부모-자식 통신 지양** — props/emits 명시
53
+ - **비-Flux 글로벌 상태 관리 지양** — Pinia 사용
54
+
55
+ ---
56
+
57
+ ## V2. Composition API + `<script setup>`
58
+
59
+ 근거: `sources/vue/composition-api.md`
60
+
61
+ ### V2.1 기본 골격
62
+
63
+ ```vue
64
+ <script setup lang="ts">
65
+ import { ref, computed, watch } from 'vue';
66
+
67
+ const props = defineProps<{ id: string }>();
68
+ const emit = defineEmits<{ change: [value: string] }>();
69
+
70
+ const count = ref(0);
71
+ const double = computed(() => count.value * 2);
72
+
73
+ watch(() => props.id, (newId) => {
74
+ // 외부 시스템 동기화
75
+ });
76
+ </script>
77
+ ```
78
+
79
+ ### V2.2 핵심 규칙
80
+
81
+ - **`<script setup>` 이 신규 코드 기본**. Options API는 레거시/마이그레이션 한정.
82
+ - **`defineProps`/`defineEmits` 는 매크로** — import 불필요, 컴파일 시점 처리
83
+ - **`defineModel()`** (Vue 3.4+) 로 양방향 바인딩 단순화
84
+ - **로직 재사용은 composable** (`useXxx`) — Vue 의 커스텀 훅 패턴
85
+
86
+ ### V2.3 ref vs reactive
87
+
88
+ - **기본은 `ref`**. 원시값 + 객체 모두 처리, `.value` 일관성
89
+ - **`reactive` 는 객체 한정** + 비구조화 시 반응성 손실 위험
90
+ - **composable 반환은 `ref`/`computed`** 로 통일 (외부에서 비구조화 안전)
91
+
92
+ ---
93
+
94
+ ## V3. 반응성 함정 (반드시 회피)
95
+
96
+ 근거: `sources/vue/reactivity-pitfalls.md`
97
+
98
+ ### V3.1 props 비구조화 (Vue 3.5+ 는 자동 처리)
99
+
100
+ ```ts
101
+ // ❌ 반응성 손실 (3.4 이하)
102
+ const { id } = defineProps<{ id: string }>();
103
+
104
+ // ✅ 3.4 이하
105
+ const props = defineProps<{ id: string }>();
106
+ const id = computed(() => props.id);
107
+
108
+ // ✅ 3.5+ 자동 변환 (Reactive Props Destructure)
109
+ const { id } = defineProps<{ id: string }>();
110
+ // 단 watch source 에서는 getter로: watch(() => id, ...)
111
+ ```
112
+
113
+ ### V3.2 reactive 비구조화 금지
114
+
115
+ ```ts
116
+ const state = reactive({ count: 0 });
117
+
118
+ // ❌ 반응성 끊김
119
+ const { count } = state;
120
+
121
+ // ✅
122
+ const { count } = toRefs(state); // 각 필드 ref 화
123
+ ```
124
+
125
+ ### V3.3 ref unwrapping 함정
126
+
127
+ - **template 안: 자동 unwrap** (`.value` 불필요)
128
+ - **template 안 배열/Map 인덱싱: unwrap 안 됨** → `computed` 로 풀어서 노출
129
+ - **JS 코드: 명시적 `.value`** 필수
130
+
131
+ ---
132
+
133
+ ## V4. 상태 관리 (Pinia)
134
+
135
+ 근거: `sources/vue/pinia-state-management.md`
136
+
137
+ ### V4.1 언제 store로 승격?
138
+
139
+ - 컴포넌트 → composable: 같은 로직 2+ 컴포넌트에서 사용
140
+ - composable → Pinia store: 인스턴스 공유 필요 (싱글톤) 또는 DevTools/SSR/플러그인 필요
141
+
142
+ ### V4.2 Setup Store 권장 (Composition API 일관성)
143
+
144
+ ```ts
145
+ export const useCounterStore = defineStore('counter', () => {
146
+ const count = ref(0);
147
+ const double = computed(() => count.value * 2);
148
+ function increment() { count.value++; }
149
+ return { count, double, increment };
150
+ });
151
+ ```
152
+
153
+ - `state`/`getters`/`actions` Options Store보다 setup store가 SSR/TS 친화적
154
+
155
+ ---
156
+
157
+ ## V5. Nuxt 3 데이터 페칭
158
+
159
+ 근거: `sources/vue/nuxt-data-fetching.md`
160
+
161
+ ### V5.1 페칭 함수 선택 매트릭스
162
+
163
+ | 함수 | SSR | 클라이언트 hydration | 사용 시점 |
164
+ |------|-----|---------------------|-----------|
165
+ | `useFetch(url)` | ✅ | 자동 dedup | 페이지/컴포넌트 데이터 |
166
+ | `useAsyncData(key, fn)` | ✅ | 자동 dedup | 임의 async 로직 |
167
+ | `useLazyFetch` | ❌ | 마운트 후 | 비차단 데이터 |
168
+ | `$fetch` | ✅(서버) | 수동 | 이벤트 핸들러 안 (form submit 등) |
169
+
170
+ ### V5.2 SSR hydration 일관성
171
+
172
+ - 페이지 로직: `useFetch`/`useAsyncData` 만 사용. `onMounted` + 페치 금지 (SSR/CSR 불일치 → CLS).
173
+ - 이벤트 트리거 페치: `$fetch` (mutation 응답 받기)
174
+ - **key 명시적으로** — `useAsyncData('order-' + id, ...)` 같이 unique key 필수
175
+
176
+ ### V5.3 server/ 디렉토리 (Nitro)
177
+
178
+ - BFF 패턴: `server/api/orders.get.ts` 같은 server route
179
+ - 클라이언트는 `useFetch('/api/orders')` 로 호출 → 자동 타입 추론
180
+
181
+ ---
182
+
183
+ ## V6. Vue 안티패턴 카탈로그
184
+
185
+ | 안티패턴 | 픽스 |
186
+ |----------|------|
187
+ | `v-if` + `v-for` 같이 사용 | computed로 필터링 |
188
+ | `v-for` 에 `key` 누락 또는 index key | 안정적 id |
189
+ | `reactive` 비구조화 | `toRefs` 또는 `ref` 사용 |
190
+ | props 비구조화 (3.4 이하) | `computed(() => props.x)` |
191
+ | Options API와 Composition API 혼용 | Composition으로 통일 |
192
+ | `onMounted` 안에서 페치 (Nuxt) | `useFetch`/`useAsyncData` |
193
+ | `defineProps()` runtime 형식 (TS 프로젝트) | `defineProps<{...}>()` 타입 형식 |
194
+ | `<style>` (scoped 없이) 컴포넌트 스타일 | `<style scoped>` 또는 CSS Module |
195
+ | 전역 watch 남발 | computed 우선 → 외부 시스템 동기화만 watch |
196
+ | Pinia state 직접 변경 (외부에서) | action으로 변경 |
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: fe-build-react
3
+ description: React/Next.js 요구사항을 받아 합의된 사내 원칙대로 구현. 명세→체크리스트→테스트 매핑을 강제하고, Server Components·React Compiler·Action 우선 패턴을 적용한다.
4
+ ---
5
+
6
+ # fe-build (React)
7
+
8
+ > **호출 시점**: "요구사항 명세 줄게, 이대로 구현해줘" 같은 신규 기능/페이지/컴포넌트 구현 요청.
9
+ > **선행 로딩**: `principles/common.md` + `principles/react.md` 필수.
10
+
11
+ ## 0. 절대 금지
12
+
13
+ 1. 명세 읽기 전에 코드 쓰지 마라.
14
+ 2. 명세에 없는 UX 결정 추가하지 마라 (명세 100% 충족 후에만).
15
+ 3. 명세에서 "옵셔널"이라고 한 항목 → 안 골라도 통과 테스트 *반드시* 작성.
16
+ 4. `useEffect` 안에서 데이터 변환/체인 setState 금지 (`principles/react.md` R2).
17
+ 5. Client Component 안에서 Server Component import 금지.
18
+
19
+ ## 1. 워크플로우 (이 순서를 깨지 마라)
20
+
21
+ ### Step 1 — 명세 → 체크리스트 변환
22
+
23
+ 명세 문서(또는 사용자 메시지) 받자마자, **다른 어떤 작업도 하기 전에**:
24
+
25
+ ```markdown
26
+ ## 체크리스트
27
+ - [ ] R-01: <명세 요구사항 한 줄>
28
+ - [ ] R-02: ...
29
+ ```
30
+
31
+ 각 항목은 **명세 원문 직접 인용**으로 작성. 해석/추론 금지.
32
+ 이 체크리스트를 사용자에게 보여주고, 빠진 게 있는지 *확인 후* 다음 단계.
33
+
34
+ ### Step 2 — 체크리스트 → 테스트 라인 매핑표
35
+
36
+ ```markdown
37
+ ## 매핑표
38
+ | 요구사항 | 컴포넌트/함수 | 테스트 파일:케이스 |
39
+ |----------|---------------|---------------------|
40
+ | R-01 | OrderForm | OrderForm.test.tsx:"제출 시 ..." |
41
+ | R-02 | useCart | useCart.test.ts:"select 옵션 미선택 시 통과해야 한다" |
42
+ ```
43
+
44
+ **옵셔널 항목은 반드시 "안 골라도 통과" 케이스로 매핑**. 표시(UI 분기)와 검증(로직 분기)이 같은 진실을 보는지 점검 — 이 불일치가 가장 흔한 명세 위반.
45
+
46
+ ### Step 3 — 아키텍처 결정
47
+
48
+ Server Component 기본. Client Component 가 필요한 신호 (R1) 가 있는 부분만 `'use client'`:
49
+
50
+ - `useState`/`useEffect` 필요
51
+ - 브라우저 API
52
+ - 이벤트 핸들러
53
+
54
+ **결정 기록 한 줄** (코드 주석 X, 사용자 메시지로):
55
+ "OrderForm은 Client (제출 핸들러). 상품 목록은 Server (정적)."
56
+
57
+ ### Step 4 — TDD (Red → Green → Refactor)
58
+
59
+ 매핑표의 각 행마다:
60
+ 1. 테스트 먼저 작성 (실패 확인)
61
+ 2. 최소 구현으로 통과
62
+ 3. 리팩터: [`common.md`](../../principles/common.md) 4원칙 적용
63
+
64
+ ### Step 5 — 셀프 리뷰
65
+
66
+ 구현 완료 후 [`fe-review/SKILL.md`](../fe-review/SKILL.md) 의 체크리스트로 자가 점검. 통과 못 한 항목 있으면 Step 4 로 회귀.
67
+
68
+ ### Step 6 — 매핑표 갱신 후 완료 선언
69
+
70
+ 매핑표 모든 행 ✅ + 모든 테스트 green. 그제서야 "완료" 선언.
71
+
72
+ ## 2. 구현 디폴트
73
+
74
+ ### 2.1 데이터 페칭
75
+
76
+ ```tsx
77
+ // Server Component — 기본
78
+ export default async function Page({ params }) {
79
+ const data = await fetch(`/api/items/${params.id}`, {
80
+ next: { tags: [`item-${params.id}`], revalidate: 60 }
81
+ }).then(r => r.json());
82
+ return <ItemView data={data} />;
83
+ }
84
+
85
+ // Client Component — 인터랙션 후 mutation
86
+ 'use client';
87
+ import { useActionState } from 'react';
88
+ async function submitAction(state, formData) {
89
+ 'use server';
90
+ await db.update(...);
91
+ revalidateTag(`item-${id}`); // 캐시 무효화 필수
92
+ return { ok: true };
93
+ }
94
+ ```
95
+
96
+ ### 2.2 폼
97
+
98
+ - 단순 폼 → `<form action>` + `useActionState` + `useFormStatus`
99
+ - 복잡한 검증 → React Hook Form + zod (조건: 5+ 필드 또는 cross-field 검증)
100
+ - **검증 규칙은 `optional` 케이스 빠뜨리지 마라** (`z.string().optional()` 분기 명시)
101
+
102
+ ### 2.3 상태
103
+
104
+ - 로컬: `useState`
105
+ - 페이지 단위 파생: `useSearchParams` (URL 단일 진실)
106
+ - 글로벌: Zustand 또는 Jotai (Redux 신규 도입 지양)
107
+ - 서버 상태: React Query (Pages Router) 또는 RSC fetch + revalidate (App Router)
108
+
109
+ ### 2.4 스타일
110
+
111
+ - Tailwind 또는 CSS Module + 디자인 토큰 (사내 디자인 시스템 우선)
112
+ - `next/image` 강제 (`<img>` 직접 사용 시 [HIGH] 리뷰 대상)
113
+
114
+ ### 2.5 접근성 디폴트
115
+
116
+ - 폼: `<label htmlFor>` 또는 `aria-label`
117
+ - 버튼 vs 링크: 동작 = `<button>`, 이동 = `<Link>`
118
+ - 모달: `<dialog>` 또는 focus trap 라이브러리 + `aria-modal`
119
+ - 인터랙티브 영역 최소 24×24 CSS px (WCAG 2.5.8)
120
+
121
+ ## 3. 출력 형식
122
+
123
+ 작업 완료 시 사용자에게:
124
+
125
+ ```
126
+ ## 완료 보고
127
+ - 체크리스트: 5/5 ✅
128
+ - 매핑표: 모든 행 테스트 green
129
+ - 변경 파일: <목록>
130
+ - 셀프 리뷰: principles/react.md R1-R9 통과
131
+ - 의사결정: <Server/Client 분리 기준 1-2줄>
132
+ ```
133
+
134
+ ## 4. 관련 문서
135
+
136
+ - 원칙: [`principles/common.md`](../../principles/common.md), [`principles/react.md`](../../principles/react.md)
137
+ - 리뷰: [`skills/react/fe-review/SKILL.md`](../fe-review/SKILL.md)
138
+ - 성능: [`skills/react/fe-perf/SKILL.md`](../fe-perf/SKILL.md)
139
+ - 코퍼스 원본: `sources/react/`, `sources/perf/`, `sources/toss-ff/`
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: fe-perf-react
3
+ description: React/Next.js 앱의 메모리 누수·CPU 병목·INP/LCP 회귀를 진단. Chrome DevTools Performance/Memory + React Profiler 절차 가이드. 흔한 누수 패턴 4종 + 픽스 코드.
4
+ ---
5
+
6
+ # fe-perf (React)
7
+
8
+ > **호출 시점**: "느려졌어", "메모리 누는 것 같아", "INP가 나빠졌어", Lighthouse 점수 회귀, OOM 등.
9
+ > **선행 로딩**: `principles/common.md` B + `sources/a11y-dx/chrome-devtools-*.md`.
10
+
11
+ ## 0. 분류 — 어떤 증상인가?
12
+
13
+ 진단 시작 전 사용자에게 1개 질문 (이미 명시했으면 스킵):
14
+
15
+ 1. **메모리** — 시간이 지날수록 RSS/heap 증가, 탭이 GB 단위, OOM
16
+ 2. **CPU/렌더** — 인터랙션 굳음, INP 나쁨, 스크롤 끊김, Long Task
17
+ 3. **로드 성능** — LCP 나쁨, TTFB 나쁨, 번들 크기
18
+
19
+ 분류에 따라 §1/§2/§3 으로 분기.
20
+
21
+ ---
22
+
23
+ ## 1. 메모리 누수 진단
24
+
25
+ 근거: `sources/a11y-dx/chrome-devtools-memory.md`
26
+
27
+ ### 1.1 측정 절차
28
+
29
+ 1. Chrome DevTools → Memory 패널
30
+ 2. **Heap snapshot 3회법** (가장 신뢰):
31
+ - 초기 페이지 → snapshot 1
32
+ - 의심 동작 N회 반복 (모달 열고 닫기 등) → snapshot 2
33
+ - GC 수동 트리거 (휴지통 아이콘) → snapshot 3
34
+ - snapshot 3에서 snapshot 1 대비 *증가한* Constructor 찾기
35
+ 3. Constructor 클릭 → Retainers 트리에서 살아있는 이유 추적
36
+ 4. `Detached` prefix 객체 발견 시 → DOM 누수
37
+
38
+ ### 1.2 흔한 누수 패턴 4종 + 픽스
39
+
40
+ | 패턴 | 증상 | 픽스 |
41
+ |------|------|------|
42
+ | **이벤트 리스너 미해제** | `window.addEventListener` 후 cleanup 없음 | `useEffect(() => { ... ; return () => removeEventListener(...) }, [])` |
43
+ | **타이머 미정리** | `setInterval`/`setTimeout` cleanup 없음 | cleanup에서 `clearInterval/Timeout` |
44
+ | **구독 미해제** | WebSocket/Observable/IntersectionObserver | cleanup에서 `unsubscribe`/`.disconnect()` |
45
+ | **클로저 캡처** | 콜백이 큰 state 캡처 + 외부 보관 | 콜백 안에서 `useRef` 로 최신값 참조, 또는 캡처 최소화 |
46
+
47
+ ### 1.3 React 특화 누수
48
+
49
+ - **RSC fetch 캐시 무한 누적** — `unstable_cache` key 안에 동적 무한 값 (timestamp 등) 사용 금지
50
+ - **`useEffect` 의존성에 객체/배열 매번 새로 생성** → 무한 리렌더 → 메모리 압박. Compiler 도입 또는 ref 안정화.
51
+ - **Context value 새 객체 매번 생성** → 모든 consumer 리렌더. `useMemo` 또는 Compiler.
52
+ - **Zustand 등 store에 거대한 캐시 무제한 적재** → LRU 또는 max-size 명시
53
+
54
+ ### 1.4 진단 결과 형식
55
+
56
+ ```
57
+ ## 메모리 누수 진단
58
+ - 증상: 모달 N회 열고 닫으면 heap +XMB
59
+ - 원인: <Detached HTMLDivElement>, retainer: AppHeader.onResize (이벤트 리스너 미해제)
60
+ - 픽스: src/components/AppHeader.tsx:34
61
+ return () => window.removeEventListener('resize', onResize)
62
+ - 검증: snapshot 3회법 재실행 → 누적 0 확인
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 2. CPU/렌더 병목 (INP 회귀)
68
+
69
+ 근거: `sources/a11y-dx/chrome-devtools-performance.md`, `sources/a11y-dx/react-devtools-profiler.md`, `sources/perf/02-inp.md`
70
+
71
+ ### 2.1 INP 진단 절차
72
+
73
+ 1. **`web-vitals/attribution` 로 INP 하위 구성 요소 식별**
74
+ ```ts
75
+ import { onINP } from 'web-vitals/attribution';
76
+ onINP((m) => console.log(m.attribution));
77
+ // 결과: { eventEntry, longAnimationFrameEntries, interactionTarget, inputDelay, processingDuration, presentationDelay }
78
+ ```
79
+ - **Input Delay** 큼 → 직전 Long Task 찾기 (Performance 탭)
80
+ - **Processing Duration** 큼 → 이벤트 핸들러 무거움
81
+ - **Presentation Delay** 큼 → 렌더/스타일 무거움
82
+
83
+ 2. **Chrome DevTools → Performance → 인터랙션 녹화**
84
+ - Long Task 색상 (빨강) 식별
85
+ - Flame Chart 가장 큰 박스 → 함수 이름
86
+ - **Forced Layout/Style Recalc** 노란 박스 → `offsetTop` 등 read-after-write 패턴
87
+
88
+ 3. **React Profiler → Why did this render?**
89
+ - 불필요한 리렌더 식별. 부모 리렌더 vs prop 변경 vs state 변경.
90
+
91
+ ### 2.2 INP 픽스 패턴
92
+
93
+ | 원인 | 픽스 |
94
+ |------|------|
95
+ | 핸들러 안 동기 무거운 계산 | `startTransition(() => setState(heavyCalc()))` 또는 `useDeferredValue` |
96
+ | 200ms+ Long Task | `scheduler.yield()` (지원 시) 또는 작업 chunk 분할 |
97
+ | 매 입력마다 외부 통신 | debounce (입력) / throttle (스크롤·리사이즈) |
98
+ | Context 거대 value 변경 | Context 분리 (Read/Write 분리) |
99
+ | useEffect 안 setState 체인 | 한 핸들러에서 모두 처리 |
100
+ | 큰 리스트 전체 렌더 | 가상화 (`react-window`, `@tanstack/react-virtual`) |
101
+
102
+ ### 2.3 React Compiler 환경
103
+
104
+ - 수동 `useMemo`/`useCallback` 추가는 *오히려 컴파일러 최적화 방해 가능*
105
+ - Compiler가 메모이즈 못 하는 코드 → ESLint plugin 경고 따라 코드 수정 (불순 함수 등)
106
+
107
+ ### 2.4 진단 결과 형식
108
+
109
+ ```
110
+ ## INP 진단
111
+ - 측정: INP p75 = 480ms (Poor)
112
+ - 하위: inputDelay=120ms, processingDuration=300ms, presentationDelay=60ms
113
+ - 원인: src/pages/list/index.tsx:88 onSearch 핸들러 안 동기 필터링 (n=10k)
114
+ - 픽스: startTransition + useDeferredValue
115
+ ```ts
116
+ const deferredQ = useDeferredValue(query);
117
+ const filtered = useMemo(() => items.filter(matches(deferredQ)), [items, deferredQ]);
118
+ ```
119
+ - 검증: INP p75 재측정 → ≤200ms 확인
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 3. 로드 성능 (LCP/TTFB/번들)
125
+
126
+ 근거: `sources/perf/03-lcp-cls.md`, `sources/perf/10-optimize-lcp.md`
127
+
128
+ ### 3.1 LCP 진단
129
+
130
+ 1. Lighthouse → LCP element 식별 (스크린샷 + 셀렉터)
131
+ 2. PageSpeed Insights → 실사용자 (Field) 데이터로 p75 확인
132
+ 3. Performance 패널 → 4 하위 구성 요소 시간 분해:
133
+ - **TTFB** (서버 응답)
134
+ - **Resource Load Delay** (LCP 자원 발견)
135
+ - **Resource Load Time** (다운로드)
136
+ - **Render Delay** (렌더)
137
+
138
+ ### 3.2 LCP 픽스 매트릭스
139
+
140
+ | 큰 구성 요소 | 픽스 |
141
+ |--------------|------|
142
+ | TTFB | RSC + 캐시 (revalidate, ISR), Edge 배포, DB 인덱스 |
143
+ | Resource Load Delay | `<link rel="preload">`, `next/image priority`, `fetchpriority="high"` |
144
+ | Resource Load Time | next/image (WebP/AVIF 자동), CDN, sizes 명시 |
145
+ | Render Delay | 클라이언트 hydration 무거움 → RSC 비중 증가, dynamic import |
146
+
147
+ ### 3.3 번들 분석
148
+
149
+ - `next build` 출력의 First Load JS 확인
150
+ - `@next/bundle-analyzer` 로 시각화 → 큰 의존성 → dynamic import 또는 RSC 분리
151
+ - `'use client'` 파일 안 markdown/syntax-highlight 등 큰 라이브러리 → Server 분리
152
+
153
+ ### 3.4 결과 형식
154
+
155
+ ```
156
+ ## LCP 진단
157
+ - 측정: LCP p75 = 3.8s (Needs Improvement)
158
+ - LCP element: src/pages/index.tsx <img src="/hero.jpg">
159
+ - 분해: TTFB 1.2s + LoadDelay 0.8s + LoadTime 1.1s + RenderDelay 0.7s
160
+ - 픽스 (우선순위):
161
+ 1. next/image + priority (Load Delay 제거): src/pages/index.tsx:24
162
+ 2. revalidate=300 → TTFB 1.2s → 200ms (캐시 hit)
163
+ 3. RSC로 메인 페이지 변환 (Render Delay 0.7s → 0.1s)
164
+ - 검증: 배포 후 24h Field Data 재확인
165
+ ```
166
+
167
+ ---
168
+
169
+ ## 4. 출력 (사용자에게 무조건 포함할 항목)
170
+
171
+ - **측정 수치 (before)** — "느낌"이 아니라 숫자
172
+ - **원인 (파일:라인 + retainer/콜스택)**
173
+ - **픽스 코드** — 카피-페이스트 가능
174
+ - **검증 방법** — 픽스 후 무엇을 측정해서 어떻게 비교할지
175
+
176
+ ## 5. 관련 문서
177
+
178
+ - [`principles/common.md`](../../principles/common.md), [`principles/react.md`](../../principles/react.md)
179
+ - 코퍼스: `sources/a11y-dx/chrome-devtools-{performance,memory}.md`, `sources/a11y-dx/react-devtools-profiler.md`, `sources/perf/`