@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
|
@@ -5,11 +5,11 @@ impactDescription: 2-10× improvement
|
|
|
5
5
|
tags: async, parallelization, promises, waterfalls
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
## Promise.all()
|
|
8
|
+
## 독립적인 작업에 Promise.all() 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
비동기 작업이 상호 의존성이 없을 때는 `Promise.all()`을 사용하여 동시에 실행합니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (순차 실행, 3번의 라운드 트립):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
const user = await fetchUser()
|
|
@@ -17,7 +17,7 @@ const posts = await fetchPosts()
|
|
|
17
17
|
const comments = await fetchComments()
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**올바른 예 (병렬 실행, 1번의 라운드 트립):**
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
23
|
const [user, posts, comments] = await Promise.all([
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/async-suspense-boundaries.md
CHANGED
|
@@ -5,16 +5,16 @@ impactDescription: faster initial paint
|
|
|
5
5
|
tags: async, suspense, streaming, layout-shift
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 전략적 Suspense 경계
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
비동기 컴포넌트에서 JSX를 반환하기 전에 데이터를 기다리는 대신, Suspense 경계를 사용하여 데이터가 로드되는 동안 래퍼 UI를 더 빠르게 표시합니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (래퍼가 데이터 페칭에 의해 차단됨):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
async function Page() {
|
|
16
|
-
const data = await fetchData() //
|
|
17
|
-
|
|
16
|
+
const data = await fetchData() // 전체 페이지를 차단
|
|
17
|
+
|
|
18
18
|
return (
|
|
19
19
|
<div>
|
|
20
20
|
<div>Sidebar</div>
|
|
@@ -28,9 +28,9 @@ async function Page() {
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
중간 섹션만 데이터가 필요함에도 전체 레이아웃이 데이터를 기다립니다.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
**올바른 예 (래퍼가 즉시 표시되고, 데이터가 스트리밍됨):**
|
|
34
34
|
|
|
35
35
|
```tsx
|
|
36
36
|
function Page() {
|
|
@@ -49,20 +49,20 @@ function Page() {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async function DataDisplay() {
|
|
52
|
-
const data = await fetchData() //
|
|
52
|
+
const data = await fetchData() // 이 컴포넌트만 차단
|
|
53
53
|
return <div>{data.content}</div>
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Sidebar, Header,
|
|
57
|
+
Sidebar, Header, Footer는 즉시 렌더링됩니다. DataDisplay만 데이터를 기다립니다.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
**대안 (컴포넌트 간 Promise 공유):**
|
|
60
60
|
|
|
61
61
|
```tsx
|
|
62
62
|
function Page() {
|
|
63
|
-
//
|
|
63
|
+
// 즉시 fetch를 시작하되, await하지 않음
|
|
64
64
|
const dataPromise = fetchData()
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
return (
|
|
67
67
|
<div>
|
|
68
68
|
<div>Sidebar</div>
|
|
@@ -77,23 +77,23 @@ function Page() {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
|
|
80
|
-
const data = use(dataPromise) //
|
|
80
|
+
const data = use(dataPromise) // Promise를 언래핑
|
|
81
81
|
return <div>{data.content}</div>
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
|
|
85
|
-
const data = use(dataPromise) //
|
|
85
|
+
const data = use(dataPromise) // 동일한 Promise를 재사용
|
|
86
86
|
return <div>{data.summary}</div>
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
두 컴포넌트가 동일한 Promise를 공유하므로 fetch는 한 번만 발생합니다. 레이아웃은 즉시 렌더링되고 두 컴포넌트는 함께 기다립니다.
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
**이 패턴을 사용하지 말아야 할 경우:**
|
|
93
93
|
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
94
|
+
- 레이아웃 결정에 필요한 중요한 데이터 (위치 지정에 영향을 미침)
|
|
95
|
+
- 첫 화면의 SEO 중요 콘텐츠
|
|
96
|
+
- Suspense 오버헤드가 불필요한 작고 빠른 쿼리
|
|
97
|
+
- 레이아웃 시프트를 피하고 싶을 때 (로딩 → 콘텐츠 점프)
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
**트레이드오프:** 더 빠른 초기 페인트 vs 잠재적인 레이아웃 시프트. UX 우선순위에 따라 선택하세요.
|
|
@@ -5,55 +5,55 @@ impactDescription: 200-800ms import cost, slow builds
|
|
|
5
5
|
tags: bundle, imports, tree-shaking, barrel-files, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 배럴 파일 임포트 피하기
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
수천 개의 사용하지 않는 모듈을 로드하지 않도록 배럴 파일 대신 소스 파일에서 직접 임포트하세요. **배럴 파일 (Barrel files)**은 여러 모듈을 재내보내는 진입점입니다 (예: `export * from './module'`을 하는 `index.js`).
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
인기 있는 아이콘 및 컴포넌트 라이브러리는 진입 파일에 **최대 10,000개의 재내보내기**를 가질 수 있습니다. 많은 React 패키지의 경우 **임포트하는 데만 200-800ms가 소요**되어 개발 속도와 프로덕션 콜드 스타트 모두에 영향을 미칩니다.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**트리 쉐이킹이 도움이 되지 않는 이유:** 라이브러리가 외부(번들되지 않음)로 표시되면 번들러가 최적화할 수 없습니다. 트리 쉐이킹을 활성화하기 위해 번들하면 전체 모듈 그래프를 분석하느라 빌드가 상당히 느려집니다.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
**잘못된 예 (전체 라이브러리를 임포트):**
|
|
17
17
|
|
|
18
18
|
```tsx
|
|
19
19
|
import { Check, X, Menu } from 'lucide-react'
|
|
20
|
-
//
|
|
21
|
-
//
|
|
20
|
+
// 1,583개의 모듈을 로드, 개발 환경에서 ~2.8초 추가 소요
|
|
21
|
+
// 런타임 비용: 모든 콜드 스타트마다 200-800ms
|
|
22
22
|
|
|
23
23
|
import { Button, TextField } from '@mui/material'
|
|
24
|
-
//
|
|
24
|
+
// 2,225개의 모듈을 로드, 개발 환경에서 ~4.2초 추가 소요
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
**올바른 예 (필요한 것만 임포트):**
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
30
|
import Check from 'lucide-react/dist/esm/icons/check'
|
|
31
31
|
import X from 'lucide-react/dist/esm/icons/x'
|
|
32
32
|
import Menu from 'lucide-react/dist/esm/icons/menu'
|
|
33
|
-
//
|
|
33
|
+
// 3개의 모듈만 로드 (~2KB vs ~1MB)
|
|
34
34
|
|
|
35
35
|
import Button from '@mui/material/Button'
|
|
36
36
|
import TextField from '@mui/material/TextField'
|
|
37
|
-
//
|
|
37
|
+
// 사용하는 것만 로드
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**대안 (Next.js 13.5+):**
|
|
41
41
|
|
|
42
42
|
```js
|
|
43
|
-
// next.config.js -
|
|
43
|
+
// next.config.js - optimizePackageImports 사용
|
|
44
44
|
module.exports = {
|
|
45
45
|
experimental: {
|
|
46
46
|
optimizePackageImports: ['lucide-react', '@mui/material']
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
//
|
|
50
|
+
// 이제 인체공학적인 배럴 임포트를 유지할 수 있습니다:
|
|
51
51
|
import { Check, X, Menu } from 'lucide-react'
|
|
52
|
-
//
|
|
52
|
+
// 빌드 시 자동으로 직접 임포트로 변환됨
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
직접 임포트는 15-70% 더 빠른 개발 부팅, 28% 더 빠른 빌드, 40% 더 빠른 콜드 스타트, 그리고 훨씬 더 빠른 HMR을 제공합니다.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
일반적으로 영향을 받는 라이브러리: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
참고: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
|
|
@@ -5,11 +5,11 @@ impactDescription: loads large data only when needed
|
|
|
5
5
|
tags: bundle, conditional-loading, lazy-loading
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 조건부 모듈 로딩
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
기능이 활성화될 때만 대용량 데이터나 모듈을 로드하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**예시 (애니메이션 프레임 지연 로드):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function AnimationPlayer({ enabled }: { enabled: boolean }) {
|
|
@@ -28,4 +28,4 @@ function AnimationPlayer({ enabled }: { enabled: boolean }) {
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
`typeof window !== 'undefined'` 검사는 SSR을 위해 이 모듈을 번들링하는 것을 방지하여 서버 번들 크기와 빌드 속도를 최적화합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-defer-third-party.md
CHANGED
|
@@ -5,11 +5,11 @@ impactDescription: loads after hydration
|
|
|
5
5
|
tags: bundle, third-party, analytics, defer
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 중요하지 않은 서드파티 라이브러리 지연 로드
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
분석, 로깅, 오류 추적은 사용자 상호작용을 차단하지 않습니다. 하이드레이션 후에 로드하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (초기 번들을 차단):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { Analytics } from '@vercel/analytics/react'
|
|
@@ -26,7 +26,7 @@ export default function RootLayout({ children }) {
|
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**올바른 예 (하이드레이션 후 로드):**
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
32
|
import dynamic from 'next/dynamic'
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/bundle-dynamic-imports.md
CHANGED
|
@@ -5,11 +5,11 @@ impactDescription: directly affects TTI and LCP
|
|
|
5
5
|
tags: bundle, dynamic-import, code-splitting, next-dynamic
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 무거운 컴포넌트에 동적 임포트 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
초기 렌더링에 필요하지 않은 대형 컴포넌트는 `next/dynamic`을 사용하여 지연 로드하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (Monaco가 메인 청크와 함께 번들됨 ~300KB):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { MonacoEditor } from './monaco-editor'
|
|
@@ -19,7 +19,7 @@ function CodePanel({ code }: { code: string }) {
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**올바른 예 (Monaco가 온디맨드로 로드됨):**
|
|
23
23
|
|
|
24
24
|
```tsx
|
|
25
25
|
import dynamic from 'next/dynamic'
|
|
@@ -5,11 +5,11 @@ impactDescription: reduces perceived latency
|
|
|
5
5
|
tags: bundle, preload, user-intent, hover
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 사용자 의도 기반 프리로드
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
필요하기 전에 무거운 번들을 미리 로드하여 체감 지연 시간을 줄이세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**예시 (호버/포커스 시 프리로드):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function EditorButton({ onClick }: { onClick: () => void }) {
|
|
@@ -31,7 +31,7 @@ function EditorButton({ onClick }: { onClick: () => void }) {
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
**예시 (피처 플래그가 활성화될 때 프리로드):**
|
|
35
35
|
|
|
36
36
|
```tsx
|
|
37
37
|
function FlagsProvider({ children, flags }: Props) {
|
|
@@ -47,4 +47,4 @@ function FlagsProvider({ children, flags }: Props) {
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
`typeof window !== 'undefined'` 검사는 SSR을 위해 프리로드된 모듈을 번들링하는 것을 방지하여 서버 번들 크기와 빌드 속도를 최적화합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/client-event-listeners.md
CHANGED
|
@@ -5,11 +5,11 @@ impactDescription: single listener for N components
|
|
|
5
5
|
tags: client, swr, event-listeners, subscription
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 전역 이벤트 리스너 중복 제거
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`useSWRSubscription()`을 사용하여 컴포넌트 인스턴스 간 전역 이벤트 리스너를 공유하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (N개 인스턴스 = N개 리스너):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function useKeyboardShortcut(key: string, callback: () => void) {
|
|
@@ -25,18 +25,18 @@ function useKeyboardShortcut(key: string, callback: () => void) {
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
`useKeyboardShortcut` 훅을 여러 번 사용하면 각 인스턴스가 새로운 리스너를 등록합니다.
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
**올바른 예 (N개 인스턴스 = 1개 리스너):**
|
|
31
31
|
|
|
32
32
|
```tsx
|
|
33
33
|
import useSWRSubscription from 'swr/subscription'
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// 키별 콜백을 추적하는 모듈 레벨 Map
|
|
36
36
|
const keyCallbacks = new Map<string, Set<() => void>>()
|
|
37
37
|
|
|
38
38
|
function useKeyboardShortcut(key: string, callback: () => void) {
|
|
39
|
-
//
|
|
39
|
+
// Map에 이 콜백 등록
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
if (!keyCallbacks.has(key)) {
|
|
42
42
|
keyCallbacks.set(key, new Set())
|
|
@@ -66,8 +66,8 @@ function useKeyboardShortcut(key: string, callback: () => void) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
function Profile() {
|
|
69
|
-
//
|
|
70
|
-
useKeyboardShortcut('p', () => { /* ... */ })
|
|
69
|
+
// 여러 단축키가 동일한 리스너를 공유함
|
|
70
|
+
useKeyboardShortcut('p', () => { /* ... */ })
|
|
71
71
|
useKeyboardShortcut('k', () => { /* ... */ })
|
|
72
72
|
// ...
|
|
73
73
|
}
|
|
@@ -5,11 +5,11 @@ impactDescription: automatic deduplication
|
|
|
5
5
|
tags: client, swr, deduplication, data-fetching
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 자동 중복 제거를 위한 SWR 사용
|
|
9
9
|
|
|
10
|
-
SWR
|
|
10
|
+
SWR은 컴포넌트 인스턴스 간 요청 중복 제거, 캐싱, 재검증을 가능하게 합니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (중복 제거 없음, 각 인스턴스가 fetch):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function UserList() {
|
|
@@ -22,7 +22,7 @@ function UserList() {
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
**올바른 예 (여러 인스턴스가 하나의 요청 공유):**
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
28
|
import useSWR from 'swr'
|
|
@@ -32,7 +32,7 @@ function UserList() {
|
|
|
32
32
|
}
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
**불변 데이터의 경우:**
|
|
36
36
|
|
|
37
37
|
```tsx
|
|
38
38
|
import { useImmutableSWR } from '@/lib/swr'
|
|
@@ -42,7 +42,7 @@ function StaticContent() {
|
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
**변경(Mutation)의 경우:**
|
|
46
46
|
|
|
47
47
|
```tsx
|
|
48
48
|
import { useSWRMutation } from 'swr/mutation'
|
|
@@ -53,4 +53,4 @@ function UpdateButton() {
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
참고: [https://swr.vercel.app](https://swr.vercel.app)
|
|
@@ -5,15 +5,15 @@ impactDescription: reduces reflows/repaints
|
|
|
5
5
|
tags: javascript, dom, css, performance, reflow
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## DOM CSS 변경 일괄 처리
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
스타일을 한 번에 하나씩 변경하지 마세요. 여러 CSS 변경을 클래스나 `cssText`를 통해 그룹화하여 브라우저 리플로우를 최소화하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (여러 번 리플로우):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
function updateElementStyles(element: HTMLElement) {
|
|
16
|
-
//
|
|
16
|
+
// 각 줄마다 리플로우 발생
|
|
17
17
|
element.style.width = '100px'
|
|
18
18
|
element.style.height = '200px'
|
|
19
19
|
element.style.backgroundColor = 'blue'
|
|
@@ -21,10 +21,10 @@ function updateElementStyles(element: HTMLElement) {
|
|
|
21
21
|
}
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
**✅ 올바른 예시 (클래스 추가 - 단일 리플로우):**
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
// CSS
|
|
27
|
+
// CSS 파일
|
|
28
28
|
.highlighted-box {
|
|
29
29
|
width: 100px;
|
|
30
30
|
height: 200px;
|
|
@@ -38,7 +38,7 @@ function updateElementStyles(element: HTMLElement) {
|
|
|
38
38
|
}
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
**✅ 올바른 예시 (cssText 변경 - 단일 리플로우):**
|
|
42
42
|
|
|
43
43
|
```typescript
|
|
44
44
|
function updateElementStyles(element: HTMLElement) {
|
|
@@ -51,13 +51,13 @@ function updateElementStyles(element: HTMLElement) {
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
**React
|
|
54
|
+
**React 예시:**
|
|
55
55
|
|
|
56
56
|
```tsx
|
|
57
|
-
//
|
|
57
|
+
// ❌ 잘못된 예시: 스타일을 하나씩 변경
|
|
58
58
|
function Box({ isHighlighted }: { isHighlighted: boolean }) {
|
|
59
59
|
const ref = useRef<HTMLDivElement>(null)
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
useEffect(() => {
|
|
62
62
|
if (ref.current && isHighlighted) {
|
|
63
63
|
ref.current.style.width = '100px'
|
|
@@ -65,11 +65,11 @@ function Box({ isHighlighted }: { isHighlighted: boolean }) {
|
|
|
65
65
|
ref.current.style.backgroundColor = 'blue'
|
|
66
66
|
}
|
|
67
67
|
}, [isHighlighted])
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
return <div ref={ref}>Content</div>
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
//
|
|
72
|
+
// ✅ 올바른 예시: 클래스 토글
|
|
73
73
|
function Box({ isHighlighted }: { isHighlighted: boolean }) {
|
|
74
74
|
return (
|
|
75
75
|
<div className={isHighlighted ? 'highlighted-box' : ''}>
|
|
@@ -79,4 +79,4 @@ function Box({ isHighlighted }: { isHighlighted: boolean }) {
|
|
|
79
79
|
}
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
가능하면 인라인 스타일보다 CSS 클래스를 사용하세요. 클래스는 브라우저에 캐시되며 관심사를 더 잘 분리합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-function-results.md
CHANGED
|
@@ -5,20 +5,20 @@ impactDescription: avoid redundant computation
|
|
|
5
5
|
tags: javascript, cache, memoization, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 반복되는 함수 호출 캐싱
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
렌더링 중 동일한 입력으로 같은 함수가 반복 호출될 때 모듈 레벨 Map을 사용하여 함수 결과를 캐시하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (중복 계산):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
function ProjectList({ projects }: { projects: Project[] }) {
|
|
16
16
|
return (
|
|
17
17
|
<div>
|
|
18
18
|
{projects.map(project => {
|
|
19
|
-
// slugify()
|
|
19
|
+
// slugify()가 동일한 프로젝트 이름에 대해 100회 이상 호출됨
|
|
20
20
|
const slug = slugify(project.name)
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
return <ProjectCard key={project.id} slug={slug} />
|
|
23
23
|
})}
|
|
24
24
|
</div>
|
|
@@ -26,10 +26,10 @@ function ProjectList({ projects }: { projects: Project[] }) {
|
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**✅ 올바른 예시 (캐시된 결과):**
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
//
|
|
32
|
+
// 모듈 레벨 캐시
|
|
33
33
|
const slugifyCache = new Map<string, string>()
|
|
34
34
|
|
|
35
35
|
function cachedSlugify(text: string): string {
|
|
@@ -45,9 +45,9 @@ function ProjectList({ projects }: { projects: Project[] }) {
|
|
|
45
45
|
return (
|
|
46
46
|
<div>
|
|
47
47
|
{projects.map(project => {
|
|
48
|
-
//
|
|
48
|
+
// 고유한 프로젝트 이름당 한 번만 계산됨
|
|
49
49
|
const slug = cachedSlugify(project.name)
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
return <ProjectCard key={project.id} slug={slug} />
|
|
52
52
|
})}
|
|
53
53
|
</div>
|
|
@@ -55,7 +55,7 @@ function ProjectList({ projects }: { projects: Project[] }) {
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
**단일 값 함수를 위한 더 간단한 패턴:**
|
|
59
59
|
|
|
60
60
|
```typescript
|
|
61
61
|
let isLoggedInCache: boolean | null = null
|
|
@@ -64,17 +64,17 @@ function isLoggedIn(): boolean {
|
|
|
64
64
|
if (isLoggedInCache !== null) {
|
|
65
65
|
return isLoggedInCache
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
isLoggedInCache = document.cookie.includes('auth=')
|
|
69
69
|
return isLoggedInCache
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
//
|
|
72
|
+
// 인증 변경 시 캐시 초기화
|
|
73
73
|
function onAuthChange() {
|
|
74
74
|
isLoggedInCache = null
|
|
75
75
|
}
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
훅이 아닌 Map을 사용하세요. 그래야 유틸리티, 이벤트 핸들러 등 React 컴포넌트뿐만 아니라 어디서나 작동합니다.
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
참고: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/js-cache-property-access.md
CHANGED
|
@@ -5,11 +5,11 @@ impactDescription: reduces lookups
|
|
|
5
5
|
tags: javascript, loops, optimization, caching
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 루프에서 프로퍼티 접근 캐싱
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
핫 패스에서 객체 프로퍼티 조회를 캐시하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (3번 조회 × N번 반복):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
for (let i = 0; i < arr.length; i++) {
|
|
@@ -17,7 +17,7 @@ for (let i = 0; i < arr.length; i++) {
|
|
|
17
17
|
}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**✅ 올바른 예시 (총 1번 조회):**
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
23
|
const value = obj.config.settings.value
|
|
@@ -5,20 +5,20 @@ impactDescription: reduces expensive I/O
|
|
|
5
5
|
tags: javascript, localStorage, storage, caching, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Storage API 호출 캐싱
|
|
9
9
|
|
|
10
|
-
`localStorage`, `sessionStorage`,
|
|
10
|
+
`localStorage`, `sessionStorage`, `document.cookie`는 동기적이고 비용이 큽니다. 읽기를 메모리에 캐시하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (호출할 때마다 스토리지 읽기):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
function getTheme() {
|
|
16
16
|
return localStorage.getItem('theme') ?? 'light'
|
|
17
17
|
}
|
|
18
|
-
//
|
|
18
|
+
// 10번 호출 = 10번 스토리지 읽기
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
**✅ 올바른 예시 (Map 캐시):**
|
|
22
22
|
|
|
23
23
|
```typescript
|
|
24
24
|
const storageCache = new Map<string, string | null>()
|
|
@@ -32,13 +32,13 @@ function getLocalStorage(key: string) {
|
|
|
32
32
|
|
|
33
33
|
function setLocalStorage(key: string, value: string) {
|
|
34
34
|
localStorage.setItem(key, value)
|
|
35
|
-
storageCache.set(key, value) //
|
|
35
|
+
storageCache.set(key, value) // 캐시 동기화 유지
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
훅이 아닌 Map을 사용하세요. 그래야 유틸리티, 이벤트 핸들러 등 React 컴포넌트뿐만 아니라 어디서나 작동합니다.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
**쿠키 캐싱:**
|
|
42
42
|
|
|
43
43
|
```typescript
|
|
44
44
|
let cookieCache: Record<string, string> | null = null
|
|
@@ -53,9 +53,9 @@ function getCookie(name: string) {
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
**중요 (외부 변경 시 무효화):**
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
스토리지가 외부에서 변경될 수 있는 경우(다른 탭, 서버 설정 쿠키), 캐시를 무효화하세요:
|
|
59
59
|
|
|
60
60
|
```typescript
|
|
61
61
|
window.addEventListener('storage', (e) => {
|