@kood/claude-code 0.4.0 → 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 -531
- package/templates/.claude/commands/prd.md +0 -629
|
@@ -5,11 +5,11 @@ impactDescription: reduces iterations
|
|
|
5
5
|
tags: javascript, arrays, loops, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 여러 배열 반복 결합
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
여러 `.filter()` 또는 `.map()` 호출은 배열을 여러 번 반복합니다. 하나의 루프로 결합하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (3번 반복):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
const admins = users.filter(u => u.isAdmin)
|
|
@@ -17,7 +17,7 @@ const testers = users.filter(u => u.isTester)
|
|
|
17
17
|
const inactive = users.filter(u => !u.isActive)
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**✅ 올바른 예시 (1번 반복):**
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
23
|
const admins: User[] = []
|
|
@@ -5,17 +5,17 @@ impactDescription: avoids unnecessary computation
|
|
|
5
5
|
tags: javascript, functions, optimization, early-return
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 함수에서 조기 반환
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
결과가 결정되면 불필요한 처리를 건너뛰기 위해 조기 반환하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예 (답을 찾은 후에도 모든 항목 처리):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
function validateUsers(users: User[]) {
|
|
16
16
|
let hasError = false
|
|
17
17
|
let errorMessage = ''
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
for (const user of users) {
|
|
20
20
|
if (!user.email) {
|
|
21
21
|
hasError = true
|
|
@@ -25,14 +25,14 @@ function validateUsers(users: User[]) {
|
|
|
25
25
|
hasError = true
|
|
26
26
|
errorMessage = 'Name required'
|
|
27
27
|
}
|
|
28
|
-
//
|
|
28
|
+
// 오류를 찾은 후에도 모든 사용자를 계속 확인
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
return hasError ? { valid: false, error: errorMessage } : { valid: true }
|
|
32
32
|
}
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
**✅ 올바른 예 (첫 번째 오류에서 즉시 반환):**
|
|
36
36
|
|
|
37
37
|
```typescript
|
|
38
38
|
function validateUsers(users: User[]) {
|
|
@@ -5,11 +5,11 @@ impactDescription: avoids recreation
|
|
|
5
5
|
tags: javascript, regexp, optimization, memoization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## RegExp 생성 호이스팅
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
렌더 내부에서 RegExp를 생성하지 마세요. 모듈 스코프로 호이스팅하거나 `useMemo()`로 메모이제이션하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예 (매 렌더마다 새 RegExp):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function Highlighter({ text, query }: Props) {
|
|
@@ -19,7 +19,7 @@ function Highlighter({ text, query }: Props) {
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**✅ 올바른 예 (메모이제이션 또는 호이스팅):**
|
|
23
23
|
|
|
24
24
|
```tsx
|
|
25
25
|
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
@@ -34,9 +34,9 @@ function Highlighter({ text, query }: Props) {
|
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
**⚠️ 주의 (전역 정규식은 가변 상태를 가짐):**
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
전역 정규식(`/g`)은 가변적인 `lastIndex` 상태를 가집니다:
|
|
40
40
|
|
|
41
41
|
```typescript
|
|
42
42
|
const regex = /foo/g
|
|
@@ -5,11 +5,11 @@ impactDescription: 1M ops to 2K ops
|
|
|
5
5
|
tags: javascript, map, indexing, optimization, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 반복 조회를 위한 인덱스 맵 구축
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
동일한 키로 여러 번 `.find()` 호출하는 경우 Map을 사용하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (조회당 O(n)):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
function processOrders(orders: Order[], users: User[]) {
|
|
@@ -20,7 +20,7 @@ function processOrders(orders: Order[], users: User[]) {
|
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
**✅ 올바른 예시 (조회당 O(1)):**
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
26
|
function processOrders(orders: Order[], users: User[]) {
|
|
@@ -33,5 +33,5 @@ function processOrders(orders: Order[], users: User[]) {
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
맵을 한 번만 구축하고(O(n)), 이후 모든 조회는 O(1)입니다.
|
|
37
|
+
1000개 주문 × 1000명 사용자의 경우: 1M 연산 → 2K 연산으로 감소합니다.
|
|
@@ -5,32 +5,32 @@ impactDescription: avoids expensive operations when lengths differ
|
|
|
5
5
|
tags: javascript, arrays, performance, optimization, comparison
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 배열 비교 시 길이 우선 체크
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
정렬, 깊은 비교, 직렬화 등 비용이 큰 연산으로 배열을 비교할 때는 길이를 먼저 확인하세요. 길이가 다르면 배열이 같을 수 없습니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
실제 애플리케이션에서 이 최적화는 비교가 핫 패스(이벤트 핸들러, 렌더 루프)에서 실행될 때 특히 유용합니다.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**❌ 잘못된 예 (항상 비싼 비교 실행):**
|
|
15
15
|
|
|
16
16
|
```typescript
|
|
17
17
|
function hasChanges(current: string[], original: string[]) {
|
|
18
|
-
//
|
|
18
|
+
// 길이가 다른 경우에도 항상 정렬과 join 실행
|
|
19
19
|
return current.sort().join() !== original.sort().join()
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
`current.length`가 5이고 `original.length`가 100인 경우에도 두 개의 O(n log n) 정렬이 실행됩니다. 배열을 join하고 문자열을 비교하는 오버헤드도 발생합니다.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
**✅ 올바른 예 (O(1) 길이 체크 우선):**
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
28
|
function hasChanges(current: string[], original: string[]) {
|
|
29
|
-
//
|
|
29
|
+
// 길이가 다르면 조기 반환
|
|
30
30
|
if (current.length !== original.length) {
|
|
31
31
|
return true
|
|
32
32
|
}
|
|
33
|
-
//
|
|
33
|
+
// 길이가 같을 때만 정렬/join
|
|
34
34
|
const currentSorted = current.toSorted()
|
|
35
35
|
const originalSorted = original.toSorted()
|
|
36
36
|
for (let i = 0; i < currentSorted.length; i++) {
|
|
@@ -42,8 +42,8 @@ function hasChanges(current: string[], original: string[]) {
|
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
45
|
+
이 새로운 접근 방식이 더 효율적인 이유:
|
|
46
|
+
- 길이가 다를 때 정렬과 join의 오버헤드를 피함
|
|
47
|
+
- join된 문자열을 위한 메모리 소비를 피함 (특히 큰 배열에 중요)
|
|
48
|
+
- 원본 배열 변형을 피함
|
|
49
|
+
- 차이를 발견하면 조기 반환
|
|
@@ -5,11 +5,11 @@ impactDescription: O(n) instead of O(n log n)
|
|
|
5
5
|
tags: javascript, arrays, performance, sorting, algorithms
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 최소/최대값은 정렬 대신 루프 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
가장 작거나 큰 요소를 찾는 것은 배열을 한 번 순회하는 것만으로 충분합니다. 정렬은 낭비이며 느립니다.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예 (O(n log n) - 최신값을 찾기 위해 정렬):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
interface Project {
|
|
@@ -24,9 +24,9 @@ function getLatestProject(projects: Project[]) {
|
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
최대값을 찾기 위해 전체 배열을 정렬합니다.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**❌ 잘못된 예 (O(n log n) - 가장 오래된 것과 최신값을 위해 정렬):**
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
32
|
function getOldestAndNewest(projects: Project[]) {
|
|
@@ -35,43 +35,43 @@ function getOldestAndNewest(projects: Project[]) {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
최소/최대값만 필요한데도 불필요하게 정렬합니다.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**✅ 올바른 예 (O(n) - 단일 루프):**
|
|
41
41
|
|
|
42
42
|
```typescript
|
|
43
43
|
function getLatestProject(projects: Project[]) {
|
|
44
44
|
if (projects.length === 0) return null
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
let latest = projects[0]
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
for (let i = 1; i < projects.length; i++) {
|
|
49
49
|
if (projects[i].updatedAt > latest.updatedAt) {
|
|
50
50
|
latest = projects[i]
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
return latest
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function getOldestAndNewest(projects: Project[]) {
|
|
58
58
|
if (projects.length === 0) return { oldest: null, newest: null }
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
let oldest = projects[0]
|
|
61
61
|
let newest = projects[0]
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
for (let i = 1; i < projects.length; i++) {
|
|
64
64
|
if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
|
|
65
65
|
if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
|
|
68
68
|
return { oldest, newest }
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
배열을 단일 패스로 순회, 복사 없음, 정렬 없음.
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
**대안 (작은 배열의 경우 Math.min/Math.max):**
|
|
75
75
|
|
|
76
76
|
```typescript
|
|
77
77
|
const numbers = [5, 2, 8, 1, 9]
|
|
@@ -79,4 +79,4 @@ const min = Math.min(...numbers)
|
|
|
79
79
|
const max = Math.max(...numbers)
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
이것은 작은 배열에서는 동작하지만 spread 연산자 제한으로 인해 매우 큰 배열에서는 느릴 수 있습니다. 안정성을 위해 루프 접근 방식을 사용하세요.
|
|
@@ -5,18 +5,18 @@ impactDescription: O(n) to O(1)
|
|
|
5
5
|
tags: javascript, set, map, data-structures, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## O(1) 조회를 위해 Set/Map 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
반복적인 멤버십 검사를 위해 배열을 Set/Map으로 변환하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예 (검사마다 O(n)):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
const allowedIds = ['a', 'b', 'c', ...]
|
|
16
16
|
items.filter(item => allowedIds.includes(item.id))
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**✅ 올바른 예 (검사마다 O(1)):**
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
22
|
const allowedIds = new Set(['a', 'b', 'c', ...])
|
|
@@ -5,15 +5,15 @@ impactDescription: prevents mutation bugs in React state
|
|
|
5
5
|
tags: javascript, arrays, immutability, react, state, mutation
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 불변성을 위해 sort() 대신 toSorted() 사용
|
|
9
9
|
|
|
10
|
-
`.sort()
|
|
10
|
+
`.sort()`는 배열을 제자리에서 변형하여 React 상태 및 props에서 버그를 일으킬 수 있습니다. 변형 없이 새로운 정렬된 배열을 생성하려면 `.toSorted()`를 사용하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예 (원본 배열 변형):**
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
function UserList({ users }: { users: User[] }) {
|
|
16
|
-
//
|
|
16
|
+
// users prop 배열을 변형합니다!
|
|
17
17
|
const sorted = useMemo(
|
|
18
18
|
() => users.sort((a, b) => a.name.localeCompare(b.name)),
|
|
19
19
|
[users]
|
|
@@ -22,11 +22,11 @@ function UserList({ users }: { users: User[] }) {
|
|
|
22
22
|
}
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
**✅ 올바른 예 (새 배열 생성):**
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
28
|
function UserList({ users }: { users: User[] }) {
|
|
29
|
-
//
|
|
29
|
+
// 새로운 정렬된 배열 생성, 원본은 변경되지 않음
|
|
30
30
|
const sorted = useMemo(
|
|
31
31
|
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
|
|
32
32
|
[users]
|
|
@@ -35,23 +35,23 @@ function UserList({ users }: { users: User[] }) {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
**
|
|
38
|
+
**React에서 중요한 이유:**
|
|
39
39
|
|
|
40
|
-
1. Props/state
|
|
41
|
-
2.
|
|
40
|
+
1. Props/state 변형은 React의 불변성 모델을 깨뜨림 - React는 props와 state가 읽기 전용으로 취급되길 기대
|
|
41
|
+
2. 오래된 클로저 버그 발생 - 클로저(콜백, 이펙트) 내부에서 배열을 변형하면 예상치 못한 동작이 발생할 수 있음
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
**브라우저 지원 (구형 브라우저를 위한 폴백):**
|
|
44
44
|
|
|
45
|
-
`.toSorted()
|
|
45
|
+
`.toSorted()`는 모든 최신 브라우저(Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+)에서 사용 가능합니다. 구형 환경의 경우 spread 연산자를 사용하세요:
|
|
46
46
|
|
|
47
47
|
```typescript
|
|
48
|
-
//
|
|
48
|
+
// 구형 브라우저를 위한 폴백
|
|
49
49
|
const sorted = [...items].sort((a, b) => a.value - b.value)
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
**다른 불변 배열 메서드:**
|
|
53
53
|
|
|
54
|
-
- `.toSorted()` -
|
|
55
|
-
- `.toReversed()` -
|
|
56
|
-
- `.toSpliced()` -
|
|
57
|
-
- `.with()` -
|
|
54
|
+
- `.toSorted()` - 불변 정렬
|
|
55
|
+
- `.toReversed()` - 불변 역순
|
|
56
|
+
- `.toSpliced()` - 불변 splice
|
|
57
|
+
- `.with()` - 불변 요소 교체
|
|
@@ -5,11 +5,11 @@ impactDescription: preserves state/DOM
|
|
|
5
5
|
tags: rendering, activity, visibility, state-preservation
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 표시/숨김에 Activity 컴포넌트 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
자주 가시성을 전환하는 비용이 많이 드는 컴포넌트의 상태/DOM을 보존하려면 React의 `<Activity>`를 사용하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**사용법:**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { Activity } from 'react'
|
|
@@ -23,4 +23,4 @@ function Dropdown({ isOpen }: Props) {
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
비용이 많이 드는 리렌더링과 상태 손실을 방지합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-animate-svg-wrapper.md
CHANGED
|
@@ -5,19 +5,19 @@ impactDescription: enables hardware acceleration
|
|
|
5
5
|
tags: rendering, svg, css, animation, performance
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## SVG 요소 대신 래퍼 애니메이션
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
많은 브라우저가 SVG 요소에 대한 CSS3 애니메이션의 하드웨어 가속을 지원하지 않습니다. SVG를 `<div>`로 감싸고 래퍼를 애니메이션하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (SVG 직접 애니메이션 - 하드웨어 가속 없음):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function LoadingSpinner() {
|
|
16
16
|
return (
|
|
17
|
-
<svg
|
|
17
|
+
<svg
|
|
18
18
|
className="animate-spin"
|
|
19
|
-
width="24"
|
|
20
|
-
height="24"
|
|
19
|
+
width="24"
|
|
20
|
+
height="24"
|
|
21
21
|
viewBox="0 0 24 24"
|
|
22
22
|
>
|
|
23
23
|
<circle cx="12" cy="12" r="10" stroke="currentColor" />
|
|
@@ -26,15 +26,15 @@ function LoadingSpinner() {
|
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**✅ 올바른 예시 (래퍼 div 애니메이션 - 하드웨어 가속):**
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
32
|
function LoadingSpinner() {
|
|
33
33
|
return (
|
|
34
34
|
<div className="animate-spin">
|
|
35
|
-
<svg
|
|
36
|
-
width="24"
|
|
37
|
-
height="24"
|
|
35
|
+
<svg
|
|
36
|
+
width="24"
|
|
37
|
+
height="24"
|
|
38
38
|
viewBox="0 0 24 24"
|
|
39
39
|
>
|
|
40
40
|
<circle cx="12" cy="12" r="10" stroke="currentColor" />
|
|
@@ -44,4 +44,4 @@ function LoadingSpinner() {
|
|
|
44
44
|
}
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
이 방식은 모든 CSS transform과 transition(`transform`, `opacity`, `translate`, `scale`, `rotate`)에 적용됩니다. 래퍼 div는 브라우저가 더 부드러운 애니메이션을 위해 GPU 가속을 사용할 수 있게 합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-conditional-render.md
CHANGED
|
@@ -5,11 +5,11 @@ impactDescription: prevents rendering 0 or NaN
|
|
|
5
5
|
tags: rendering, conditional, jsx, falsy-values
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 명시적 조건부 렌더링 사용
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
조건이 `0`, `NaN` 또는 렌더링되는 다른 falsy 값일 수 있는 경우, 조건부 렌더링에 `&&` 대신 명시적인 삼항 연산자(`? :`)를 사용하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (count가 0일 때 "0"을 렌더링):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function Badge({ count }: { count: number }) {
|
|
@@ -20,11 +20,11 @@ function Badge({ count }: { count: number }) {
|
|
|
20
20
|
)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
//
|
|
23
|
+
// count = 0일 때 렌더링: <div>0</div>
|
|
24
|
+
// count = 5일 때 렌더링: <div><span class="badge">5</span></div>
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
**✅ 올바른 예시 (count가 0일 때 아무것도 렌더링하지 않음):**
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
30
|
function Badge({ count }: { count: number }) {
|
|
@@ -35,6 +35,6 @@ function Badge({ count }: { count: number }) {
|
|
|
35
35
|
)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
//
|
|
38
|
+
// count = 0일 때 렌더링: <div></div>
|
|
39
|
+
// count = 5일 때 렌더링: <div><span class="badge">5</span></div>
|
|
40
40
|
```
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-content-visibility.md
CHANGED
|
@@ -5,9 +5,9 @@ impactDescription: faster initial render
|
|
|
5
5
|
tags: rendering, css, content-visibility, long-lists
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
## CSS content-visibility
|
|
8
|
+
## 긴 목록을 위한 CSS content-visibility
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`content-visibility: auto`를 적용하여 화면 밖 렌더링을 연기하세요.
|
|
11
11
|
|
|
12
12
|
**CSS:**
|
|
13
13
|
|
|
@@ -18,7 +18,7 @@ Apply `content-visibility: auto` to defer off-screen rendering.
|
|
|
18
18
|
}
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
**예시:**
|
|
22
22
|
|
|
23
23
|
```tsx
|
|
24
24
|
function MessageList({ messages }: { messages: Message[] }) {
|
|
@@ -35,4 +35,4 @@ function MessageList({ messages }: { messages: Message[] }) {
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
1000개의 메시지가 있을 때, 브라우저는 ~990개의 화면 밖 항목에 대한 레이아웃/페인트를 건너뜁니다 (초기 렌더링 10배 빠름).
|
|
@@ -5,11 +5,11 @@ impactDescription: avoids re-creation
|
|
|
5
5
|
tags: rendering, jsx, static, optimization
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 정적 JSX 요소 끌어올리기
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
정적 JSX를 컴포넌트 외부로 추출하여 재생성을 방지하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (매 렌더링마다 요소 재생성):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function LoadingSkeleton() {
|
|
@@ -25,7 +25,7 @@ function Container() {
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**✅ 올바른 예시 (동일한 요소 재사용):**
|
|
29
29
|
|
|
30
30
|
```tsx
|
|
31
31
|
const loadingSkeleton = (
|
|
@@ -41,6 +41,6 @@ function Container() {
|
|
|
41
41
|
}
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
이 방식은 매 렌더링마다 재생성하기에 비용이 많이 드는 크고 정적인 SVG 노드에 특히 유용합니다.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
**참고:** 프로젝트에 [React Compiler](https://react.dev/learn/react-compiler)가 활성화되어 있다면, 컴파일러가 자동으로 정적 JSX 요소를 끌어올리고 컴포넌트 리렌더링을 최적화하므로 수동 끌어올리기가 불필요합니다.
|
package/templates/.claude/skills/nextjs-react-best-practices/rules/rendering-hydration-no-flicker.md
CHANGED
|
@@ -5,17 +5,17 @@ impactDescription: avoids visual flicker and hydration errors
|
|
|
5
5
|
tags: rendering, ssr, hydration, localStorage, flicker
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 깜박임 없이 하이드레이션 불일치 방지
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
클라이언트 측 저장소(localStorage, 쿠키)에 의존하는 콘텐츠를 렌더링할 때, 동기 스크립트를 주입하여 React가 하이드레이션하기 전에 DOM을 업데이트함으로써 SSR 오류와 하이드레이션 후 깜박임을 모두 방지하세요.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
**❌ 잘못된 예시 (SSR 오류 발생):**
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
16
|
-
// localStorage
|
|
16
|
+
// localStorage는 서버에서 사용 불가 - 오류 발생
|
|
17
17
|
const theme = localStorage.getItem('theme') || 'light'
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
return (
|
|
20
20
|
<div className={theme}>
|
|
21
21
|
{children}
|
|
@@ -24,22 +24,22 @@ function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
|
24
24
|
}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
`localStorage`가 undefined이기 때문에 서버 사이드 렌더링이 실패합니다.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
**❌ 잘못된 예시 (시각적 깜박임):**
|
|
30
30
|
|
|
31
31
|
```tsx
|
|
32
32
|
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
33
33
|
const [theme, setTheme] = useState('light')
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
useEffect(() => {
|
|
36
|
-
//
|
|
36
|
+
// 하이드레이션 후 실행 - 눈에 보이는 깜박임 발생
|
|
37
37
|
const stored = localStorage.getItem('theme')
|
|
38
38
|
if (stored) {
|
|
39
39
|
setTheme(stored)
|
|
40
40
|
}
|
|
41
41
|
}, [])
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
return (
|
|
44
44
|
<div className={theme}>
|
|
45
45
|
{children}
|
|
@@ -48,9 +48,9 @@ function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
컴포넌트가 먼저 기본값(`light`)으로 렌더링된 다음 하이드레이션 후 업데이트되어, 잘못된 콘텐츠가 눈에 보이게 깜박입니다.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
**✅ 올바른 예시 (깜박임 없음, 하이드레이션 불일치 없음):**
|
|
54
54
|
|
|
55
55
|
```tsx
|
|
56
56
|
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
@@ -77,6 +77,6 @@ function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
|
77
77
|
}
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
인라인 스크립트가 요소를 표시하기 전에 동기적으로 실행되어, DOM이 이미 올바른 값을 가지도록 보장합니다. 깜박임도 없고, 하이드레이션 불일치도 없습니다.
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
이 패턴은 테마 토글, 사용자 선호도, 인증 상태 및 기본값을 깜박이지 않고 즉시 렌더링해야 하는 모든 클라이언트 전용 데이터에 특히 유용합니다.
|