@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,201 @@
1
+ ---
2
+ title: LCP 최적화 심화 — 4가지 하위 구성 요소별 전략
3
+ source: https://web.dev/articles/optimize-lcp
4
+ fetched: 2026-05-18
5
+ category: images
6
+ ---
7
+
8
+ ## LCP 목표
9
+
10
+ **Good**: 75번째 백분위수 기준 ≤ 2.5초
11
+ **Needs Improvement**: 2.5 ~ 4.0초
12
+ **Poor**: > 4.0초
13
+
14
+ ---
15
+
16
+ ## LCP = 4가지 하위 구성 요소의 합
17
+
18
+ ```
19
+ LCP = TTFB + Resource Load Delay + Resource Load Duration + Element Render Delay
20
+ ```
21
+
22
+ | 구성 요소 | 이상적 비중 | 의미 |
23
+ |-----------|------------|------|
24
+ | **TTFB** | ~40% | 서버 최초 바이트 응답 시간 |
25
+ | **Resource Load Delay** | <10% | TTFB 종료 ~ LCP 리소스 로딩 시작 |
26
+ | **Resource Load Duration** | ~40% | 실제 리소스 전송 시간 |
27
+ | **Element Render Delay** | <10% | 리소스 완료 ~ 화면에 그려지는 시간 |
28
+
29
+ **핵심 원칙**: 어느 단계에도 "아무것도 로딩되지 않는" 구간을 만들지 마라.
30
+ 두 리소스(HTML + LCP 리소스)가 동시에 로딩되는 것이 이상적.
31
+
32
+ ---
33
+
34
+ ## 최적화 우선순위 1: Resource Load Delay 제거
35
+
36
+ ### 문제: LCP 이미지가 늦게 발견됨
37
+
38
+ ```html
39
+ <!-- BAD: CSS background (preload scanner 미탐지) -->
40
+ <div class="hero"></div>
41
+ ```
42
+ ```css
43
+ .hero { background-image: url('/hero.webp'); }
44
+ ```
45
+
46
+ ```html
47
+ <!-- GOOD: HTML에 직접 포함 (preload scanner 탐지) -->
48
+ <img src="/hero.webp" alt="히어로" fetchpriority="high">
49
+ ```
50
+
51
+ ### 문제: LCP 이미지가 JS에서만 참조됨
52
+
53
+ ```html
54
+ <!-- GOOD: preload link 추가 -->
55
+ <link rel="preload" fetchpriority="high" as="image"
56
+ href="/hero.webp" type="image/webp">
57
+ ```
58
+
59
+ ### 문제: LCP 이미지에 lazy loading 적용
60
+
61
+ ```html
62
+ <!-- BAD -->
63
+ <img src="/hero.webp" loading="lazy" alt="히어로">
64
+
65
+ <!-- GOOD -->
66
+ <img src="/hero.webp" loading="eager" fetchpriority="high" alt="히어로">
67
+ ```
68
+
69
+ ### fetchpriority 힌트
70
+
71
+ ```html
72
+ <!-- LCP 이미지에 높은 우선순위 -->
73
+ <img fetchpriority="high" src="/hero.webp" alt="히어로">
74
+
75
+ <!-- 폴드 아래 이미지에 낮은 우선순위 (기본 lazy와 병행 가능) -->
76
+ <img fetchpriority="low" src="/below-fold.jpg" alt="...">
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 최적화 우선순위 2: Element Render Delay 제거
82
+
83
+ ### 렌더링 블록 CSS
84
+
85
+ ```html
86
+ <!-- BAD: 큰 외부 CSS 파일이 렌더링 블록 -->
87
+ <link rel="stylesheet" href="/large-styles.css">
88
+
89
+ <!-- GOOD: 크리티컬 CSS 인라인화 -->
90
+ <style>
91
+ /* 첫 화면에 필요한 최소한의 CSS만 */
92
+ .hero { ... }
93
+ </style>
94
+ <!-- 나머지 CSS는 비동기 로드 -->
95
+ <link rel="stylesheet" href="/non-critical.css" media="print"
96
+ onload="this.media='all'">
97
+ ```
98
+
99
+ ### 렌더링 블록 JavaScript
100
+
101
+ ```html
102
+ <!-- BAD: head의 동기 스크립트 -->
103
+ <script src="/analytics.js"></script>
104
+
105
+ <!-- GOOD: defer 또는 async 사용 -->
106
+ <script src="/analytics.js" defer></script>
107
+ <!-- 또는 크리티컬하지 않으면 body 끝으로 이동 -->
108
+ ```
109
+
110
+ ### 서버사이드 렌더링 활용
111
+
112
+ ```javascript
113
+ // SSR: 이미지 src가 초기 HTML에 포함 → preload scanner 탐지 가능
114
+ // CSR: 이미지 src가 JS 실행 후 결정 → Resource Load Delay 증가
115
+ ```
116
+
117
+ ### Long Task 방지
118
+
119
+ ```javascript
120
+ // BAD: 메인 스레드를 오래 블록하는 작업
121
+ function heavyCalculation() { /* 200ms+ */ }
122
+
123
+ // GOOD: 분할 실행
124
+ async function chunkedCalculation(items) {
125
+ for (const item of items) {
126
+ processItem(item)
127
+ // 다음 frame 전에 제어권 반환
128
+ await scheduler.yield()
129
+ }
130
+ }
131
+ ```
132
+
133
+ ---
134
+
135
+ ## 최적화 우선순위 3: Resource Load Duration 단축
136
+
137
+ ```html
138
+ <!-- 현대 이미지 포맷 사용 -->
139
+ <picture>
140
+ <source srcset="/hero.avif" type="image/avif">
141
+ <source srcset="/hero.webp" type="image/webp">
142
+ <img src="/hero.jpg" alt="히어로">
143
+ </picture>
144
+
145
+ <!-- 반응형 srcset으로 적절한 크기 제공 -->
146
+ <img
147
+ srcset="/hero-480.webp 480w, /hero-800.webp 800w, /hero-1200.webp 1200w"
148
+ sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
149
+ src="/hero-1200.webp"
150
+ alt="히어로"
151
+ fetchpriority="high">
152
+ ```
153
+
154
+ ### CDN / Image CDN 활용
155
+
156
+ ```javascript
157
+ // Cloudinary, Imgix 등 Image CDN은:
158
+ // - 자동 WebP/AVIF 변환
159
+ // - 엣지에서 크기 최적화
160
+ // - 자동 압축
161
+ // URL 패턴 예시
162
+ const optimizedUrl = `https://res.cloudinary.com/demo/image/fetch/w_800,f_auto,q_auto/${originalUrl}`
163
+ ```
164
+
165
+ ### 캐싱 정책
166
+
167
+ ```http
168
+ Cache-Control: public, max-age=31536000, immutable
169
+ ```
170
+
171
+ ---
172
+
173
+ ## 최적화 우선순위 4: TTFB 단축
174
+
175
+ - 서버 사이드 렌더링 캐싱 (Vercel Edge Cache, CDN)
176
+ - 불필요한 리다이렉트 제거 (`http://` → `https://`, `www.` 제거)
177
+ - 서버 처리 시간 최소화
178
+ - Edge Function으로 지리적 지연 최소화
179
+
180
+ ---
181
+
182
+ ## 진단 도구
183
+
184
+ ```javascript
185
+ // LCP 하위 구성 요소 측정 (web-vitals 라이브러리)
186
+ import {onLCP} from 'web-vitals/attribution';
187
+
188
+ onLCP(metric => {
189
+ const {
190
+ timeToFirstByte, // TTFB
191
+ resourceLoadDelay, // Resource Load Delay
192
+ resourceLoadDuration, // Resource Load Duration
193
+ elementRenderDelay // Element Render Delay
194
+ } = metric.attribution;
195
+
196
+ console.log({timeToFirstByte, resourceLoadDelay, resourceLoadDuration, elementRenderDelay});
197
+ });
198
+ ```
199
+
200
+ **실제 사용자 데이터(CrUX)를 랩 데이터(Lighthouse)보다 우선** 확인할 것.
201
+ 75번째 백분위수가 2.5초 이하라면 하위 구성 요소 세분화 최적화보다 다른 우선순위를 고려.
@@ -0,0 +1,88 @@
1
+ ---
2
+ title: FE 성능 가이드 — 문서 인덱스
3
+ fetched: 2026-05-18
4
+ category: index
5
+ ---
6
+
7
+ # FE 성능 가이드 소스 문서 인덱스
8
+
9
+ 2024-2026 최신 웹 성능 트렌드 기반. Vercel + web.dev + Chrome Developers 공식 문서.
10
+
11
+ ---
12
+
13
+ ## 문서 목록
14
+
15
+ ### Core Web Vitals (web-vitals)
16
+
17
+ | 파일 | 주제 | 핵심 임계값 |
18
+ |------|------|------------|
19
+ | [01-core-web-vitals.md](./01-core-web-vitals.md) | LCP·INP·CLS 전체 개요 | LCP≤2.5s, INP≤200ms, CLS≤0.1 |
20
+ | [02-inp.md](./02-inp.md) | INP 심화 — FID 대체 신 지표 (2024) | ≤200ms Good, >500ms Poor |
21
+ | [03-lcp-cls.md](./03-lcp-cls.md) | LCP·CLS 최신 가이드라인 + 4 하위 구성 요소 | LCP≤2.5s, CLS≤0.1 |
22
+
23
+ ### 네비게이션 (navigation)
24
+
25
+ | 파일 | 주제 | 브라우저 지원 |
26
+ |------|------|--------------|
27
+ | [04-speculation-rules.md](./04-speculation-rules.md) | Speculation Rules API — 즉시 페이지 전환 | Chrome 109+, Edge 109+ |
28
+ | [05-view-transitions.md](./05-view-transitions.md) | View Transitions API — 페이지 전환 애니메이션 | Same-doc: Chrome 111+; Cross-doc: Chrome 126+ |
29
+
30
+ ### Next.js 캐싱·렌더링 (caching)
31
+
32
+ | 파일 | 주제 | 버전 |
33
+ |------|------|------|
34
+ | [06-nextjs-caching.md](./06-nextjs-caching.md) | App Router 캐싱 전략 (fetch, unstable_cache, use cache, React cache) | Next.js 16 |
35
+ | [07-server-components.md](./07-server-components.md) | Server vs Client Components 선택 기준 + 렌더링 흐름 | Next.js 16 |
36
+ | [08-ppr.md](./08-ppr.md) | PPR (Partial Prerendering) — 정적 shell + 동적 스트리밍 | Next.js 16 |
37
+
38
+ ### 이미지 최적화 (images)
39
+
40
+ | 파일 | 주제 | 핵심 |
41
+ |------|------|------|
42
+ | [09-nextjs-image.md](./09-nextjs-image.md) | next/image 컴포넌트 전체 props + 최적화 패턴 | preload, fill, sizes, placeholder |
43
+ | [10-optimize-lcp.md](./10-optimize-lcp.md) | LCP 4 하위 구성 요소별 최적화 심화 | fetchpriority, SSR, CDN |
44
+
45
+ ---
46
+
47
+ ## 카테고리별 빠른 참조
48
+
49
+ ### 임계값 요약
50
+
51
+ | 지표 | Good | Needs Improvement | Poor |
52
+ |------|------|-------------------|------|
53
+ | **LCP** | ≤ 2.5초 | 2.5 ~ 4.0초 | > 4.0초 |
54
+ | **INP** | ≤ 200ms | 201 ~ 500ms | > 500ms |
55
+ | **CLS** | ≤ 0.1 | 0.1 ~ 0.25 | > 0.25 |
56
+
57
+ 기준: 75번째 백분위수 (모바일 + 데스크톱)
58
+
59
+ ### 2024 주요 변경사항
60
+
61
+ 1. **INP 공식 Core Web Vital 승격** (2024년 3월) — FID 은퇴
62
+ 2. **Cross-Document View Transitions** 지원 (Chrome 126, 2024년)
63
+ 3. **Next.js 16 cacheComponents** — PPR + use cache + dynamicIO 통합 플래그
64
+
65
+ ### 도구 추천
66
+
67
+ | 목적 | 도구 |
68
+ |------|------|
69
+ | 실사용자 데이터 | Chrome UX Report, PageSpeed Insights (Field Data) |
70
+ | 랩 측정 | Lighthouse, WebPageTest, Chrome DevTools |
71
+ | JS 측정 | `web-vitals` 라이브러리 (`onLCP`, `onINP`, `onCLS`) |
72
+ | INP attribution | `web-vitals/attribution` (하위 구성 요소 세분화) |
73
+
74
+ ---
75
+
76
+ ## 출처
77
+
78
+ - https://web.dev/articles/vitals
79
+ - https://web.dev/articles/inp
80
+ - https://web.dev/articles/lcp
81
+ - https://web.dev/articles/cls
82
+ - https://web.dev/articles/optimize-lcp
83
+ - https://developer.chrome.com/docs/web-platform/prerender-pages
84
+ - https://developer.chrome.com/docs/web-platform/view-transitions
85
+ - https://nextjs.org/docs/app/building-your-application/caching
86
+ - https://nextjs.org/docs/app/building-your-application/rendering/server-components
87
+ - https://nextjs.org/docs/app/api-reference/next-config-js/ppr
88
+ - https://nextjs.org/docs/app/api-reference/components/image
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: React 19 FE 가이드 문서 인덱스
3
+ fetched: 2026-05-18
4
+ ---
5
+
6
+ # React 19 핵심 문서
7
+
8
+ | 파일 | 제목 | 카테고리 | 소스 |
9
+ |------|------|----------|------|
10
+ | [use-hook.md](./use-hook.md) | `use()` Hook | api | https://react.dev/reference/react/use |
11
+ | [use-action-state.md](./use-action-state.md) | `useActionState` | api | https://react.dev/reference/react/useActionState |
12
+ | [use-optimistic.md](./use-optimistic.md) | `useOptimistic` | api | https://react.dev/reference/react/useOptimistic |
13
+ | [use-form-status.md](./use-form-status.md) | `useFormStatus` / `<form action={}>` | api | https://react.dev/reference/react-dom/hooks/useFormStatus |
14
+ | [server-components.md](./server-components.md) | Server Components | rsc | https://react.dev/reference/rsc/server-components |
15
+ | [server-functions.md](./server-functions.md) | Server Functions (`'use server'` / `'use client'`) | rsc | https://react.dev/reference/rsc/server-functions |
16
+ | [suspense.md](./suspense.md) | Suspense & Streaming | suspense | https://react.dev/reference/react/Suspense |
17
+ | [react-compiler.md](./react-compiler.md) | React Compiler (자동 메모이제이션) | compiler | https://react.dev/learn/react-compiler |
18
+ | [no-effect-patterns.md](./no-effect-patterns.md) | Effect 회피 패턴 | patterns | https://react.dev/learn/you-might-not-need-an-effect |
19
+ | [keeping-components-pure.md](./keeping-components-pure.md) | 컴포넌트 순수성 유지 | patterns | https://react.dev/learn/keeping-components-pure |
20
+
21
+ ## 카테고리별 분류
22
+
23
+ ### API (React 19 신 Hook)
24
+ - `use()` — Promise/Context 읽기, 조건부 호출 가능
25
+ - `useActionState` — Action 결과로 state 관리, form 제출
26
+ - `useOptimistic` — 낙관적 UI 업데이트
27
+ - `useFormStatus` — 부모 form 제출 상태 읽기
28
+
29
+ ### RSC (React Server Components)
30
+ - Server Components — 서버 전용 렌더링, async/await 직접 사용
31
+ - Server Functions — `"use server"` 함수, form action에 직접 전달
32
+
33
+ ### Suspense & Streaming
34
+ - 중첩 Suspense로 순차 스트리밍, startTransition으로 fallback 방지
35
+
36
+ ### Compiler
37
+ - 자동 메모이제이션 — `useMemo`/`useCallback` 수동 작성 불필요
38
+
39
+ ### Patterns (리뷰 핵심)
40
+ - Effect 회피 패턴 — 불필요한 Effect 제거
41
+ - 컴포넌트 순수성 — 렌더 중 side effect 금지
@@ -0,0 +1,135 @@
1
+ ---
2
+ title: Keeping Components Pure (컴포넌트 순수성 유지)
3
+ source: https://react.dev/learn/keeping-components-pure
4
+ fetched: 2026-05-18
5
+ category: patterns
6
+ react_version: 19
7
+ ---
8
+
9
+ ## 개요
10
+
11
+ React는 모든 컴포넌트가 순수 함수라고 가정. 같은 입력에 항상 같은 JSX를 반환해야 함.
12
+
13
+ **순수 컴포넌트의 두 원칙:**
14
+ 1. **자기 일에만 집중** — 호출 전 존재하던 객체/변수를 변경하지 않음
15
+ 2. **같은 입력 → 같은 출력** — 항상 동일한 결과 반환
16
+
17
+ ---
18
+
19
+ ## 순수 컴포넌트 예시
20
+
21
+ ```js
22
+ function Recipe({ drinkers }) {
23
+ return (
24
+ <ol>
25
+ <li>Boil {drinkers} cups of water.</li>
26
+ <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
27
+ <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
28
+ </ol>
29
+ );
30
+ }
31
+ ```
32
+
33
+ `drinkers={2}`이면 항상 "2 cups of water" JSX 반환.
34
+
35
+ ---
36
+
37
+ ## 불순 컴포넌트 (Side Effect During Render)
38
+
39
+ ❌ 외부 변수 변경 — 예측 불가:
40
+ ```js
41
+ let guest = 0;
42
+
43
+ function Cup() {
44
+ guest = guest + 1; // 🚩 외부 변수 변경
45
+ return <h2>Tea cup for guest #{guest}</h2>;
46
+ }
47
+ ```
48
+
49
+ 같은 컴포넌트를 여러 번 호출하면 다른 결과 → 버그.
50
+
51
+ ✅ props로 데이터 전달:
52
+ ```js
53
+ function Cup({ guest }) {
54
+ return <h2>Tea cup for guest #{guest}</h2>;
55
+ }
56
+
57
+ export default function TeaSet() {
58
+ return (
59
+ <>
60
+ <Cup guest={1} />
61
+ <Cup guest={2} />
62
+ <Cup guest={3} />
63
+ </>
64
+ );
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Local Mutation은 안전
71
+
72
+ 렌더링 중 **직접 생성한** 변수의 변경은 안전 ("local mutation"):
73
+
74
+ ```js
75
+ export default function TeaGathering() {
76
+ const cups = []; // 이 렌더에서 생성
77
+ for (let i = 1; i <= 12; i++) {
78
+ cups.push(<Cup key={i} guest={i} />); // ✅ local mutation, 순수성 유지
79
+ }
80
+ return cups;
81
+ }
82
+ ```
83
+
84
+ 외부 코드가 이 변수를 알 수 없으므로 순수성 유지.
85
+
86
+ ---
87
+
88
+ ## Side Effect가 허용되는 위치
89
+
90
+ **1. 이벤트 핸들러 (권장):**
91
+ ```js
92
+ function Button() {
93
+ const handleClick = () => {
94
+ updateDatabase(); // ✅ 이벤트 핸들러 내부
95
+ };
96
+ return <button onClick={handleClick}>Click me</button>;
97
+ }
98
+ ```
99
+
100
+ **2. useEffect (최후 수단):**
101
+ ```js
102
+ useEffect(() => {
103
+ document.title = "Updated"; // ✅ 렌더링 후 실행
104
+ }, []);
105
+ ```
106
+
107
+ 렌더 함수 자체에서는 side effect 금지.
108
+
109
+ ---
110
+
111
+ ## React StrictMode
112
+
113
+ 개발 모드에서 컴포넌트 함수를 **두 번** 호출하여 불순 컴포넌트 감지:
114
+ - 순수 함수: 두 번 호출해도 같은 결과
115
+ - 불순 함수: 버그가 드러남
116
+
117
+ ---
118
+
119
+ ## 순수성이 가능하게 하는 것
120
+
121
+ - **서버 사이드 렌더링**: 같은 입력 → 같은 결과 보장
122
+ - **성능 최적화**: 입력이 변경되지 않으면 캐시된 결과 재사용
123
+ - **렌더 인터럽트 안전**: 데이터 변경 시 새 렌더 재시작 가능
124
+
125
+ ---
126
+
127
+ ## 핵심 규칙 요약
128
+
129
+ | 규칙 | 설명 |
130
+ |------|------|
131
+ | 외부 변수 변경 금지 | 렌더 전 존재하던 변수 수정 불가 |
132
+ | props/state/context는 읽기 전용 | 직접 수정 불가 |
133
+ | local mutation은 허용 | 렌더 중 새로 생성한 변수만 |
134
+ | side effect 위치 | 이벤트 핸들러 또는 `useEffect` |
135
+ | 독립적 렌더 | 컴포넌트는 렌더 중 다른 컴포넌트와 협력하지 않음 |
@@ -0,0 +1,183 @@
1
+ ---
2
+ title: You Might Not Need an Effect (Effect 회피 패턴)
3
+ source: https://react.dev/learn/you-might-not-need-an-effect
4
+ fetched: 2026-05-18
5
+ category: patterns
6
+ react_version: 19
7
+ ---
8
+
9
+ ## 개요
10
+
11
+ Effect는 React 패러다임의 **탈출구** — 외부 시스템과 동기화할 때만 사용. 많은 경우 더 간단한 대안이 있음.
12
+
13
+ **Effect가 필요 없는 두 가지 주요 케이스:**
14
+ 1. 렌더링을 위한 데이터 변환
15
+ 2. 사용자 이벤트 처리
16
+
17
+ ---
18
+
19
+ ## 패턴별 가이드
20
+
21
+ ### 1. 렌더링용 데이터 변환 → 렌더 중 계산
22
+
23
+ ❌ 피할 것:
24
+ ```js
25
+ function Form() {
26
+ const [firstName, setFirstName] = useState('Taylor');
27
+ const [lastName, setLastName] = useState('Swift');
28
+ const [fullName, setFullName] = useState('');
29
+
30
+ useEffect(() => {
31
+ setFullName(firstName + ' ' + lastName);
32
+ }, [firstName, lastName]);
33
+ }
34
+ ```
35
+
36
+ ✅ 렌더 중 직접 계산:
37
+ ```js
38
+ function Form() {
39
+ const [firstName, setFirstName] = useState('Taylor');
40
+ const [lastName, setLastName] = useState('Swift');
41
+ const fullName = firstName + ' ' + lastName; // Effect 불필요
42
+ }
43
+ ```
44
+
45
+ ---
46
+
47
+ ### 2. 비용이 큰 계산 → useMemo
48
+
49
+ ```js
50
+ function TodoList({ todos, filter }) {
51
+ const visibleTodos = useMemo(
52
+ () => getFilteredTodos(todos, filter),
53
+ [todos, filter]
54
+ );
55
+ }
56
+ ```
57
+
58
+ ---
59
+
60
+ ### 3. prop 변경 시 state 리셋 → key 사용
61
+
62
+ ```js
63
+ export default function ProfilePage({ userId }) {
64
+ return (
65
+ <Profile
66
+ userId={userId}
67
+ key={userId} // userId가 변경되면 컴포넌트 전체 리셋
68
+ />
69
+ );
70
+ }
71
+ ```
72
+
73
+ ---
74
+
75
+ ### 4. prop 기반 state 조정 → 렌더 중 계산
76
+
77
+ ```js
78
+ function List({ items }) {
79
+ const [selectedId, setSelectedId] = useState(null);
80
+ // Effect 없이 렌더 중 계산
81
+ const selection = items.find(item => item.id === selectedId) ?? null;
82
+ }
83
+ ```
84
+
85
+ ---
86
+
87
+ ### 5. 이벤트 핸들러 간 로직 공유 → 함수 추출
88
+
89
+ ❌ 피할 것:
90
+ ```js
91
+ useEffect(() => {
92
+ if (product.isInCart) {
93
+ showNotification(`Added ${product.name} to cart!`);
94
+ }
95
+ }, [product]);
96
+ ```
97
+
98
+ ✅ 이벤트 핸들러에서 직접 처리:
99
+ ```js
100
+ function ProductPage({ product, addToCart }) {
101
+ function buyProduct() {
102
+ addToCart(product);
103
+ showNotification(`Added ${product.name} to cart!`);
104
+ }
105
+
106
+ function handleBuyClick() {
107
+ buyProduct();
108
+ }
109
+
110
+ function handleCheckoutClick() {
111
+ buyProduct();
112
+ navigateTo('/checkout');
113
+ }
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ### 6. state 업데이트 체인 → 이벤트 핸들러에서 한 번에
120
+
121
+ ❌ Effect 체인 (N번 리렌더):
122
+ ```js
123
+ useEffect(() => {
124
+ if (card.gold) setGoldCardCount(c => c + 1);
125
+ }, [card]);
126
+
127
+ useEffect(() => {
128
+ if (goldCardCount > 3) setRound(r => r + 1);
129
+ }, [goldCardCount]);
130
+ ```
131
+
132
+ ✅ 이벤트 핸들러에서 한 번에:
133
+ ```js
134
+ function handlePlaceCard(nextCard) {
135
+ setCard(nextCard);
136
+ if (nextCard.gold) {
137
+ if (goldCardCount < 3) {
138
+ setGoldCardCount(goldCardCount + 1);
139
+ } else {
140
+ setGoldCardCount(0);
141
+ setRound(round + 1);
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ ---
148
+
149
+ ### 7. 부모에게 변경 알리기 → 같은 이벤트에서 처리
150
+
151
+ ```js
152
+ function Toggle({ onChange }) {
153
+ const [isOn, setIsOn] = useState(false);
154
+
155
+ function updateToggle(nextIsOn) {
156
+ setIsOn(nextIsOn);
157
+ onChange(nextIsOn); // Effect 아닌 이벤트 핸들러에서 호출
158
+ }
159
+ }
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Effect가 실제로 필요한 경우
165
+
166
+ - **외부 시스템 동기화** (jQuery 위젯, 브라우저 API)
167
+ - **데이터 페칭** (race condition 처리를 위한 cleanup 필요)
168
+ - **외부 스토어 구독** → `useSyncExternalStore` 사용
169
+
170
+ ---
171
+
172
+ ## 요약 표
173
+
174
+ | 패턴 | ❌ Effect 사용 | ✅ 대안 |
175
+ |------|--------------|---------|
176
+ | 데이터 변환 | state + Effect | 렌더 중 계산 |
177
+ | 비싼 계산 | state + Effect | `useMemo` |
178
+ | prop 변경 시 리셋 | Effect에서 `setState` | `key` prop |
179
+ | 사용자 이벤트 처리 | Effect | 이벤트 핸들러 |
180
+ | 부모에게 변경 알림 | Effect | 이벤트 핸들러 |
181
+ | 외부 동기화 | (적절한 경우) | Effect 또는 `useSyncExternalStore` |
182
+
183
+ **황금 규칙:** 컴포넌트가 _표시_되어서 실행해야 하면 Effect, 특정 _인터랙션_ 때문이면 이벤트 핸들러.