@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
package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-svg-precision.md
CHANGED
|
@@ -5,23 +5,23 @@ impactDescription: reduces file size
|
|
|
5
5
|
tags: rendering, svg, optimization, svgo
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## SVG 정밀도 최적화
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
SVG 좌표 정밀도를 줄여 파일 크기를 감소시키세요. 최적의 정밀도는 viewBox 크기에 따라 다르지만, 일반적으로 정밀도 감소를 고려해야 합니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (과도한 정밀도):**
|
|
13
13
|
|
|
14
14
|
```svg
|
|
15
15
|
<path d="M 10.293847 20.847362 L 30.938472 40.192837" />
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
**✅ 올바른 예시 (소수점 1자리):**
|
|
19
19
|
|
|
20
20
|
```svg
|
|
21
21
|
<path d="M 10.3 20.8 L 30.9 40.2" />
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
**
|
|
24
|
+
**SVGO로 자동화:**
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
npx svgo --precision=1 --multipass icon.svg
|
|
@@ -5,11 +5,11 @@ impactDescription: avoids unnecessary subscriptions
|
|
|
5
5
|
tags: rerender, searchParams, localStorage, optimization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 상태 읽기를 사용 시점으로 지연
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
콜백 내부에서만 읽는 경우 동적 상태(searchParams, localStorage)를 구독하지 마세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (모든 searchParams 변경에 구독):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function ShareButton({ chatId }: { chatId: string }) {
|
|
@@ -24,7 +24,7 @@ function ShareButton({ chatId }: { chatId: string }) {
|
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
**✅ 올바른 예시 (필요할 때만 읽기, 구독 없음):**
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
30
|
function ShareButton({ chatId }: { chatId: string }) {
|
|
@@ -5,11 +5,11 @@ impactDescription: minimizes effect re-runs
|
|
|
5
5
|
tags: rerender, useEffect, dependencies, optimization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Effect 의존성 좁히기
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
객체 대신 원시 타입 의존성을 지정하여 effect 재실행을 최소화하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (user의 모든 필드 변경 시 재실행):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
useEffect(() => {
|
|
@@ -17,7 +17,7 @@ useEffect(() => {
|
|
|
17
17
|
}, [user])
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**✅ 올바른 예시 (id 변경 시에만 재실행):**
|
|
21
21
|
|
|
22
22
|
```tsx
|
|
23
23
|
useEffect(() => {
|
|
@@ -25,17 +25,17 @@ useEffect(() => {
|
|
|
25
25
|
}, [user.id])
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**파생 상태의 경우, effect 밖에서 계산:**
|
|
29
29
|
|
|
30
30
|
```tsx
|
|
31
|
-
//
|
|
31
|
+
// ❌ 잘못된 예시: width=767, 766, 765... 모든 변경 시 실행
|
|
32
32
|
useEffect(() => {
|
|
33
33
|
if (width < 768) {
|
|
34
34
|
enableMobileMode()
|
|
35
35
|
}
|
|
36
36
|
}, [width])
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// ✅ 올바른 예시: boolean 전환 시에만 실행
|
|
39
39
|
const isMobile = width < 768
|
|
40
40
|
useEffect(() => {
|
|
41
41
|
if (isMobile) {
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-derived-state.md
CHANGED
|
@@ -5,21 +5,21 @@ impactDescription: reduces re-render frequency
|
|
|
5
5
|
tags: rerender, derived-state, media-query, optimization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 파생 상태 구독
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
연속적인 값 대신 파생된 boolean 상태를 구독하여 리렌더링 빈도를 줄이세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (픽셀 변경마다 리렌더링):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function Sidebar() {
|
|
16
|
-
const width = useWindowWidth() //
|
|
16
|
+
const width = useWindowWidth() // 연속적으로 업데이트
|
|
17
17
|
const isMobile = width < 768
|
|
18
18
|
return <nav className={isMobile ? 'mobile' : 'desktop'}>
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**✅ 올바른 예시 (boolean 변경 시에만 리렌더링):**
|
|
23
23
|
|
|
24
24
|
```tsx
|
|
25
25
|
function Sidebar() {
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-functional-setstate.md
CHANGED
|
@@ -5,70 +5,70 @@ impactDescription: prevents stale closures and unnecessary callback recreations
|
|
|
5
5
|
tags: react, hooks, useState, useCallback, callbacks, closures
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 함수형 setState 업데이트 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
현재 상태 값을 기반으로 상태를 업데이트할 때, 상태 변수를 직접 참조하는 대신 setState의 함수형 업데이트 형태를 사용하세요. 이는 오래된 클로저를 방지하고, 불필요한 의존성을 제거하며, 안정적인 콜백 참조를 생성합니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (상태를 의존성으로 요구):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function TodoList() {
|
|
16
16
|
const [items, setItems] = useState(initialItems)
|
|
17
|
-
|
|
18
|
-
//
|
|
17
|
+
|
|
18
|
+
// 콜백이 items에 의존해야 하며, items 변경마다 재생성됨
|
|
19
19
|
const addItems = useCallback((newItems: Item[]) => {
|
|
20
20
|
setItems([...items, ...newItems])
|
|
21
|
-
}, [items]) // ❌ items
|
|
22
|
-
|
|
23
|
-
//
|
|
21
|
+
}, [items]) // ❌ items 의존성이 재생성을 유발
|
|
22
|
+
|
|
23
|
+
// 의존성을 잊으면 오래된 클로저 위험
|
|
24
24
|
const removeItem = useCallback((id: string) => {
|
|
25
25
|
setItems(items.filter(item => item.id !== id))
|
|
26
|
-
}, []) // ❌
|
|
27
|
-
|
|
26
|
+
}, []) // ❌ items 의존성 누락 - 오래된 items를 사용!
|
|
27
|
+
|
|
28
28
|
return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
첫 번째 콜백은 `items`가 변경될 때마다 재생성되어 자식 컴포넌트의 불필요한 리렌더링을 유발할 수 있습니다. 두 번째 콜백은 오래된 클로저 버그가 있습니다—항상 초기 `items` 값을 참조합니다.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
**✅ 올바른 예시 (안정적인 콜백, 오래된 클로저 없음):**
|
|
35
35
|
|
|
36
36
|
```tsx
|
|
37
37
|
function TodoList() {
|
|
38
38
|
const [items, setItems] = useState(initialItems)
|
|
39
|
-
|
|
40
|
-
//
|
|
39
|
+
|
|
40
|
+
// 안정적인 콜백, 재생성 안 됨
|
|
41
41
|
const addItems = useCallback((newItems: Item[]) => {
|
|
42
42
|
setItems(curr => [...curr, ...newItems])
|
|
43
|
-
}, []) // ✅
|
|
44
|
-
|
|
45
|
-
//
|
|
43
|
+
}, []) // ✅ 의존성 불필요
|
|
44
|
+
|
|
45
|
+
// 항상 최신 상태 사용, 오래된 클로저 위험 없음
|
|
46
46
|
const removeItem = useCallback((id: string) => {
|
|
47
47
|
setItems(curr => curr.filter(item => item.id !== id))
|
|
48
|
-
}, []) // ✅
|
|
49
|
-
|
|
48
|
+
}, []) // ✅ 안전하고 안정적
|
|
49
|
+
|
|
50
50
|
return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
**장점:**
|
|
55
55
|
|
|
56
|
-
1.
|
|
57
|
-
2.
|
|
58
|
-
3.
|
|
59
|
-
4.
|
|
56
|
+
1. **안정적인 콜백 참조** - 상태 변경 시 콜백을 재생성할 필요가 없음
|
|
57
|
+
2. **오래된 클로저 없음** - 항상 최신 상태 값에서 작동
|
|
58
|
+
3. **더 적은 의존성** - 의존성 배열을 단순화하고 메모리 누수 감소
|
|
59
|
+
4. **버그 방지** - React 클로저 버그의 가장 일반적인 원인 제거
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
**함수형 업데이트를 사용해야 하는 경우:**
|
|
62
62
|
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
63
|
+
- 현재 상태 값에 의존하는 모든 setState
|
|
64
|
+
- 상태가 필요한 경우 useCallback/useMemo 내부
|
|
65
|
+
- 상태를 참조하는 이벤트 핸들러
|
|
66
|
+
- 상태를 업데이트하는 비동기 작업
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
**직접 업데이트가 괜찮은 경우:**
|
|
69
69
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
70
|
+
- 정적 값으로 상태 설정: `setCount(0)`
|
|
71
|
+
- props/인수로만 상태 설정: `setName(newName)`
|
|
72
|
+
- 상태가 이전 값에 의존하지 않음
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
**참고:** 프로젝트에 [React Compiler](https://react.dev/learn/react-compiler)가 활성화되어 있다면, 컴파일러가 일부 케이스를 자동으로 최적화할 수 있지만, 정확성을 위해 그리고 오래된 클로저 버그를 방지하기 위해 함수형 업데이트를 여전히 권장합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rerender-lazy-state-init.md
CHANGED
|
@@ -5,54 +5,54 @@ impactDescription: wasted computation on every render
|
|
|
5
5
|
tags: react, hooks, useState, performance, initialization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 지연 상태 초기화 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
비용이 큰 초기값에는 `useState`에 함수를 전달하세요. 함수 형태가 없으면 초기화 함수가 한 번만 사용되더라도 모든 렌더링마다 실행됩니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (모든 렌더링마다 실행):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function FilteredList({ items }: { items: Item[] }) {
|
|
16
|
-
// buildSearchIndex()
|
|
16
|
+
// buildSearchIndex()가 초기화 후에도 모든 렌더링마다 실행됨
|
|
17
17
|
const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
|
|
18
18
|
const [query, setQuery] = useState('')
|
|
19
|
-
|
|
20
|
-
//
|
|
19
|
+
|
|
20
|
+
// query가 변경되면 buildSearchIndex가 불필요하게 다시 실행됨
|
|
21
21
|
return <SearchResults index={searchIndex} query={query} />
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function UserProfile() {
|
|
25
|
-
// JSON.parse
|
|
25
|
+
// JSON.parse가 모든 렌더링마다 실행됨
|
|
26
26
|
const [settings, setSettings] = useState(
|
|
27
27
|
JSON.parse(localStorage.getItem('settings') || '{}')
|
|
28
28
|
)
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
return <SettingsForm settings={settings} onChange={setSettings} />
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
**✅ 올바른 예시 (한 번만 실행):**
|
|
35
35
|
|
|
36
36
|
```tsx
|
|
37
37
|
function FilteredList({ items }: { items: Item[] }) {
|
|
38
|
-
// buildSearchIndex()
|
|
38
|
+
// buildSearchIndex()가 초기 렌더링에만 실행됨
|
|
39
39
|
const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
|
|
40
40
|
const [query, setQuery] = useState('')
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
return <SearchResults index={searchIndex} query={query} />
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function UserProfile() {
|
|
46
|
-
// JSON.parse
|
|
46
|
+
// JSON.parse가 초기 렌더링에만 실행됨
|
|
47
47
|
const [settings, setSettings] = useState(() => {
|
|
48
48
|
const stored = localStorage.getItem('settings')
|
|
49
49
|
return stored ? JSON.parse(stored) : {}
|
|
50
50
|
})
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
return <SettingsForm settings={settings} onChange={setSettings} />
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
localStorage/sessionStorage에서 초기값을 계산하거나, 데이터 구조(인덱스, 맵)를 구축하거나, DOM에서 읽거나, 무거운 변환을 수행할 때 지연 초기화를 사용하세요.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
단순 원시 타입(`useState(0)`), 직접 참조(`useState(props.value)`), 또는 저렴한 리터럴(`useState({})`)의 경우 함수 형태는 불필요합니다.
|
|
@@ -5,11 +5,11 @@ impactDescription: enables early returns
|
|
|
5
5
|
tags: rerender, memo, useMemo, optimization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 메모이제이션된 컴포넌트로 추출
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
비용이 큰 작업을 메모이제이션된 컴포넌트로 추출하여 계산 전 조기 반환을 활성화하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (로딩 중에도 avatar 계산):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function Profile({ user, loading }: Props) {
|
|
@@ -23,7 +23,7 @@ function Profile({ user, loading }: Props) {
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
**✅ 올바른 예시 (로딩 중 계산 건너뛰기):**
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
29
|
const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
|
|
@@ -41,4 +41,4 @@ function Profile({ user, loading }: Props) {
|
|
|
41
41
|
}
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
**참고:** 프로젝트에 [React Compiler](https://react.dev/learn/react-compiler)가 활성화되어 있다면, `memo()`와 `useMemo()`를 사용한 수동 메모이제이션은 필요하지 않습니다. 컴파일러가 자동으로 리렌더링을 최적화합니다.
|
|
@@ -5,11 +5,11 @@ impactDescription: maintains UI responsiveness
|
|
|
5
5
|
tags: rerender, transitions, startTransition, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 긴급하지 않은 업데이트에 Transition 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
빈번하고 긴급하지 않은 상태 업데이트를 transition으로 표시하여 UI 응답성을 유지하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (모든 스크롤마다 UI 차단):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function ScrollTracker() {
|
|
@@ -22,7 +22,7 @@ function ScrollTracker() {
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
**✅ 올바른 예시 (논블로킹 업데이트):**
|
|
26
26
|
|
|
27
27
|
```tsx
|
|
28
28
|
import { startTransition } from 'react'
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/server-after-nonblocking.md
CHANGED
|
@@ -5,23 +5,23 @@ impactDescription: faster response times
|
|
|
5
5
|
tags: server, async, logging, analytics, side-effects
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 논블로킹 작업을 위한 after() 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Next.js의 `after()`를 사용하여 응답이 전송된 후 실행되어야 하는 작업을 예약하세요. 이를 통해 로깅, 분석, 기타 부수 효과가 응답을 차단하는 것을 방지할 수 있습니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (응답 차단):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { logUserAction } from '@/app/utils'
|
|
16
16
|
|
|
17
17
|
export async function POST(request: Request) {
|
|
18
|
-
//
|
|
18
|
+
// 변경 수행
|
|
19
19
|
await updateDatabase(request)
|
|
20
|
-
|
|
21
|
-
//
|
|
20
|
+
|
|
21
|
+
// 로깅이 응답을 차단함
|
|
22
22
|
const userAgent = request.headers.get('user-agent') || 'unknown'
|
|
23
23
|
await logUserAction({ userAgent })
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
return new Response(JSON.stringify({ status: 'success' }), {
|
|
26
26
|
status: 200,
|
|
27
27
|
headers: { 'Content-Type': 'application/json' }
|
|
@@ -29,7 +29,7 @@ export async function POST(request: Request) {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
**올바른 예 (논블로킹):**
|
|
33
33
|
|
|
34
34
|
```tsx
|
|
35
35
|
import { after } from 'next/server'
|
|
@@ -37,17 +37,17 @@ import { headers, cookies } from 'next/headers'
|
|
|
37
37
|
import { logUserAction } from '@/app/utils'
|
|
38
38
|
|
|
39
39
|
export async function POST(request: Request) {
|
|
40
|
-
//
|
|
40
|
+
// 변경 수행
|
|
41
41
|
await updateDatabase(request)
|
|
42
|
-
|
|
43
|
-
//
|
|
42
|
+
|
|
43
|
+
// 응답 전송 후 로깅
|
|
44
44
|
after(async () => {
|
|
45
45
|
const userAgent = (await headers()).get('user-agent') || 'unknown'
|
|
46
46
|
const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
logUserAction({ sessionCookie, userAgent })
|
|
49
49
|
})
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
return new Response(JSON.stringify({ status: 'success' }), {
|
|
52
52
|
status: 200,
|
|
53
53
|
headers: { 'Content-Type': 'application/json' }
|
|
@@ -55,19 +55,19 @@ export async function POST(request: Request) {
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
응답은 즉시 전송되고 로깅은 백그라운드에서 실행됩니다.
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
**일반적인 사용 사례:**
|
|
61
61
|
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
62
|
+
- 분석 추적
|
|
63
|
+
- 감사 로깅
|
|
64
|
+
- 알림 전송
|
|
65
|
+
- 캐시 무효화
|
|
66
|
+
- 정리 작업
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
**중요 사항:**
|
|
69
69
|
|
|
70
|
-
- `after()
|
|
71
|
-
-
|
|
70
|
+
- `after()`는 응답이 실패하거나 리다이렉트되어도 실행됩니다
|
|
71
|
+
- Server Action, Route Handler, Server Component에서 작동합니다
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
참고: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)
|
|
@@ -5,18 +5,18 @@ impactDescription: caches across requests
|
|
|
5
5
|
tags: server, cache, lru, cross-request
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 요청 간 LRU 캐싱
|
|
9
9
|
|
|
10
|
-
`React.cache()
|
|
10
|
+
`React.cache()`는 단일 요청 내에서만 동작합니다. 연속된 요청 간 데이터 공유가 필요한 경우(사용자가 버튼 A를 클릭한 후 버튼 B를 클릭하는 경우)에는 LRU 캐시를 사용하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**구현:**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
import { LRUCache } from 'lru-cache'
|
|
16
16
|
|
|
17
17
|
const cache = new LRUCache<string, any>({
|
|
18
18
|
max: 1000,
|
|
19
|
-
ttl: 5 * 60 * 1000 // 5
|
|
19
|
+
ttl: 5 * 60 * 1000 // 5분
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
export async function getUser(id: string) {
|
|
@@ -28,14 +28,14 @@ export async function getUser(id: string) {
|
|
|
28
28
|
return user
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
31
|
+
// 요청 1: DB 쿼리, 결과 캐시됨
|
|
32
|
+
// 요청 2: 캐시 히트, DB 쿼리 없음
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
연속된 사용자 행동이 몇 초 내에 동일한 데이터가 필요한 여러 엔드포인트를 호출할 때 사용하세요.
|
|
36
36
|
|
|
37
|
-
**
|
|
37
|
+
**Vercel의 [Fluid Compute](https://vercel.com/docs/fluid-compute)를 사용하는 경우:** LRU 캐싱은 특히 효과적입니다. 여러 동시 요청이 동일한 함수 인스턴스와 캐시를 공유할 수 있기 때문입니다. 이는 Redis와 같은 외부 스토리지 없이도 요청 간 캐시가 유지됨을 의미합니다.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
**전통적인 서버리스 환경에서는:** 각 호출이 격리된 환경에서 실행되므로 프로세스 간 캐싱을 위해 Redis 사용을 고려하세요.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
참고: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)
|
|
@@ -5,11 +5,11 @@ impactDescription: deduplicates within request
|
|
|
5
5
|
tags: server, cache, react-cache, deduplication
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## React.cache()를 사용한 요청별 중복 제거
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
서버 사이드 요청 중복 제거를 위해 `React.cache()`를 사용하세요. 인증과 데이터베이스 쿼리에서 가장 큰 효과를 봅니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**사용법:**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
import { cache } from 'react'
|
|
@@ -23,4 +23,4 @@ export const getCurrentUser = cache(async () => {
|
|
|
23
23
|
})
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
단일 요청 내에서 `getCurrentUser()`를 여러 번 호출해도 쿼리는 한 번만 실행됩니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/server-parallel-fetching.md
CHANGED
|
@@ -5,11 +5,11 @@ impactDescription: eliminates server-side waterfalls
|
|
|
5
5
|
tags: server, rsc, parallel-fetching, composition
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 컴포넌트 조합을 통한 병렬 데이터 페칭
|
|
9
9
|
|
|
10
|
-
React Server
|
|
10
|
+
React Server Component는 트리 내에서 순차적으로 실행됩니다. 컴포지션을 활용해 재구성하여 데이터 페칭을 병렬화하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (Sidebar가 Page의 fetch 완료를 기다림):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
export default async function Page() {
|
|
@@ -28,7 +28,7 @@ async function Sidebar() {
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
**올바른 예 (둘 다 동시에 fetch):**
|
|
32
32
|
|
|
33
33
|
```tsx
|
|
34
34
|
async function Header() {
|
|
@@ -51,7 +51,7 @@ export default function Page() {
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
**
|
|
54
|
+
**children prop을 사용한 대안:**
|
|
55
55
|
|
|
56
56
|
```tsx
|
|
57
57
|
async function Layout({ children }: { children: ReactNode }) {
|
|
@@ -5,25 +5,25 @@ impactDescription: reduces data transfer size
|
|
|
5
5
|
tags: server, rsc, serialization, props
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## RSC 경계에서 직렬화 최소화
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
React Server/Client 경계는 모든 객체 속성을 문자열로 직렬화하여 HTML 응답과 후속 RSC 요청에 포함시킵니다. 이 직렬화된 데이터는 페이지 크기와 로드 시간에 직접적인 영향을 미치므로 **크기가 매우 중요합니다**. 클라이언트가 실제로 사용하는 필드만 전달하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**잘못된 예 (50개 필드 모두 직렬화):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
async function Page() {
|
|
16
|
-
const user = await fetchUser() // 50
|
|
16
|
+
const user = await fetchUser() // 50개 필드
|
|
17
17
|
return <Profile user={user} />
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
'use client'
|
|
21
21
|
function Profile({ user }: { user: User }) {
|
|
22
|
-
return <div>{user.name}</div> //
|
|
22
|
+
return <div>{user.name}</div> // 1개 필드만 사용
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
**올바른 예 (1개 필드만 직렬화):**
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
29
|
async function Page() {
|