@kood/claude-code 0.4.1 → 0.5.0
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.
- package/dist/index.js +53 -7
- package/package.json +2 -1
- package/templates/.claude/PARALLEL_AGENTS.md +737 -0
- package/templates/.claude/agents/analyst.md +416 -0
- package/templates/.claude/agents/architect.md +569 -0
- package/templates/.claude/agents/code-reviewer.md +132 -133
- package/templates/.claude/agents/dependency-manager.md +93 -94
- package/templates/.claude/agents/deployment-validator.md +64 -65
- package/templates/.claude/agents/designer.md +655 -0
- package/templates/.claude/agents/document-writer.md +500 -0
- package/templates/.claude/agents/explore.md +499 -0
- package/templates/.claude/agents/git-operator.md +74 -75
- package/templates/.claude/agents/implementation-executor.md +138 -109
- package/templates/.claude/agents/ko-to-en-translator.md +18 -22
- package/templates/.claude/agents/lint-fixer.md +250 -93
- package/templates/.claude/agents/planner.md +356 -0
- package/templates/.claude/agents/refactor-advisor.md +135 -136
- package/templates/.claude/commands/bug-fix.md +296 -207
- package/templates/.claude/commands/git-all.md +199 -46
- package/templates/.claude/commands/git-session.md +113 -57
- package/templates/.claude/commands/lint-fix.md +219 -153
- package/templates/.claude/commands/lint-init.md +113 -76
- package/templates/.claude/commands/pre-deploy.md +190 -124
- package/templates/.claude/commands/refactor.md +407 -162
- package/templates/.claude/commands/version-update.md +138 -37
- package/templates/.claude/instructions/context-engineering/ANTHROPIC_CONTEXT_ENGINEERING.md +178 -0
- package/templates/.claude/instructions/context-engineering/references/claude-4x.md +215 -0
- package/templates/.claude/instructions/context-engineering/references/core-principles.md +137 -0
- package/templates/.claude/instructions/context-engineering/references/examples.md +351 -0
- package/templates/.claude/instructions/context-engineering/references/techniques.md +162 -0
- package/templates/.claude/instructions/parallel-agent-execution.md +874 -0
- package/templates/.claude/skills/docs-creator/AGENTS.md +238 -0
- package/templates/.claude/{commands/docs-creator.md → skills/docs-creator/SKILL.md} +61 -75
- package/templates/.claude/skills/docs-refactor/AGENTS.md +270 -0
- package/templates/.claude/{commands/docs-refactor.md → skills/docs-refactor/SKILL.md} +30 -44
- package/templates/.claude/skills/execute/SKILL.md +451 -0
- package/templates/.claude/skills/figma-to-code/AGENTS.md +287 -0
- package/templates/.claude/skills/figma-to-code/SKILL.md +225 -225
- package/templates/.claude/skills/figma-to-code/references/design-tokens.md +75 -73
- package/templates/.claude/skills/figma-to-code/references/figma-mcp-tools.md +73 -73
- package/templates/.claude/skills/figma-to-code/references/layout-mapping.md +104 -104
- package/templates/.claude/skills/figma-to-code/references/responsive-design.md +99 -99
- package/templates/.claude/skills/figma-to-code/references/verification.md +91 -91
- package/templates/.claude/skills/global-uiux-design/AGENTS.md +317 -0
- package/templates/.claude/skills/global-uiux-design/SKILL.md +738 -0
- package/templates/.claude/skills/global-uiux-design/references/accessibility.md +401 -0
- package/templates/.claude/skills/global-uiux-design/references/color-system.md +275 -0
- package/templates/.claude/skills/global-uiux-design/references/design-philosophy.md +206 -0
- package/templates/.claude/skills/global-uiux-design/references/design-systems.md +446 -0
- package/templates/.claude/skills/korea-uiux-design/AGENTS.md +307 -0
- package/templates/.claude/skills/korea-uiux-design/SKILL.md +170 -0
- package/templates/.claude/skills/nextjs-react-best-practices/AGENTS.md +95 -116
- package/templates/.claude/skills/nextjs-react-best-practices/SKILL.md +134 -152
- package/templates/.claude/skills/nextjs-react-best-practices/rules/advanced-event-handler-refs.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/advanced-use-latest.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-api-routes.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-defer-await.md +22 -22
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-dependencies.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-parallel.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/async-suspense-boundaries.md +21 -21
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-barrel-imports.md +18 -18
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-conditional.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-defer-third-party.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-dynamic-imports.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-preload.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/client-event-listeners.md +9 -9
- package/templates/.claude/skills/nextjs-react-best-practices/rules/client-swr-dedup.md +7 -7
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-batch-dom-css.md +13 -13
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-function-results.md +14 -14
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-property-access.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-storage.md +10 -10
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-combine-iterations.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-early-exit.md +7 -7
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-hoist-regexp.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-index-maps.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-length-check-first.md +14 -14
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-min-max-loop.md +16 -16
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-set-map-lookups.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/js-tosorted-immutable.md +17 -17
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-activity.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-animate-svg-wrapper.md +11 -11
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-conditional-render.md +8 -8
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-content-visibility.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-hoist-jsx.md +6 -6
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-hydration-no-flicker.md +14 -14
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-svg-precision.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-defer-reads.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-dependencies.md +7 -7
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-derived-state.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-functional-setstate.md +34 -34
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-lazy-state-init.md +15 -15
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-memo.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-transitions.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-after-nonblocking.md +24 -24
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-cache-lru.md +10 -10
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-cache-react.md +4 -4
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-parallel-fetching.md +5 -5
- package/templates/.claude/skills/nextjs-react-best-practices/rules/server-serialization.md +6 -6
- package/templates/.claude/skills/plan/SKILL.md +594 -0
- package/templates/.claude/skills/prd/SKILL.md +496 -0
- package/templates/.claude/skills/ralph/AGENTS.md +393 -0
- package/templates/.claude/skills/ralph/SKILL.md +1035 -0
- package/templates/.claude/skills/tanstack-start-react-best-practices/AGENTS.md +100 -121
- package/templates/.claude/skills/tanstack-start-react-best-practices/SKILL.md +139 -157
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-defer-await.md +22 -22
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-dependencies.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-loader.md +7 -7
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/async-parallel.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-barrel-imports.md +18 -18
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-conditional.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-defer-third-party.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-lazy-routes.md +12 -12
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/bundle-preload.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-event-listeners.md +9 -9
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/client-tanstack-query.md +12 -12
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-batch-dom-css.md +13 -13
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-cache-function-results.md +14 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-cache-property-access.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-cache-storage.md +10 -10
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-combine-iterations.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-early-exit.md +7 -7
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-hoist-regexp.md +6 -6
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-index-maps.md +6 -6
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-length-check-first.md +14 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-min-max-loop.md +16 -16
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-set-map-lookups.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/js-tosorted-immutable.md +17 -17
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-animate-svg-wrapper.md +11 -11
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-conditional-render.md +8 -8
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-content-visibility.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-hoist-jsx.md +6 -6
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rendering-svg-precision.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-defer-reads.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-dependencies.md +7 -7
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-derived-state.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-functional-setstate.md +34 -34
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-lazy-state-init.md +15 -15
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-memo.md +5 -5
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/rerender-transitions.md +4 -4
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-cache-lru.md +12 -12
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-deferred-data.md +14 -14
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-parallel-fetching.md +9 -9
- package/templates/.claude/skills/tanstack-start-react-best-practices/rules/server-serialization.md +6 -6
- package/templates/.claude/commands/agent-creator.md +0 -370
- package/templates/.claude/commands/command-creator.md +0 -524
- package/templates/.claude/commands/execute.md +0 -469
- package/templates/.claude/commands/git.md +0 -98
- package/templates/.claude/commands/plan.md +0 -526
- package/templates/.claude/commands/prd.md +0 -629
|
@@ -1,44 +1,23 @@
|
|
|
1
|
-
# Next.js React
|
|
1
|
+
# Next.js React 베스트 프랙티스
|
|
2
2
|
|
|
3
3
|
**Version 1.0.0**
|
|
4
4
|
Vercel Engineering
|
|
5
5
|
January 2026
|
|
6
6
|
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
> generating, or refactoring React and Next.js codebases. Humans
|
|
10
|
-
> may also find it useful, but guidance here is optimized for automation
|
|
11
|
-
> and consistency by AI-assisted workflows.
|
|
7
|
+
> **참고:**
|
|
8
|
+
> 이 문서는 주로 에이전트와 LLM이 React 및 Next.js 코드베이스를 유지보수, 생성, 리팩토링할 때 따르기 위한 것입니다. 사람도 유용하게 사용할 수 있지만, AI 지원 워크플로의 자동화 및 일관성을 위해 최적화되어 있습니다.
|
|
12
9
|
|
|
13
10
|
---
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
## 요약
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
**CRITICAL: Always communicate with the user in Korean (한국어).**
|
|
20
|
-
|
|
21
|
-
This applies to:
|
|
22
|
-
- Questions and clarifications
|
|
23
|
-
- Progress updates and summaries
|
|
24
|
-
- Explaining decisions and trade-offs
|
|
25
|
-
- Reporting errors or blockers
|
|
26
|
-
|
|
27
|
-
Internal processing and code comments remain in English. Only user-facing messages must be in Korean.
|
|
28
|
-
|
|
29
|
-
</communication>
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Abstract
|
|
34
|
-
|
|
35
|
-
Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.
|
|
14
|
+
AI 에이전트와 LLM을 위한 React 및 Next.js 애플리케이션 종합 성능 최적화 가이드. 8개 카테고리에 걸쳐 40개 이상의 규칙을 포함하며, 영향도별로 우선순위를 매겼습니다 (critical: waterfall 제거, 번들 크기 감소 → incremental: 고급 패턴). 각 규칙은 자동 리팩토링 및 코드 생성을 위한 상세한 설명, 올바른 구현 대 잘못된 구현을 비교하는 실제 예시, 구체적인 영향 지표를 포함합니다.
|
|
36
15
|
|
|
37
16
|
---
|
|
38
17
|
|
|
39
18
|
<instructions>
|
|
40
19
|
|
|
41
|
-
##
|
|
20
|
+
## 문서 사용 지침
|
|
42
21
|
|
|
43
22
|
@rules/async-defer-await.md
|
|
44
23
|
@rules/async-parallel.md
|
|
@@ -92,18 +71,18 @@ Comprehensive performance optimization guide for React and Next.js applications,
|
|
|
92
71
|
|
|
93
72
|
<categories>
|
|
94
73
|
|
|
95
|
-
##
|
|
74
|
+
## 카테고리별 우선순위
|
|
96
75
|
|
|
97
|
-
|
|
|
98
|
-
|
|
99
|
-
| 1 |
|
|
100
|
-
| 2 |
|
|
101
|
-
| 3 |
|
|
102
|
-
| 4 |
|
|
103
|
-
| 5 | Re-render
|
|
104
|
-
| 6 |
|
|
105
|
-
| 7 | JavaScript
|
|
106
|
-
| 8 |
|
|
76
|
+
| 우선순위 | 카테고리 | 영향도 | 설명 |
|
|
77
|
+
|---------|---------|--------|------|
|
|
78
|
+
| 1 | Waterfall 제거 | **CRITICAL** | 순차 await를 병렬로 전환. 가장 큰 성능 향상 제공 |
|
|
79
|
+
| 2 | 번들 크기 최적화 | **CRITICAL** | TTI와 LCP 개선. 초기 로딩 속도 향상 |
|
|
80
|
+
| 3 | 서버 사이드 성능 | HIGH | 서버 사이드 waterfall 제거, 응답 시간 단축 |
|
|
81
|
+
| 4 | 클라이언트 데이터 페칭 | MEDIUM-HIGH | 자동 중복 제거, 효율적 데이터 페칭 |
|
|
82
|
+
| 5 | Re-render 최적화 | MEDIUM | 불필요한 re-render 최소화, UI 반응성 향상 |
|
|
83
|
+
| 6 | 렌더링 성능 | MEDIUM | 브라우저 렌더링 작업 최적화 |
|
|
84
|
+
| 7 | JavaScript 성능 | LOW-MEDIUM | Hot path 마이크로 최적화 |
|
|
85
|
+
| 8 | 고급 패턴 | LOW | 특정 케이스용 고급 구현 패턴 |
|
|
107
86
|
|
|
108
87
|
</categories>
|
|
109
88
|
|
|
@@ -111,19 +90,19 @@ Comprehensive performance optimization guide for React and Next.js applications,
|
|
|
111
90
|
|
|
112
91
|
<critical_patterns>
|
|
113
92
|
|
|
114
|
-
## 1.
|
|
93
|
+
## 1. Waterfall 제거 (CRITICAL)
|
|
115
94
|
|
|
116
|
-
|
|
95
|
+
Waterfall은 가장 큰 성능 저해 요소입니다. 순차 await는 전체 네트워크 지연을 추가합니다.
|
|
117
96
|
|
|
118
|
-
###
|
|
97
|
+
### 병렬 실행
|
|
119
98
|
|
|
120
99
|
```typescript
|
|
121
|
-
// ❌
|
|
100
|
+
// ❌ 순차 실행 (3번 왕복)
|
|
122
101
|
const user = await fetchUser()
|
|
123
102
|
const posts = await fetchPosts()
|
|
124
103
|
const comments = await fetchComments()
|
|
125
104
|
|
|
126
|
-
// ✅
|
|
105
|
+
// ✅ 병렬 실행 (1번 왕복)
|
|
127
106
|
const [user, posts, comments] = await Promise.all([
|
|
128
107
|
fetchUser(),
|
|
129
108
|
fetchPosts(),
|
|
@@ -131,17 +110,17 @@ const [user, posts, comments] = await Promise.all([
|
|
|
131
110
|
])
|
|
132
111
|
```
|
|
133
112
|
|
|
134
|
-
###
|
|
113
|
+
### 의존성 기반 병렬화
|
|
135
114
|
|
|
136
115
|
```typescript
|
|
137
|
-
// ❌ profile
|
|
116
|
+
// ❌ profile이 config을 불필요하게 대기
|
|
138
117
|
const [user, config] = await Promise.all([
|
|
139
118
|
fetchUser(),
|
|
140
119
|
fetchConfig()
|
|
141
120
|
])
|
|
142
121
|
const profile = await fetchProfile(user.id)
|
|
143
122
|
|
|
144
|
-
// ✅
|
|
123
|
+
// ✅ better-all로 최대 병렬화
|
|
145
124
|
import { all } from 'better-all'
|
|
146
125
|
|
|
147
126
|
const { user, config, profile } = await all({
|
|
@@ -153,10 +132,10 @@ const { user, config, profile } = await all({
|
|
|
153
132
|
})
|
|
154
133
|
```
|
|
155
134
|
|
|
156
|
-
### Suspense
|
|
135
|
+
### Suspense 경계
|
|
157
136
|
|
|
158
137
|
```tsx
|
|
159
|
-
// ❌
|
|
138
|
+
// ❌ 전체 레이아웃이 데이터 대기
|
|
160
139
|
async function Page() {
|
|
161
140
|
const data = await fetchData()
|
|
162
141
|
return (
|
|
@@ -169,7 +148,7 @@ async function Page() {
|
|
|
169
148
|
)
|
|
170
149
|
}
|
|
171
150
|
|
|
172
|
-
// ✅
|
|
151
|
+
// ✅ 래퍼는 즉시 표시, 데이터는 스트리밍
|
|
173
152
|
function Page() {
|
|
174
153
|
return (
|
|
175
154
|
<div>
|
|
@@ -195,20 +174,20 @@ async function DataDisplay() {
|
|
|
195
174
|
|
|
196
175
|
<bundle_optimization>
|
|
197
176
|
|
|
198
|
-
## 2.
|
|
177
|
+
## 2. 번들 크기 최적화 (CRITICAL)
|
|
199
178
|
|
|
200
|
-
###
|
|
179
|
+
### Barrel Import 회피
|
|
201
180
|
|
|
202
181
|
```tsx
|
|
203
|
-
// ❌
|
|
182
|
+
// ❌ 전체 라이브러리 import (1583개 모듈, ~2.8초)
|
|
204
183
|
import { Check, X, Menu } from 'lucide-react'
|
|
205
184
|
|
|
206
|
-
// ✅
|
|
185
|
+
// ✅ 직접 import (3개 모듈만)
|
|
207
186
|
import Check from 'lucide-react/dist/esm/icons/check'
|
|
208
187
|
import X from 'lucide-react/dist/esm/icons/x'
|
|
209
188
|
import Menu from 'lucide-react/dist/esm/icons/menu'
|
|
210
189
|
|
|
211
|
-
// ✅ Next.js 13.5+
|
|
190
|
+
// ✅ Next.js 13.5+ 자동 최적화
|
|
212
191
|
// next.config.js
|
|
213
192
|
module.exports = {
|
|
214
193
|
experimental: {
|
|
@@ -217,19 +196,19 @@ module.exports = {
|
|
|
217
196
|
}
|
|
218
197
|
```
|
|
219
198
|
|
|
220
|
-
|
|
199
|
+
영향 받는 라이브러리: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`
|
|
221
200
|
|
|
222
|
-
### Dynamic
|
|
201
|
+
### Dynamic Import
|
|
223
202
|
|
|
224
203
|
```tsx
|
|
225
|
-
// ❌ Monaco
|
|
204
|
+
// ❌ Monaco가 메인 청크에 포함 (~300KB)
|
|
226
205
|
import { MonacoEditor } from './monaco-editor'
|
|
227
206
|
|
|
228
207
|
function CodePanel({ code }: { code: string }) {
|
|
229
208
|
return <MonacoEditor value={code} />
|
|
230
209
|
}
|
|
231
210
|
|
|
232
|
-
// ✅ Monaco
|
|
211
|
+
// ✅ Monaco는 필요시 로드
|
|
233
212
|
import dynamic from 'next/dynamic'
|
|
234
213
|
|
|
235
214
|
const MonacoEditor = dynamic(
|
|
@@ -248,9 +227,9 @@ function CodePanel({ code }: { code: string }) {
|
|
|
248
227
|
|
|
249
228
|
<server_performance>
|
|
250
229
|
|
|
251
|
-
## 3.
|
|
230
|
+
## 3. 서버 사이드 성능 (HIGH)
|
|
252
231
|
|
|
253
|
-
### React.cache() -
|
|
232
|
+
### React.cache() - 요청별 중복 제거
|
|
254
233
|
|
|
255
234
|
```typescript
|
|
256
235
|
import { cache } from 'react'
|
|
@@ -261,17 +240,17 @@ export const getCurrentUser = cache(async () => {
|
|
|
261
240
|
return await db.user.findUnique({ where: { id: session.user.id } })
|
|
262
241
|
})
|
|
263
242
|
|
|
264
|
-
//
|
|
243
|
+
// 단일 요청 내에서 여러 번 호출해도 쿼리는 1번만 실행
|
|
265
244
|
```
|
|
266
245
|
|
|
267
|
-
### LRU
|
|
246
|
+
### LRU 캐시 - 요청 간 캐싱
|
|
268
247
|
|
|
269
248
|
```typescript
|
|
270
249
|
import { LRUCache } from 'lru-cache'
|
|
271
250
|
|
|
272
251
|
const cache = new LRUCache<string, any>({
|
|
273
252
|
max: 1000,
|
|
274
|
-
ttl: 5 * 60 * 1000 // 5
|
|
253
|
+
ttl: 5 * 60 * 1000 // 5분
|
|
275
254
|
})
|
|
276
255
|
|
|
277
256
|
export async function getUser(id: string) {
|
|
@@ -283,25 +262,25 @@ export async function getUser(id: string) {
|
|
|
283
262
|
return user
|
|
284
263
|
}
|
|
285
264
|
|
|
286
|
-
//
|
|
287
|
-
//
|
|
265
|
+
// 요청 1: DB 쿼리, 결과 캐싱
|
|
266
|
+
// 요청 2: 캐시 히트, DB 쿼리 없음
|
|
288
267
|
```
|
|
289
268
|
|
|
290
|
-
###
|
|
269
|
+
### RSC 경계에서 직렬화 최소화
|
|
291
270
|
|
|
292
271
|
```tsx
|
|
293
|
-
// ❌
|
|
272
|
+
// ❌ 50개 필드 모두 직렬화
|
|
294
273
|
async function Page() {
|
|
295
|
-
const user = await fetchUser() // 50
|
|
274
|
+
const user = await fetchUser() // 50개 필드
|
|
296
275
|
return <Profile user={user} />
|
|
297
276
|
}
|
|
298
277
|
|
|
299
278
|
'use client'
|
|
300
279
|
function Profile({ user }: { user: User }) {
|
|
301
|
-
return <div>{user.name}</div> //
|
|
280
|
+
return <div>{user.name}</div> // 1개 필드만 사용
|
|
302
281
|
}
|
|
303
282
|
|
|
304
|
-
// ✅
|
|
283
|
+
// ✅ 1개 필드만 직렬화
|
|
305
284
|
async function Page() {
|
|
306
285
|
const user = await fetchUser()
|
|
307
286
|
return <Profile name={user.name} />
|
|
@@ -313,7 +292,7 @@ function Profile({ name }: { name: string }) {
|
|
|
313
292
|
}
|
|
314
293
|
```
|
|
315
294
|
|
|
316
|
-
### after()
|
|
295
|
+
### after()로 비차단 작업
|
|
317
296
|
|
|
318
297
|
```tsx
|
|
319
298
|
import { after } from 'next/server'
|
|
@@ -321,7 +300,7 @@ import { after } from 'next/server'
|
|
|
321
300
|
export async function POST(request: Request) {
|
|
322
301
|
await updateDatabase(request)
|
|
323
302
|
|
|
324
|
-
//
|
|
303
|
+
// 응답 전송 후 로깅
|
|
325
304
|
after(async () => {
|
|
326
305
|
const userAgent = (await headers()).get('user-agent') || 'unknown'
|
|
327
306
|
logUserAction({ userAgent })
|
|
@@ -340,14 +319,14 @@ export async function POST(request: Request) {
|
|
|
340
319
|
|
|
341
320
|
<client_data_fetching>
|
|
342
321
|
|
|
343
|
-
## 4.
|
|
322
|
+
## 4. 클라이언트 데이터 페칭 (MEDIUM-HIGH)
|
|
344
323
|
|
|
345
|
-
### SWR
|
|
324
|
+
### SWR로 자동 중복 제거
|
|
346
325
|
|
|
347
326
|
```tsx
|
|
348
327
|
import useSWR from 'swr'
|
|
349
328
|
|
|
350
|
-
// ❌
|
|
329
|
+
// ❌ 중복 제거 없음, 각 인스턴스가 fetch
|
|
351
330
|
function UserList() {
|
|
352
331
|
const [users, setUsers] = useState([])
|
|
353
332
|
useEffect(() => {
|
|
@@ -357,7 +336,7 @@ function UserList() {
|
|
|
357
336
|
}, [])
|
|
358
337
|
}
|
|
359
338
|
|
|
360
|
-
// ✅
|
|
339
|
+
// ✅ 여러 인스턴스가 하나의 요청 공유
|
|
361
340
|
function UserList() {
|
|
362
341
|
const { data: users } = useSWR('/api/users', fetcher)
|
|
363
342
|
}
|
|
@@ -369,57 +348,57 @@ function UserList() {
|
|
|
369
348
|
|
|
370
349
|
<rerender_optimization>
|
|
371
350
|
|
|
372
|
-
## 5. Re-render
|
|
351
|
+
## 5. Re-render 최적화 (MEDIUM)
|
|
373
352
|
|
|
374
|
-
###
|
|
353
|
+
### 함수형 setState
|
|
375
354
|
|
|
376
355
|
```tsx
|
|
377
|
-
// ❌ items
|
|
356
|
+
// ❌ items가 의존성, 매번 재생성
|
|
378
357
|
function TodoList() {
|
|
379
358
|
const [items, setItems] = useState(initialItems)
|
|
380
359
|
|
|
381
360
|
const addItems = useCallback((newItems: Item[]) => {
|
|
382
361
|
setItems([...items, ...newItems])
|
|
383
|
-
}, [items]) //
|
|
362
|
+
}, [items]) // items 변경마다 재생성
|
|
384
363
|
}
|
|
385
364
|
|
|
386
|
-
// ✅
|
|
365
|
+
// ✅ 안정적 콜백, 재생성 없음
|
|
387
366
|
function TodoList() {
|
|
388
367
|
const [items, setItems] = useState(initialItems)
|
|
389
368
|
|
|
390
369
|
const addItems = useCallback((newItems: Item[]) => {
|
|
391
370
|
setItems(curr => [...curr, ...newItems])
|
|
392
|
-
}, []) //
|
|
371
|
+
}, []) // 의존성 없음, 항상 최신 상태 사용
|
|
393
372
|
}
|
|
394
373
|
```
|
|
395
374
|
|
|
396
|
-
### Lazy
|
|
375
|
+
### Lazy 상태 초기화
|
|
397
376
|
|
|
398
377
|
```tsx
|
|
399
|
-
// ❌
|
|
378
|
+
// ❌ 매 렌더마다 실행
|
|
400
379
|
function FilteredList({ items }: { items: Item[] }) {
|
|
401
380
|
const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
|
|
402
381
|
const [query, setQuery] = useState('')
|
|
403
382
|
}
|
|
404
383
|
|
|
405
|
-
// ✅
|
|
384
|
+
// ✅ 초기 렌더 시에만 실행
|
|
406
385
|
function FilteredList({ items }: { items: Item[] }) {
|
|
407
386
|
const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
|
|
408
387
|
const [query, setQuery] = useState('')
|
|
409
388
|
}
|
|
410
389
|
```
|
|
411
390
|
|
|
412
|
-
###
|
|
391
|
+
### 파생 상태 구독
|
|
413
392
|
|
|
414
393
|
```tsx
|
|
415
|
-
// ❌
|
|
394
|
+
// ❌ 픽셀 변경마다 re-render
|
|
416
395
|
function Sidebar() {
|
|
417
|
-
const width = useWindowWidth() //
|
|
396
|
+
const width = useWindowWidth() // 연속 업데이트
|
|
418
397
|
const isMobile = width < 768
|
|
419
398
|
return <nav className={isMobile ? 'mobile' : 'desktop'}>
|
|
420
399
|
}
|
|
421
400
|
|
|
422
|
-
// ✅
|
|
401
|
+
// ✅ boolean 변경 시에만 re-render
|
|
423
402
|
function Sidebar() {
|
|
424
403
|
const isMobile = useMediaQuery('(max-width: 767px)')
|
|
425
404
|
return <nav className={isMobile ? 'mobile' : 'desktop'}>
|
|
@@ -432,12 +411,12 @@ function Sidebar() {
|
|
|
432
411
|
|
|
433
412
|
<rendering_performance>
|
|
434
413
|
|
|
435
|
-
## 6.
|
|
414
|
+
## 6. 렌더링 성능 (MEDIUM)
|
|
436
415
|
|
|
437
|
-
###
|
|
416
|
+
### SVG 래퍼 애니메이션
|
|
438
417
|
|
|
439
418
|
```tsx
|
|
440
|
-
// ❌
|
|
419
|
+
// ❌ SVG 직접 애니메이션 - 하드웨어 가속 없음
|
|
441
420
|
function LoadingSpinner() {
|
|
442
421
|
return (
|
|
443
422
|
<svg className="animate-spin" width="24" height="24">
|
|
@@ -446,7 +425,7 @@ function LoadingSpinner() {
|
|
|
446
425
|
)
|
|
447
426
|
}
|
|
448
427
|
|
|
449
|
-
// ✅
|
|
428
|
+
// ✅ 래퍼 div 애니메이션 - 하드웨어 가속
|
|
450
429
|
function LoadingSpinner() {
|
|
451
430
|
return (
|
|
452
431
|
<div className="animate-spin">
|
|
@@ -461,7 +440,7 @@ function LoadingSpinner() {
|
|
|
461
440
|
### content-visibility
|
|
462
441
|
|
|
463
442
|
```css
|
|
464
|
-
/*
|
|
443
|
+
/* 긴 리스트 최적화 */
|
|
465
444
|
.message-item {
|
|
466
445
|
content-visibility: auto;
|
|
467
446
|
contain-intrinsic-size: 0 80px;
|
|
@@ -483,30 +462,30 @@ function MessageList({ messages }: { messages: Message[] }) {
|
|
|
483
462
|
}
|
|
484
463
|
```
|
|
485
464
|
|
|
486
|
-
|
|
465
|
+
1000개 메시지 기준, 브라우저가 ~990개 오프스크린 항목의 레이아웃/페인트 스킵 (초기 렌더 10배 빠름)
|
|
487
466
|
|
|
488
|
-
###
|
|
467
|
+
### Hydration 불일치 방지
|
|
489
468
|
|
|
490
469
|
```tsx
|
|
491
|
-
// ❌ SSR
|
|
470
|
+
// ❌ SSR 실패
|
|
492
471
|
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
493
|
-
const theme = localStorage.getItem('theme') || 'light' // localStorage undefined
|
|
472
|
+
const theme = localStorage.getItem('theme') || 'light' // localStorage는 서버에서 undefined
|
|
494
473
|
return <div className={theme}>{children}</div>
|
|
495
474
|
}
|
|
496
475
|
|
|
497
|
-
// ❌
|
|
476
|
+
// ❌ 깜빡임
|
|
498
477
|
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
499
478
|
const [theme, setTheme] = useState('light')
|
|
500
479
|
|
|
501
480
|
useEffect(() => {
|
|
502
481
|
const stored = localStorage.getItem('theme')
|
|
503
|
-
if (stored) setTheme(stored) //
|
|
482
|
+
if (stored) setTheme(stored) // hydration 후 실행 → 깜빡임
|
|
504
483
|
}, [])
|
|
505
484
|
|
|
506
485
|
return <div className={theme}>{children}</div>
|
|
507
486
|
}
|
|
508
487
|
|
|
509
|
-
// ✅
|
|
488
|
+
// ✅ 깜빡임 없음, hydration 불일치 없음
|
|
510
489
|
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
511
490
|
return (
|
|
512
491
|
<>
|
|
@@ -535,9 +514,9 @@ function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
|
535
514
|
|
|
536
515
|
<javascript_performance>
|
|
537
516
|
|
|
538
|
-
## 7. JavaScript
|
|
517
|
+
## 7. JavaScript 성능 (LOW-MEDIUM)
|
|
539
518
|
|
|
540
|
-
###
|
|
519
|
+
### 반복 조회용 Map
|
|
541
520
|
|
|
542
521
|
```typescript
|
|
543
522
|
// ❌ O(n) per lookup
|
|
@@ -560,15 +539,15 @@ function processOrders(orders: Order[], users: User[]) {
|
|
|
560
539
|
|
|
561
540
|
1000 orders × 1000 users: 1M ops → 2K ops
|
|
562
541
|
|
|
563
|
-
###
|
|
542
|
+
### 배열 비교 시 길이 체크
|
|
564
543
|
|
|
565
544
|
```typescript
|
|
566
|
-
// ❌
|
|
545
|
+
// ❌ 항상 비싼 비교 실행
|
|
567
546
|
function hasChanges(current: string[], original: string[]) {
|
|
568
547
|
return current.sort().join() !== original.sort().join()
|
|
569
548
|
}
|
|
570
549
|
|
|
571
|
-
// ✅ O(1)
|
|
550
|
+
// ✅ O(1) 길이 체크 먼저
|
|
572
551
|
function hasChanges(current: string[], original: string[]) {
|
|
573
552
|
if (current.length !== original.length) return true
|
|
574
553
|
|
|
@@ -581,10 +560,10 @@ function hasChanges(current: string[], original: string[]) {
|
|
|
581
560
|
}
|
|
582
561
|
```
|
|
583
562
|
|
|
584
|
-
###
|
|
563
|
+
### toSorted()로 불변성 유지
|
|
585
564
|
|
|
586
565
|
```typescript
|
|
587
|
-
// ❌
|
|
566
|
+
// ❌ 원본 배열 변경
|
|
588
567
|
function UserList({ users }: { users: User[] }) {
|
|
589
568
|
const sorted = useMemo(
|
|
590
569
|
() => users.sort((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -592,7 +571,7 @@ function UserList({ users }: { users: User[] }) {
|
|
|
592
571
|
)
|
|
593
572
|
}
|
|
594
573
|
|
|
595
|
-
// ✅
|
|
574
|
+
// ✅ 새 배열 생성
|
|
596
575
|
function UserList({ users }: { users: User[] }) {
|
|
597
576
|
const sorted = useMemo(
|
|
598
577
|
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -607,9 +586,9 @@ function UserList({ users }: { users: User[] }) {
|
|
|
607
586
|
|
|
608
587
|
<advanced_patterns>
|
|
609
588
|
|
|
610
|
-
## 8.
|
|
589
|
+
## 8. 고급 패턴 (LOW)
|
|
611
590
|
|
|
612
|
-
### useLatest
|
|
591
|
+
### useLatest로 안정적 콜백
|
|
613
592
|
|
|
614
593
|
```typescript
|
|
615
594
|
function useLatest<T>(value: T) {
|
|
@@ -622,7 +601,7 @@ function useLatest<T>(value: T) {
|
|
|
622
601
|
```
|
|
623
602
|
|
|
624
603
|
```tsx
|
|
625
|
-
// ❌
|
|
604
|
+
// ❌ effect가 콜백 변경마다 재실행
|
|
626
605
|
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
|
627
606
|
const [query, setQuery] = useState('')
|
|
628
607
|
|
|
@@ -632,7 +611,7 @@ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
|
|
632
611
|
}, [query, onSearch])
|
|
633
612
|
}
|
|
634
613
|
|
|
635
|
-
// ✅
|
|
614
|
+
// ✅ 안정적 effect, 최신 콜백
|
|
636
615
|
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
|
637
616
|
const [query, setQuery] = useState('')
|
|
638
617
|
const onSearchRef = useLatest(onSearch)
|
|
@@ -650,14 +629,14 @@ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
|
|
650
629
|
|
|
651
630
|
<references>
|
|
652
631
|
|
|
653
|
-
##
|
|
632
|
+
## 참고 자료
|
|
654
633
|
|
|
655
634
|
1. [React](https://react.dev)
|
|
656
635
|
2. [Next.js](https://nextjs.org)
|
|
657
636
|
3. [SWR](https://swr.vercel.app)
|
|
658
637
|
4. [better-all](https://github.com/shuding/better-all)
|
|
659
638
|
5. [node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
660
|
-
6. [Next.js Package Import
|
|
661
|
-
7. [Vercel Dashboard
|
|
639
|
+
6. [Next.js Package Import 최적화](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
|
|
640
|
+
7. [Vercel Dashboard 2배 빠르게](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
|
|
662
641
|
|
|
663
642
|
</references>
|