@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,141 @@
1
+ ---
2
+ name: fe-review-react
3
+ description: React/Next.js PR/diff를 사내 원칙 기반으로 리뷰. `[SEVERITY] file:line — 이슈` 형식으로 출력. 4원칙 + Web Vitals + WCAG 2.2 + React 19/Compiler 안티패턴 체크.
4
+ ---
5
+
6
+ # fe-review (React)
7
+
8
+ > **호출 시점**: PR 링크, diff, 또는 변경된 파일 목록 받았을 때.
9
+ > **선행 로딩**: `principles/common.md` + `principles/react.md`.
10
+
11
+ ## 0. 출력 형식 (반드시 준수)
12
+
13
+ ```
14
+ [SEVERITY] path/to/file.tsx:42 — <한 줄 이슈>
15
+ 근거: principles/<doc>.md <섹션> 또는 sources/<file>.md
16
+ 픽스: <코드 또는 한 줄 처방>
17
+ ```
18
+
19
+ **SEVERITY**: `HIGH` (즉시 픽스 / 머지 차단), `MED` (다음 PR 가능), `LOW` (취향/넛지).
20
+
21
+ 리뷰 시작 시 한 줄 요약:
22
+ ```
23
+ ## 리뷰 요약
24
+ - 변경 범위: <files / +N -M>
25
+ - HIGH N개, MED N개, LOW N개
26
+ - 머지 가능 여부: [차단 / 권장 수정 후 / 가능]
27
+ ```
28
+
29
+ ## 1. 체크 순서 (위반 시 곧장 [HIGH])
30
+
31
+ ### Phase 1 — 명세/요구사항 정합성 (가장 먼저)
32
+
33
+ - 요구사항 명세가 함께 제공됐는가? 없으면 다음 출처 후보를 사용자에게 *명시적으로 요청* 후 일시 중지:
34
+ 1. PR/MR 본문의 "Why / Spec" 섹션
35
+ 2. 연결된 이슈 (Linear/Jira/GitHub Issue)
36
+ 3. Notion/Confluence 명세 페이지 링크
37
+ 4. 디자인 명세 (Figma 코멘트 / 디자인 토큰 문서)
38
+ 5. 위 어디에도 없으면 *구두 합의 내용을 텍스트로 적어달라고* 요청
39
+ → 명세 없는 리뷰는 "취향 코멘트" 가 된다. 받기 전에는 Phase 2 이후로 *진행하지 않는다.*
40
+ - 명세 옵셔널 항목이 검증 로직에서 필수 취급된 곳 없는가? (표시/검증 불일치 — 가장 흔한 명세 위반)
41
+ → 점검 절차: 명세에서 "옵셔널" 단어를 grep → 해당 필드/옵션 → UI 분기 (`isRequired`, `disabled`) ↔ 검증 분기 (`return error`, `continue`) 가 **같은 진실을 보는가**.
42
+ - README/주석이 자랑하는 패턴이 실제 코드에서 누락된 곳은? (discriminated union 분기 누락 등)
43
+
44
+ ### Phase 2 — React 19 / Next.js 16 안티패턴 (`principles/react.md` R9)
45
+
46
+ - [ ] `useEffect` 안에서 데이터 변환 / 체인 setState — [HIGH] 픽스: 렌더 중 파생값
47
+ - [ ] `useState` + `useEffect` 로 fetch — [HIGH] 픽스: Suspense + `use()` 또는 React Query
48
+ - [ ] Compiler 환경에서 수동 `useMemo`/`useCallback` 추가 — [MED] 픽스: 제거
49
+ - [ ] Client 컴포넌트가 Server 컴포넌트 import — [HIGH] 픽스: `children` prop 주입
50
+ - [ ] Server Component 안에 `useState`/`useEffect` — [HIGH] 픽스: `'use client'` 분리
51
+ - [ ] Form 상태 `useState` 수동 관리 (단순 폼) — [MED] 픽스: `<form action>` + `useActionState`
52
+ - [ ] `<img>` 직접 사용 — [HIGH] 픽스: `next/image` (또는 `width/height` 명시)
53
+ - [ ] 변경 액션 후 `revalidateTag`/`revalidatePath` 누락 — [HIGH] 픽스: 호출 추가
54
+ - [ ] `'use client'` 파일에 대형 의존성 import — [MED] 픽스: dynamic import 또는 Server 분리
55
+
56
+ ### Phase 3 — 코드 품질 4원칙 (`principles/common.md` A)
57
+
58
+ 가독성 > 예측성 > 응집도 > 결합도 순으로 검토.
59
+
60
+ - [ ] 같이 실행되지 않는 코드 분리 (권한/조건별 분기 한 컴포넌트) — [MED]
61
+ - [ ] 구현 상세 노출 (인증 체크 inline 등) — [MED] 픽스: `<AuthGuard>`
62
+ - [ ] 다목적 훅 (페이지 전체 쿼리파람 통합) — [HIGH] 픽스: 책임별 분리
63
+ - [ ] 익명 복잡 조건 — [LOW] 픽스: 명명된 변수
64
+ - [ ] 매직 넘버 — [LOW] 픽스: 상수
65
+ - [ ] 시점 이동 3단계+ — [MED] 픽스: 평탄화
66
+ - [ ] 중첩 삼항 — [LOW] 픽스: IIFE + if
67
+ - [ ] 부등식 순서 (`a >= b && a <= c`) — [LOW] 픽스: `b <= a && a <= c`
68
+ - [ ] 같은 종류 함수 반환 타입 불일치 — [MED] 픽스: 통일
69
+ - [ ] 숨은 로직 (fetch 안 logging 등) — [MED] 픽스: 책임 분리
70
+ - [ ] Props 3단 드릴링 — [HIGH] 픽스: Composition or Context
71
+ - [ ] 잘못된 공통화 (유사 컴포넌트 강제 추출) — [HIGH] 픽스: 중복 허용
72
+
73
+ ### Phase 4 — Web Vitals (`principles/common.md` B)
74
+
75
+ - [ ] LCP 후보 이미지 `priority` 누락 — [HIGH]
76
+ - [ ] 이미지 `width/height`/`fill` 없음 → CLS — [HIGH]
77
+ - [ ] 메인 스레드 Long Task (>200ms) 동기 계산 — [MED] 픽스: `startTransition` 또는 chunk 분할
78
+ - [ ] `useOptimistic` 사용 가능 인터랙션에 미적용 (좋아요/메시지 등) — [LOW]
79
+ - [ ] `font-display` 미설정 → 폰트 깜빡임 → CLS — [MED]
80
+
81
+ ### Phase 5 — 접근성 (`principles/common.md` C, WCAG 2.2)
82
+
83
+ - [ ] `<div onClick>` 또는 `<span onClick>` — [HIGH] 픽스: `<button>`
84
+ - [ ] **사내/외부 wrapper 컴포넌트에 `onClick`** (`<Card onClick>`, `<Icon onClick>`, `<ListRow onClick>`) — [MED→HIGH] 내부 구현이 `<div>` 면 키보드 접근 불가. 1회 확인 절차:
85
+ 1. wrapper 컴포넌트 정의 파일 열기 → 루트 엘리먼트 확인
86
+ 2. `<button>` 또는 `role="button"` + `tabIndex={0}` + `onKeyDown` (Enter/Space) 있으면 OK
87
+ 3. 아니면 사내 a11y 프리미티브(`<Clickable>` 등)로 감싸거나 `<button>` 사용
88
+ - [ ] `<button>` 에 `type` 누락 (form 내부) — [MED] 픽스: `type="button"` 또는 `"submit"`
89
+ - [ ] 폼 컨트롤 `<label>` 누락 — [HIGH]
90
+ - [ ] 인터랙티브 영역 < 24×24 CSS px — [MED] (WCAG 2.5.8)
91
+ - [ ] `outline: none` 만 있고 `:focus-visible` 대안 없음 — [HIGH] (WCAG 2.4.7)
92
+ - [ ] 이미지 `alt` 누락 또는 부정확 — [HIGH]
93
+ - [ ] 텍스트 대비 4.5:1 (본문) / 3:1 (큰 글자) 미만 — [HIGH]
94
+ - [ ] 모달 focus trap 누락 — [HIGH]
95
+ - [ ] sticky header 가 포커스 가림 (WCAG 2.4.11) — [MED] 픽스: `scroll-padding-top`
96
+
97
+ ### Phase 6 — 보안/안전 (전역 규칙)
98
+
99
+ - [ ] `.env`/credential 커밋 — [HIGH] 즉시 차단
100
+ - [ ] `dangerouslySetInnerHTML` + 외부 입력 — [HIGH] sanitize 필요
101
+ - [ ] 빈 catch 블록 — [HIGH] 최소 로그 또는 re-throw
102
+ - [ ] eslint-disable / @ts-ignore 무근거 — [MED] 사유 주석 필요
103
+
104
+ ### Phase 7 — 안티패턴 일반 (전역 규칙)
105
+
106
+ - [ ] 50줄 초과 함수 — [MED] 분리
107
+ - [ ] 중첩 깊이 5+ — [MED] early return
108
+ - [ ] 같은 파일 3+ 회 수정 흔적 (PR 내) — [MED] 전체 재설계 권고
109
+
110
+ ## 2. 우선순위 충돌 시
111
+
112
+ - a11y vs 가독성 → a11y 우선
113
+ - 성능 vs 가독성 → 사용자 영향 큰 쪽 (LCP/INP 결정적 개선이면 성능)
114
+ - React Compiler vs 명시적 메모 → Compiler 환경이면 자동, 아니면 명시적 유지
115
+
116
+ ## 3. 출력 예시
117
+
118
+ ```
119
+ ## 리뷰 요약
120
+ - 변경 범위: src/pages/order/* 8 files +312 -45
121
+ - HIGH 3개, MED 5개, LOW 2개
122
+ - 머지 가능 여부: 차단
123
+
124
+ [HIGH] src/pages/order/OrderForm.tsx:88 — select 옵션 옵셔널인데 검증에서 차단
125
+ 근거: principles/common.md D (안티패턴 카탈로그)
126
+ 픽스: optionRules.ts에서 `opt.type !== 'grid'` 분기 `continue`
127
+
128
+ [HIGH] src/components/HeroImage.tsx:12 — <img> 직접 사용
129
+ 근거: principles/react.md R8
130
+ 픽스: next/image + priority + sizes
131
+
132
+ [MED] src/hooks/usePageState.ts:1 — 쿼리파람 통합 훅 (다목적)
133
+ 근거: principles/common.md A.4, sources/toss-ff/coupling-use-page-state.md
134
+ 픽스: 파라미터별 독립 훅으로 분리 (useOrderId, useFilter ...)
135
+ ```
136
+
137
+ ## 4. 관련 문서
138
+
139
+ - [`principles/common.md`](../../principles/common.md), [`principles/react.md`](../../principles/react.md)
140
+ - [`fe-build/SKILL.md`](../fe-build/SKILL.md), [`fe-perf/SKILL.md`](../fe-perf/SKILL.md)
141
+ - 안티패턴 카탈로그: `principles/common.md` D + `principles/react.md` R9
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: fe-build-vue
3
+ description: Vue 3/Nuxt 3 요구사항을 받아 사내 원칙대로 구현. 명세→체크리스트→테스트 매핑 강제, Composition API + `<script setup>` + Nuxt 데이터 페칭 디폴트.
4
+ ---
5
+
6
+ # fe-build (Vue)
7
+
8
+ > **호출 시점**: "요구사항 명세 줄게, Vue로 구현해줘".
9
+ > **선행 로딩**: `principles/common.md` + `principles/vue.md` 필수.
10
+
11
+ ## 0. 절대 금지
12
+
13
+ 1. 명세 읽기 전에 코드 쓰지 마라.
14
+ 2. 명세에 없는 UX 결정 추가하지 마라.
15
+ 3. 명세 "옵셔널" 항목 → "안 골라도 통과" 테스트 *반드시*.
16
+ 4. `reactive` 객체 비구조화 금지 (`principles/vue.md` V3.2).
17
+ 5. props 비구조화 (3.4 이하) 금지. 3.5+ 에서도 watch source 는 getter 필수.
18
+ 6. Nuxt: `onMounted` 안 페치 금지 (`useFetch`/`useAsyncData` 사용).
19
+
20
+ ## 1. 워크플로우
21
+
22
+ ### Step 1 — 명세 → 체크리스트
23
+
24
+ ```markdown
25
+ ## 체크리스트
26
+ - [ ] R-01: <명세 원문 인용>
27
+ - [ ] R-02: ...
28
+ ```
29
+
30
+ 해석 없이 원문만. 사용자에게 보여주고 빠진 항목 확인.
31
+
32
+ ### Step 2 — 매핑표
33
+
34
+ ```markdown
35
+ | 요구사항 | 컴포넌트/composable | 테스트 |
36
+ |----------|---------------------|--------|
37
+ | R-01 | OrderForm.vue | OrderForm.spec.ts:"제출 시 ..." |
38
+ | R-02 | useCart | useCart.spec.ts:"옵셔널 옵션 미선택 시 통과" |
39
+ ```
40
+
41
+ ### Step 3 — 아키텍처 결정
42
+
43
+ 상태 위치 결정 (V4.1):
44
+ - 단일 컴포넌트: `ref`/`reactive`
45
+ - 2+ 컴포넌트 공유: composable
46
+ - 싱글톤/SSR: Pinia store
47
+
48
+ Nuxt 페이지/컴포넌트:
49
+ - 페이지 진입 시 데이터: `useFetch` 또는 `useAsyncData`
50
+ - 이벤트 후 mutation: `$fetch`
51
+ - BFF 필요: `server/api/*.ts`
52
+
53
+ 결정 한 줄 사용자에게 공유.
54
+
55
+ ### Step 4 — TDD (vitest + Vue Test Utils 또는 Nuxt test-utils)
56
+
57
+ 각 매핑표 행마다 Red → Green → Refactor.
58
+
59
+ ### Step 5 — 셀프 리뷰
60
+
61
+ [`fe-review/SKILL.md`](../fe-review/SKILL.md) 체크리스트로 자가 점검.
62
+
63
+ ### Step 6 — 완료 선언
64
+
65
+ 모든 행 ✅ + 테스트 green.
66
+
67
+ ## 2. 구현 디폴트
68
+
69
+ ### 2.1 SFC 골격
70
+
71
+ ```vue
72
+ <script setup lang="ts">
73
+ import { ref, computed } from 'vue';
74
+
75
+ const props = defineProps<{ id: string }>();
76
+ const emit = defineEmits<{ submit: [payload: FormPayload] }>();
77
+
78
+ const count = ref(0);
79
+ const double = computed(() => count.value * 2);
80
+
81
+ function onSubmit() {
82
+ emit('submit', { id: props.id, count: count.value });
83
+ }
84
+ </script>
85
+
86
+ <template>
87
+ <form @submit.prevent="onSubmit">
88
+ <!-- ... -->
89
+ </form>
90
+ </template>
91
+
92
+ <style scoped>
93
+ /* 클래스 셀렉터 사용 (element 셀렉터 지양) */
94
+ </style>
95
+ ```
96
+
97
+ ### 2.2 데이터 페칭 (Nuxt)
98
+
99
+ ```ts
100
+ // 페이지 진입 시 — SSR + hydration 일관
101
+ const { data: order, error } = await useFetch(`/api/orders/${route.params.id}`, {
102
+ key: `order-${route.params.id}`,
103
+ });
104
+
105
+ // 이벤트 핸들러
106
+ async function onSubmit() {
107
+ const res = await $fetch('/api/orders', { method: 'POST', body: form.value });
108
+ await refreshCookie('order-list'); // 또는 router.push
109
+ }
110
+ ```
111
+
112
+ ### 2.3 폼
113
+
114
+ - 단순: `ref` + `v-model` + `@submit.prevent`
115
+ - 복잡: VeeValidate + zod 또는 Vue 3 + `@vue/reactivity` 기반 폼 라이브러리
116
+ - **검증 규칙에 optional 분기 명시** — `z.string().optional()`
117
+
118
+ ### 2.4 상태
119
+
120
+ - 컴포넌트 로컬: `ref`/`computed`
121
+ - 공유 로직: composable (`useOrder.ts`)
122
+ - 글로벌: Pinia setup store (`defineStore('order', () => {...})`)
123
+ - URL 단일 진실: `useRoute().query` + `navigateTo({ query: ... })`
124
+
125
+ ### 2.5 접근성 디폴트
126
+
127
+ - 폼: `<label :for="id">` + `:id`
128
+ - 동작 vs 이동: `<button>` vs `<NuxtLink>` (또는 `<router-link>`)
129
+ - 모달: `<dialog>` + focus trap
130
+ - 인터랙티브 24×24 CSS px
131
+
132
+ ## 3. 출력 형식
133
+
134
+ ```
135
+ ## 완료 보고
136
+ - 체크리스트: N/N ✅
137
+ - 매핑표: 모든 행 green
138
+ - 변경 파일: <목록>
139
+ - 셀프 리뷰: principles/vue.md V1-V6 통과
140
+ - 의사결정: <상태 위치 / 페칭 함수 선택 근거 1-2줄>
141
+ ```
142
+
143
+ ## 4. 관련 문서
144
+
145
+ - [`principles/common.md`](../../principles/common.md), [`principles/vue.md`](../../principles/vue.md)
146
+ - [`skills/vue/fe-review/SKILL.md`](../fe-review/SKILL.md)
147
+ - [`skills/vue/fe-perf/SKILL.md`](../fe-perf/SKILL.md)
148
+ - 코퍼스: `sources/vue/`, `sources/perf/`, `sources/toss-ff/`
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: fe-perf-vue
3
+ description: Vue 3/Nuxt 3 앱 메모리 누수·CPU 병목·INP/LCP 회귀 진단. Chrome DevTools + Vue DevTools + Nuxt DevTools 절차. 반응성 관련 누수 + watch 폭주 패턴.
4
+ ---
5
+
6
+ # fe-perf (Vue)
7
+
8
+ > **호출 시점**: "느려졌어", "메모리 누는 것 같아", "INP 나빠", Nuxt SSR 응답 느림.
9
+ > **선행 로딩**: `principles/common.md` B + `sources/a11y-dx/chrome-devtools-*.md`.
10
+
11
+ ## 0. 분류
12
+
13
+ 1. **메모리** — heap 증가 / OOM
14
+ 2. **CPU/렌더** — INP 나쁨 / 인터랙션 굳음
15
+ 3. **로드** — LCP/TTFB 나쁨 / 번들 큼
16
+
17
+ ---
18
+
19
+ ## 1. 메모리 누수 진단
20
+
21
+ 근거: `sources/a11y-dx/chrome-devtools-memory.md`
22
+
23
+ ### 1.1 측정 (Heap Snapshot 3회법)
24
+
25
+ (React 와 동일 절차 — 도구는 동일)
26
+
27
+ 1. snapshot 1 (초기) → 의심 동작 N회 → snapshot 2 → GC → snapshot 3
28
+ 2. 증가한 Constructor + Retainer 트리 추적
29
+ 3. `Detached` prefix = DOM 누수
30
+
31
+ ### 1.2 Vue 특화 누수 패턴
32
+
33
+ | 패턴 | 증상 | 픽스 |
34
+ |------|------|------|
35
+ | **watch 핸들러가 외부 변수 캡처** | watch 콜백이 큰 객체 참조 + 컴포넌트 unmount 후에도 살아있음 | `onUnmounted` 에서 `stopWatch()` 호출 (또는 컴포넌트 스코프 watch면 자동 정리) |
36
+ | **`watchEffect` 가 reactive ref 외부 보관** | 외부 store에 ref 저장 | 컴포넌트 scope 안에서만 사용 |
37
+ | **글로벌 이벤트 리스너 미해제** | `window.addEventListener` + `onUnmounted` 누락 | `useEventListener` (VueUse) 또는 명시적 `onUnmounted` cleanup |
38
+ | **Pinia store 무제한 누적** | 캐시/히스토리에 max 없음 | LRU 또는 max-size |
39
+ | **`provide` 객체에 컴포넌트 참조 저장** | 자식 컴포넌트가 unmount 후에도 부모 provide 통해 살아있음 | provide는 primitive/ref만 |
40
+ | **Nuxt asyncData payload 거대** | SSR payload에 직렬화된 거대 객체 → 메모리 + hydration 비용 | `transform` 옵션으로 필요한 필드만 |
41
+
42
+ ### 1.3 진단 결과 형식
43
+
44
+ ```
45
+ ## 메모리 누수 진단
46
+ - 증상: 모달 N회 열고 닫으면 heap +XMB
47
+ - 원인: Detached HTMLDivElement, retainer = window.onResize (onUnmounted 정리 누락)
48
+ - 픽스: src/components/AppHeader.vue:32
49
+ onUnmounted(() => window.removeEventListener('resize', onResize))
50
+ // 또는 VueUse useEventListener 사용
51
+ - 검증: 3회법 재실행 → 누적 0
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 2. CPU/렌더 병목 (INP 회귀)
57
+
58
+ 근거: `sources/perf/02-inp.md`, `sources/a11y-dx/chrome-devtools-performance.md`
59
+
60
+ ### 2.1 INP 진단
61
+
62
+ (측정 도구는 React 동일 — `web-vitals/attribution`)
63
+
64
+ ```ts
65
+ import { onINP } from 'web-vitals/attribution';
66
+ onINP((m) => console.log(m.attribution));
67
+ ```
68
+
69
+ Chrome DevTools Performance 패널 + Vue DevTools (Inspector → Performance 탭) 병행.
70
+
71
+ ### 2.2 Vue 특화 픽스 패턴
72
+
73
+ | 원인 | 픽스 |
74
+ |------|------|
75
+ | 핸들러 안 동기 무거운 계산 | 작업 chunk + `scheduler.yield()` 또는 `requestIdleCallback` |
76
+ | 무거운 watch (deep, 큰 객체) | computed로 대체 가능한지 검토. 안 되면 throttle/debounce |
77
+ | `watchEffect` 가 너무 자주 트리거 | 명시적 `watch` + 좁은 source |
78
+ | reactive 큰 객체 변경 → 의존 컴포넌트 폭주 | `shallowRef`/`shallowReactive` 또는 분할 |
79
+ | 큰 리스트 v-for | `v-memo` (안정 분기 시) 또는 가상 스크롤 (VueUse `useVirtualList`) |
80
+ | 매 입력마다 외부 통신 | debounce (VueUse `useDebounceFn`) |
81
+ | reactivity 추적 폭주 | `markRaw()` (반응성 불필요 큰 객체) |
82
+
83
+ ### 2.3 Vue DevTools — 렌더 추적
84
+
85
+ - Performance 탭 → 컴포넌트 렌더 시간 측정
86
+ - Highlight Updates → 어떤 컴포넌트가 재렌더되는지 시각화
87
+ - 불필요한 자식 리렌더 → props 안정화 또는 `defineProps` 분리
88
+
89
+ ### 2.4 진단 결과 형식
90
+
91
+ ```
92
+ ## INP 진단
93
+ - 측정: INP p75 = 460ms (Poor)
94
+ - 하위: inputDelay=80ms, processingDuration=340ms, presentationDelay=40ms
95
+ - 원인: pages/list.vue:55 onSearch 안 동기 필터 (n=12k) + reactive 전체 변경
96
+ - 픽스:
97
+ ```ts
98
+ const query = ref('');
99
+ const debouncedQuery = useDebounceFn((v: string) => query.value = v, 150);
100
+ const filtered = computed(() => items.filter(matches(query.value)));
101
+ // items가 큰 정적 데이터면 shallowRef로 wrap
102
+ ```
103
+ - 검증: INP p75 재측정 → ≤200ms
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 3. 로드 성능 (Nuxt SSR / LCP)
109
+
110
+ 근거: `sources/perf/03-lcp-cls.md`, `sources/perf/10-optimize-lcp.md`
111
+
112
+ ### 3.1 LCP 진단
113
+
114
+ (측정 절차는 React 동일 — Lighthouse, PageSpeed Insights, Performance 패널)
115
+
116
+ ### 3.2 Nuxt SSR 특화 픽스
117
+
118
+ | 큰 구성 요소 | 픽스 |
119
+ |--------------|------|
120
+ | **TTFB** | Nuxt `defineEventHandler` 안 동기 작업 줄이기, Nitro `cachedFunction`, ISR (`routeRules: { isr: true }`) |
121
+ | **Resource Load Delay** | `<NuxtImg>` `preload`, `<Head>` 안 `<link rel="preload">` |
122
+ | **Resource Load Time** | `@nuxt/image` (WebP/AVIF), CDN, sizes |
123
+ | **Render Delay** | hydration mismatch 확인 (콘솔 경고), `<ClientOnly>` 신중하게 |
124
+
125
+ ### 3.3 Nuxt asyncData 최적화
126
+
127
+ - `transform` 으로 직렬화 payload 슬림화
128
+ - `pick` 으로 필요한 필드만 (`useFetch(url, { pick: ['id', 'name'] })`)
129
+ - `lazy: true` (비차단) — 단 LCP 콘텐츠는 lazy 금지
130
+
131
+ ### 3.4 번들 분석
132
+
133
+ - `nuxi analyze` 로 클라이언트 번들 시각화
134
+ - 큰 컴포넌트 → `defineAsyncComponent` 또는 `components.lazy`
135
+ - 서버 전용 의존성은 `server/` 또는 `nitro.experimental.componentIslands`
136
+
137
+ ### 3.5 결과 형식
138
+
139
+ ```
140
+ ## LCP 진단
141
+ - 측정: LCP p75 = 3.5s (Needs Improvement)
142
+ - LCP element: pages/index.vue <img src="/hero.jpg">
143
+ - 분해: TTFB 1.1s + LoadDelay 0.7s + LoadTime 1.0s + RenderDelay 0.7s
144
+ - 픽스 (우선순위):
145
+ 1. `<NuxtImg src="/hero.jpg" preload format="webp">` — LoadDelay 제거
146
+ 2. nuxt.config: routeRules { '/': { isr: 60 } } — TTFB 1.1s → 150ms
147
+ 3. hydration mismatch (콘솔 경고) 제거 — RenderDelay 단축
148
+ - 검증: 배포 후 24h Field Data 재확인
149
+ ```
150
+
151
+ ---
152
+
153
+ ## 4. 출력 (필수)
154
+
155
+ - **측정 수치 (before)**
156
+ - **원인 (파일:라인 + retainer/콜스택)**
157
+ - **픽스 코드** — 카피-페이스트 가능
158
+ - **검증 방법**
159
+
160
+ ## 5. 관련 문서
161
+
162
+ - [`principles/common.md`](../../principles/common.md), [`principles/vue.md`](../../principles/vue.md)
163
+ - 코퍼스: `sources/a11y-dx/chrome-devtools-*`, `sources/a11y-dx/react-devtools-profiler.md` (DevTools 개념 동일), `sources/perf/`
@@ -0,0 +1,136 @@
1
+ ---
2
+ name: fe-review-vue
3
+ description: Vue 3/Nuxt 3 PR/diff를 사내 원칙 기반으로 리뷰. `[SEVERITY] file:line — 이슈` 형식. 4원칙 + Web Vitals + WCAG 2.2 + Composition API/반응성 함정 체크.
4
+ ---
5
+
6
+ # fe-review (Vue)
7
+
8
+ > **호출 시점**: PR 링크, diff, 변경 파일 목록.
9
+ > **선행 로딩**: `principles/common.md` + `principles/vue.md`.
10
+
11
+ ## 0. 출력 형식
12
+
13
+ ```
14
+ [SEVERITY] path/to/file.vue:42 — <한 줄 이슈>
15
+ 근거: principles/<doc>.md <섹션>
16
+ 픽스: <코드 또는 한 줄 처방>
17
+ ```
18
+
19
+ `HIGH` / `MED` / `LOW`.
20
+
21
+ 리뷰 시작 시:
22
+ ```
23
+ ## 리뷰 요약
24
+ - 변경 범위: <files / +N -M>
25
+ - HIGH N, MED N, LOW N
26
+ - 머지 가능 여부: [차단 / 권장 수정 후 / 가능]
27
+ ```
28
+
29
+ ## 1. 체크 순서
30
+
31
+ ### Phase 1 — 명세 정합성
32
+
33
+ - 명세 동봉됐는가? 없으면 다음 출처 후보를 *명시적으로 요청* 후 일시 중지:
34
+ 1. PR/MR 본문의 "Why / Spec" 섹션
35
+ 2. 연결된 이슈 (Linear/Jira/GitHub Issue)
36
+ 3. Notion/Confluence 명세 페이지
37
+ 4. 디자인 명세 (Figma 코멘트 / 디자인 토큰)
38
+ 5. 위에도 없으면 *구두 합의 내용을 텍스트로 적어달라고* 요청
39
+ → 명세 없는 리뷰는 "취향 코멘트". 받기 전에는 Phase 2 진행 금지.
40
+ - 옵셔널 항목이 검증 로직에서 필수 취급된 곳 없는가? (표시/검증 불일치 패턴)
41
+ → 점검 절차: 명세에서 "옵셔널" grep → 해당 필드 → 템플릿 `:disabled` / `v-if="isRequired"` ↔ 스크립트 검증 분기가 **같은 진실을 보는가**.
42
+ - README/주석의 자랑 vs 실제 코드 결함?
43
+
44
+ ### Phase 2 — Vue 안티패턴 (`principles/vue.md` V6)
45
+
46
+ - [ ] `v-if` + `v-for` 동시 사용 — [HIGH] 픽스: computed 필터 후 v-for
47
+ - [ ] `v-for` key 누락 / index key — [HIGH] 픽스: 안정 id
48
+ - [ ] `reactive` 비구조화 — [HIGH] 픽스: `toRefs` 또는 `ref`
49
+ - [ ] props 비구조화 (Vue 3.4 이하) — [HIGH] 픽스: `computed(() => props.x)`
50
+ - [ ] Options API 신규 코드 — [MED] 픽스: `<script setup>` 마이그레이션
51
+ - [ ] Nuxt `onMounted` 안 페치 — [HIGH] 픽스: `useFetch`/`useAsyncData`
52
+ - [ ] `defineProps()` runtime 형식 (TS 프로젝트) — [LOW] 픽스: `defineProps<{...}>()`
53
+ - [ ] `<style>` (scoped 없이) — [MED] 픽스: `<style scoped>` 또는 CSS Module
54
+ - [ ] 전역 watch 남발 (computed로 가능한데도) — [MED]
55
+ - [ ] Pinia state 외부 직접 변경 — [HIGH] 픽스: action 통한 변경
56
+ - [ ] `useAsyncData` key 누락 또는 비고유 — [HIGH] 픽스: unique key 명시
57
+ - [ ] `useFetch` watcher prop 안 쓰고 수동 refetch — [MED] 픽스: `watch` 옵션 사용
58
+
59
+ ### Phase 3 — Vue 스타일 가이드 Priority A (`principles/vue.md` V1.1)
60
+
61
+ - [ ] 컴포넌트 이름 단일 단어 (`Todo`) — [HIGH] 픽스: 다중 단어 (`TodoItem`)
62
+ - [ ] prop 타입 미명세 — [MED] 픽스: TS 타입 형식
63
+ - [ ] 컴포넌트 데이터 객체 직접 (Options API) — [HIGH] 픽스: 함수 반환
64
+
65
+ ### Phase 4 — 코드 품질 4원칙 (`principles/common.md` A)
66
+
67
+ (React fe-review 와 동일. 가독성 > 예측성 > 응집도 > 결합도 순.)
68
+
69
+ - [ ] 다목적 composable (`usePageState`) — [HIGH] 책임별 분리
70
+ - [ ] 매직 넘버 — [LOW]
71
+ - [ ] 시점 이동 3단계+ — [MED]
72
+ - [ ] 중첩 삼항 — [LOW]
73
+ - [ ] 같은 종류 composable 반환 타입 불일치 — [MED]
74
+ - [ ] 숨은 로직 — [MED]
75
+ - [ ] Props 3단 드릴링 — [HIGH] 픽스: `provide`/`inject` 또는 slot composition
76
+ - [ ] 잘못된 공통화 — [HIGH] 중복 허용
77
+
78
+ ### Phase 5 — Web Vitals (`principles/common.md` B)
79
+
80
+ - [ ] LCP 이미지 `<NuxtImg>` priority 누락 또는 `<img>` 직접 — [HIGH]
81
+ - [ ] 이미지 width/height 미명세 → CLS — [HIGH]
82
+ - [ ] 동기 무거운 watch — [MED] 픽스: `watchEffect` + `throttle/debounce`
83
+ - [ ] `key` 변경으로 컴포넌트 강제 리마운트 — [MED] (의도적이 아니면)
84
+ - [ ] SSR/CSR 불일치 (Nuxt) — [HIGH] 픽스: `useFetch`/`useAsyncData`로 양쪽 동일 데이터
85
+
86
+ ### Phase 6 — 접근성 (WCAG 2.2)
87
+
88
+ (React fe-review Phase 5 와 동일 항목.)
89
+
90
+ - [ ] `<div @click>` 버튼 — [HIGH] `<button>` 사용
91
+ - [ ] **사내/외부 wrapper 컴포넌트에 `@click`** (`<Card @click>`, `<BaseIcon @click>`, `<ListRow @click>`) — [MED→HIGH] 내부가 `<div>` 면 키보드 접근 불가. 1회 확인:
92
+ 1. 컴포넌트 정의 `.vue` 파일 → 템플릿 루트 엘리먼트
93
+ 2. `<button>` 또는 `role="button"` + `tabindex="0"` + `@keydown.enter`/`.space` 있으면 OK
94
+ 3. 아니면 사내 a11y 프리미티브로 감싸거나 `<button>` 사용
95
+ - [ ] 폼 `<label :for>` 누락 — [HIGH]
96
+ - [ ] 인터랙티브 < 24×24 — [MED]
97
+ - [ ] `:focus-visible` 누락 — [HIGH]
98
+ - [ ] 이미지 alt — [HIGH]
99
+ - [ ] 모달 focus trap — [HIGH]
100
+ - [ ] sticky header 포커스 가림 — [MED]
101
+
102
+ ### Phase 7 — 보안/안티패턴
103
+
104
+ - [ ] `v-html` + 외부 입력 — [HIGH] sanitize 필요
105
+ - [ ] `.env`/credential 커밋 — [HIGH]
106
+ - [ ] 빈 catch — [HIGH]
107
+ - [ ] eslint-disable 무근거 — [MED]
108
+ - [ ] 50줄 초과 함수 / 5+ 중첩 — [MED]
109
+
110
+ ## 2. 출력 예시
111
+
112
+ ```
113
+ ## 리뷰 요약
114
+ - 변경 범위: pages/order/*.vue 6 files +210 -38
115
+ - HIGH 4, MED 3, LOW 1
116
+ - 머지 가능 여부: 차단
117
+
118
+ [HIGH] pages/order/[id].vue:24 — onMounted 안에서 페치 → SSR/CSR 불일치
119
+ 근거: principles/vue.md V5.2
120
+ 픽스:
121
+ const { data } = await useAsyncData(`order-${route.params.id}`,
122
+ () => $fetch(`/api/orders/${route.params.id}`))
123
+
124
+ [HIGH] components/OptionList.vue:45 — v-if와 v-for 동시 사용
125
+ 근거: principles/vue.md V1.1
126
+ 픽스: computed로 필터 후 v-for
127
+
128
+ [HIGH] stores/order.ts:30 — 외부에서 store.state 직접 변경
129
+ 근거: principles/vue.md V6
130
+ 픽스: action 추가 후 호출
131
+ ```
132
+
133
+ ## 3. 관련 문서
134
+
135
+ - [`principles/common.md`](../../principles/common.md), [`principles/vue.md`](../../principles/vue.md)
136
+ - [`fe-build/SKILL.md`](../fe-build/SKILL.md), [`fe-perf/SKILL.md`](../fe-perf/SKILL.md)
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: A11y + DX/디버깅 문서 인덱스
3
+ fetched: 2026-05-18
4
+ category: index
5
+ ---
6
+
7
+ # A11y + DX 디버깅 문서 인덱스
8
+
9
+ ## 파일 목록
10
+
11
+ | 파일 | 카테고리 | 내용 |
12
+ |------|----------|------|
13
+ | [wcag22-new-criteria.md](./wcag22-new-criteria.md) | wcag22 | WCAG 2.2 신규 SC 9개 — 레벨·요약·영향 컴포넌트·체크리스트 |
14
+ | [chrome-devtools-performance.md](./chrome-devtools-performance.md) | devtools-perf | Performance 패널 — 녹화 절차, Flame Chart, Long Task, Forced Layout 진단 |
15
+ | [chrome-devtools-memory.md](./chrome-devtools-memory.md) | devtools-memory | Memory 패널 — Heap Snapshot, Allocation Timeline, 4가지 누수 패턴 |
16
+ | [react-devtools-profiler.md](./react-devtools-profiler.md) | react-profiler | React Profiler — Flame Chart, Why did this render?, Highlight Updates |
17
+ | [lighthouse-audits.md](./lighthouse-audits.md) | lighthouse | 접근성·성능 감사 항목 전체 + 가중치 + 빠른 수정 코드 |
18
+
19
+ ## 주제별 빠른 참조
20
+
21
+ ### 접근성(A11y)
22
+ - WCAG 2.2 신규 요건 → `wcag22-new-criteria.md`
23
+ - Lighthouse 접근성 점수 향상 → `lighthouse-audits.md`
24
+
25
+ ### 성능 진단
26
+ - 렌더 병목 (Flame Chart, Long Task) → `chrome-devtools-performance.md`
27
+ - React 불필요 재렌더 → `react-devtools-profiler.md`
28
+ - Lighthouse 성능 지표 (LCP/TBT/CLS) → `lighthouse-audits.md`
29
+
30
+ ### 메모리 누수 진단
31
+ - Detached DOM / 이벤트 리스너 / 타이머 / 클로저 → `chrome-devtools-memory.md`
32
+
33
+ ## 출처
34
+
35
+ - https://www.w3.org/WAI/WCAG22/quickref/
36
+ - https://developer.chrome.com/docs/devtools/performance
37
+ - https://developer.chrome.com/docs/devtools/memory-problems
38
+ - https://react.dev/learn/react-developer-tools
39
+ - https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html
40
+ - https://developer.chrome.com/docs/lighthouse/accessibility/
41
+ - https://developer.chrome.com/docs/lighthouse/performance/