@mandujs/mcp 0.12.2 → 0.13.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/README.md +367 -367
- package/package.json +2 -2
- package/src/activity-monitor.ts +847 -847
- package/src/adapters/index.ts +20 -20
- package/src/adapters/monitor-adapter.ts +100 -100
- package/src/adapters/tool-adapter.ts +88 -88
- package/src/executor/error-handler.ts +250 -250
- package/src/executor/index.ts +22 -22
- package/src/executor/tool-executor.ts +148 -148
- package/src/hooks/config-watcher.ts +174 -174
- package/src/hooks/index.ts +23 -23
- package/src/hooks/mcp-hooks.ts +227 -227
- package/src/index.ts +106 -106
- package/src/logging/index.ts +15 -15
- package/src/logging/mcp-transport.ts +134 -134
- package/src/registry/index.ts +13 -13
- package/src/registry/mcp-tool-registry.ts +298 -298
- package/src/resources/skills/guides.ts +1136 -1136
- package/src/resources/skills/index.ts +12 -12
- package/src/resources/skills/loader.ts +218 -218
- package/src/resources/skills/mandu-composition/SKILL.md +91 -91
- package/src/resources/skills/mandu-composition/metadata.json +13 -13
- package/src/resources/skills/mandu-composition/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-composition/rules/_template.md +77 -77
- package/src/resources/skills/mandu-composition/rules/comp-arch-avoid-boolean-props.md +146 -146
- package/src/resources/skills/mandu-composition/rules/comp-arch-compound-components.md +164 -164
- package/src/resources/skills/mandu-composition/rules/comp-island-event.md +161 -161
- package/src/resources/skills/mandu-composition/rules/comp-island-slot-split.md +167 -167
- package/src/resources/skills/mandu-composition/rules/comp-pattern-children.md +149 -149
- package/src/resources/skills/mandu-composition/rules/comp-state-context-interface.md +148 -148
- package/src/resources/skills/mandu-composition/rules/comp-state-lift-state.md +150 -150
- package/src/resources/skills/mandu-deployment/SKILL.md +92 -92
- package/src/resources/skills/mandu-deployment/_sections.md +41 -41
- package/src/resources/skills/mandu-deployment/_template.md +38 -38
- package/src/resources/skills/mandu-deployment/metadata.json +13 -13
- package/src/resources/skills/mandu-deployment/rules/deploy-build-bun.md +109 -109
- package/src/resources/skills/mandu-deployment/rules/deploy-build-output.md +115 -115
- package/src/resources/skills/mandu-deployment/rules/deploy-cicd-github.md +219 -219
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-bun.md +150 -150
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-compose.md +223 -223
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-fly.md +152 -152
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-render.md +179 -179
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-supabase.md +323 -323
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-vercel.md +140 -140
- package/src/resources/skills/mandu-fs-routes/SKILL.md +82 -82
- package/src/resources/skills/mandu-fs-routes/metadata.json +12 -12
- package/src/resources/skills/mandu-fs-routes/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-fs-routes/rules/_template.md +69 -69
- package/src/resources/skills/mandu-fs-routes/rules/routes-api-methods.md +65 -65
- package/src/resources/skills/mandu-fs-routes/rules/routes-dynamic-param.md +93 -93
- package/src/resources/skills/mandu-fs-routes/rules/routes-naming-page.md +55 -55
- package/src/resources/skills/mandu-guard/SKILL.md +129 -129
- package/src/resources/skills/mandu-guard/metadata.json +12 -12
- package/src/resources/skills/mandu-guard/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-guard/rules/_template.md +82 -82
- package/src/resources/skills/mandu-guard/rules/guard-config-rules.md +100 -100
- package/src/resources/skills/mandu-guard/rules/guard-layer-direction.md +76 -76
- package/src/resources/skills/mandu-guard/rules/guard-preset-mandu.md +81 -81
- package/src/resources/skills/mandu-guard/rules/guard-validate-import.md +80 -80
- package/src/resources/skills/mandu-hydration/SKILL.md +91 -91
- package/src/resources/skills/mandu-hydration/metadata.json +12 -12
- package/src/resources/skills/mandu-hydration/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-hydration/rules/_template.md +72 -72
- package/src/resources/skills/mandu-hydration/rules/hydration-data-event.md +109 -109
- package/src/resources/skills/mandu-hydration/rules/hydration-directive-use-client.md +55 -55
- package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +113 -113
- package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +68 -68
- package/src/resources/skills/mandu-performance/SKILL.md +85 -85
- package/src/resources/skills/mandu-performance/metadata.json +14 -14
- package/src/resources/skills/mandu-performance/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-performance/rules/_template.md +64 -64
- package/src/resources/skills/mandu-performance/rules/perf-async-defer-await.md +103 -103
- package/src/resources/skills/mandu-performance/rules/perf-async-parallel.md +95 -95
- package/src/resources/skills/mandu-performance/rules/perf-bun-file.md +124 -124
- package/src/resources/skills/mandu-performance/rules/perf-bun-serve.md +125 -125
- package/src/resources/skills/mandu-performance/rules/perf-bundle-imports.md +80 -80
- package/src/resources/skills/mandu-performance/rules/perf-bundle-island-lazy.md +145 -145
- package/src/resources/skills/mandu-performance/rules/perf-cache-react.md +98 -98
- package/src/resources/skills/mandu-performance/rules/perf-render-transitions.md +154 -154
- package/src/resources/skills/mandu-security/SKILL.md +87 -87
- package/src/resources/skills/mandu-security/metadata.json +13 -13
- package/src/resources/skills/mandu-security/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-security/rules/_template.md +74 -74
- package/src/resources/skills/mandu-security/rules/sec-auth-guard.md +127 -127
- package/src/resources/skills/mandu-security/rules/sec-env-management.md +133 -133
- package/src/resources/skills/mandu-security/rules/sec-input-validate.md +148 -148
- package/src/resources/skills/mandu-security/rules/sec-protect-csrf.md +146 -146
- package/src/resources/skills/mandu-security/rules/sec-protect-headers.md +138 -138
- package/src/resources/skills/mandu-slot/SKILL.md +85 -85
- package/src/resources/skills/mandu-slot/metadata.json +12 -12
- package/src/resources/skills/mandu-slot/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-slot/rules/_template.md +63 -63
- package/src/resources/skills/mandu-slot/rules/slot-basic-structure.md +38 -38
- package/src/resources/skills/mandu-slot/rules/slot-ctx-response.md +56 -56
- package/src/resources/skills/mandu-slot/rules/slot-guard-auth.md +59 -59
- package/src/resources/skills/mandu-slot/rules/slot-http-methods.md +64 -64
- package/src/resources/skills/mandu-styling/SKILL.md +154 -154
- package/src/resources/skills/mandu-styling/_sections.md +43 -43
- package/src/resources/skills/mandu-styling/_template.md +32 -32
- package/src/resources/skills/mandu-styling/metadata.json +15 -15
- package/src/resources/skills/mandu-styling/rules/style-component-compound.md +235 -235
- package/src/resources/skills/mandu-styling/rules/style-component-slots.md +255 -255
- package/src/resources/skills/mandu-styling/rules/style-component-tokens.md +205 -205
- package/src/resources/skills/mandu-styling/rules/style-island-animations.md +272 -272
- package/src/resources/skills/mandu-styling/rules/style-island-scoping.md +167 -167
- package/src/resources/skills/mandu-styling/rules/style-island-variants.md +221 -221
- package/src/resources/skills/mandu-styling/rules/style-perf-critical.md +209 -209
- package/src/resources/skills/mandu-styling/rules/style-perf-purge.md +192 -192
- package/src/resources/skills/mandu-styling/rules/style-setup-modules.md +162 -162
- package/src/resources/skills/mandu-styling/rules/style-setup-panda.md +164 -164
- package/src/resources/skills/mandu-styling/rules/style-setup-tailwind.md +170 -170
- package/src/resources/skills/mandu-styling/rules/style-tailwind-v4-gotchas.md +179 -179
- package/src/resources/skills/mandu-styling/rules/style-theme-darkmode.md +229 -229
- package/src/resources/skills/mandu-testing/SKILL.md +99 -99
- package/src/resources/skills/mandu-testing/metadata.json +13 -13
- package/src/resources/skills/mandu-testing/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-testing/rules/_template.md +65 -65
- package/src/resources/skills/mandu-testing/rules/test-component-island.md +195 -195
- package/src/resources/skills/mandu-testing/rules/test-e2e-playwright.md +196 -196
- package/src/resources/skills/mandu-testing/rules/test-mock-fetch.md +219 -219
- package/src/resources/skills/mandu-testing/rules/test-slot-unit.md +192 -192
- package/src/resources/skills/mandu-ui/SKILL.md +117 -117
- package/src/resources/skills/mandu-ui/_sections.md +23 -23
- package/src/resources/skills/mandu-ui/_template.md +32 -32
- package/src/resources/skills/mandu-ui/metadata.json +13 -13
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-aria.md +232 -232
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-focus.md +238 -238
- package/src/resources/skills/mandu-ui/rules/ui-composition-patterns.md +259 -259
- package/src/resources/skills/mandu-ui/rules/ui-island-integration.md +258 -258
- package/src/resources/skills/mandu-ui/rules/ui-radix-patterns.md +213 -213
- package/src/resources/skills/mandu-ui/rules/ui-shadcn-setup.md +209 -209
- package/src/resources/skills/recipes.ts +932 -932
- package/src/tools/generate.ts +7 -4
- package/src/tools/guard.ts +17 -4
- package/src/tools/hydration.ts +10 -10
- package/src/tools/project.ts +334 -334
- package/src/tools/runtime.ts +497 -497
- package/src/tools/seo.ts +417 -417
- package/src/tools/spec.ts +80 -159
- package/src/utils/project.ts +22 -12
- package/src/utils/withWarnings.ts +83 -83
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
# Rule Template
|
|
2
|
-
|
|
3
|
-
Use this template when creating new rules for mandu-performance.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
```markdown
|
|
8
|
-
---
|
|
9
|
-
title: Rule Title Here
|
|
10
|
-
impact: CRITICAL | HIGH | MEDIUM | LOW
|
|
11
|
-
impactDescription: 개선 수치 (예: "2-10× improvement", "40% faster")
|
|
12
|
-
tags: performance, tag1, tag2
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Rule Title Here
|
|
16
|
-
|
|
17
|
-
**Impact: {LEVEL} ({impactDescription})**
|
|
18
|
-
|
|
19
|
-
규칙의 목적과 성능 영향을 설명합니다.
|
|
20
|
-
|
|
21
|
-
**Incorrect (문제점 - 성능 영향 명시):**
|
|
22
|
-
|
|
23
|
-
\`\`\`typescript
|
|
24
|
-
// ❌ 순차 실행: 3번의 네트워크 왕복
|
|
25
|
-
const user = await fetchUser();
|
|
26
|
-
const posts = await fetchPosts();
|
|
27
|
-
const comments = await fetchComments();
|
|
28
|
-
// 총 시간: 100ms + 100ms + 100ms = 300ms
|
|
29
|
-
\`\`\`
|
|
30
|
-
|
|
31
|
-
**Correct (해결책 - 성능 개선 명시):**
|
|
32
|
-
|
|
33
|
-
\`\`\`typescript
|
|
34
|
-
// ✅ 병렬 실행: 1번의 네트워크 왕복
|
|
35
|
-
const [user, posts, comments] = await Promise.all([
|
|
36
|
-
fetchUser(),
|
|
37
|
-
fetchPosts(),
|
|
38
|
-
fetchComments(),
|
|
39
|
-
]);
|
|
40
|
-
// 총 시간: max(100ms, 100ms, 100ms) = 100ms
|
|
41
|
-
\`\`\`
|
|
42
|
-
|
|
43
|
-
## Mandu Context
|
|
44
|
-
|
|
45
|
-
Mandu에서 이 규칙을 적용하는 구체적인 방법을 설명합니다.
|
|
46
|
-
|
|
47
|
-
Reference: [관련 문서 링크](https://example.com)
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Naming Convention
|
|
53
|
-
|
|
54
|
-
- 파일명: `perf-{category}-{rule-name}.md`
|
|
55
|
-
- 예시: `perf-async-parallel.md`, `perf-bundle-imports.md`
|
|
56
|
-
|
|
57
|
-
## Impact Measurement
|
|
58
|
-
|
|
59
|
-
| Level | Typical Improvement |
|
|
60
|
-
|-------|---------------------|
|
|
61
|
-
| CRITICAL | 2-10× or 50%+ |
|
|
62
|
-
| HIGH | 20-50% |
|
|
63
|
-
| MEDIUM | 10-20% |
|
|
64
|
-
| LOW | < 10% |
|
|
1
|
+
# Rule Template
|
|
2
|
+
|
|
3
|
+
Use this template when creating new rules for mandu-performance.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
```markdown
|
|
8
|
+
---
|
|
9
|
+
title: Rule Title Here
|
|
10
|
+
impact: CRITICAL | HIGH | MEDIUM | LOW
|
|
11
|
+
impactDescription: 개선 수치 (예: "2-10× improvement", "40% faster")
|
|
12
|
+
tags: performance, tag1, tag2
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Rule Title Here
|
|
16
|
+
|
|
17
|
+
**Impact: {LEVEL} ({impactDescription})**
|
|
18
|
+
|
|
19
|
+
규칙의 목적과 성능 영향을 설명합니다.
|
|
20
|
+
|
|
21
|
+
**Incorrect (문제점 - 성능 영향 명시):**
|
|
22
|
+
|
|
23
|
+
\`\`\`typescript
|
|
24
|
+
// ❌ 순차 실행: 3번의 네트워크 왕복
|
|
25
|
+
const user = await fetchUser();
|
|
26
|
+
const posts = await fetchPosts();
|
|
27
|
+
const comments = await fetchComments();
|
|
28
|
+
// 총 시간: 100ms + 100ms + 100ms = 300ms
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
**Correct (해결책 - 성능 개선 명시):**
|
|
32
|
+
|
|
33
|
+
\`\`\`typescript
|
|
34
|
+
// ✅ 병렬 실행: 1번의 네트워크 왕복
|
|
35
|
+
const [user, posts, comments] = await Promise.all([
|
|
36
|
+
fetchUser(),
|
|
37
|
+
fetchPosts(),
|
|
38
|
+
fetchComments(),
|
|
39
|
+
]);
|
|
40
|
+
// 총 시간: max(100ms, 100ms, 100ms) = 100ms
|
|
41
|
+
\`\`\`
|
|
42
|
+
|
|
43
|
+
## Mandu Context
|
|
44
|
+
|
|
45
|
+
Mandu에서 이 규칙을 적용하는 구체적인 방법을 설명합니다.
|
|
46
|
+
|
|
47
|
+
Reference: [관련 문서 링크](https://example.com)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Naming Convention
|
|
53
|
+
|
|
54
|
+
- 파일명: `perf-{category}-{rule-name}.md`
|
|
55
|
+
- 예시: `perf-async-parallel.md`, `perf-bundle-imports.md`
|
|
56
|
+
|
|
57
|
+
## Impact Measurement
|
|
58
|
+
|
|
59
|
+
| Level | Typical Improvement |
|
|
60
|
+
|-------|---------------------|
|
|
61
|
+
| CRITICAL | 2-10× or 50%+ |
|
|
62
|
+
| HIGH | 20-50% |
|
|
63
|
+
| MEDIUM | 10-20% |
|
|
64
|
+
| LOW | < 10% |
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Defer await to Where Actually Used
|
|
3
|
-
impact: CRITICAL
|
|
4
|
-
impactDescription: Eliminates unnecessary blocking
|
|
5
|
-
tags: performance, async, await, slot
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Defer await to Where Actually Used
|
|
9
|
-
|
|
10
|
-
**Impact: CRITICAL (Eliminates unnecessary blocking)**
|
|
11
|
-
|
|
12
|
-
await는 실제로 값이 필요한 시점까지 지연하세요. 함수 시작에서 모든 것을 await하면 불필요한 블로킹이 발생합니다.
|
|
13
|
-
|
|
14
|
-
**Incorrect (너무 일찍 await):**
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
// spec/slots/user.slot.ts
|
|
18
|
-
import { Mandu } from "@mandujs/core";
|
|
19
|
-
|
|
20
|
-
export default Mandu.filling()
|
|
21
|
-
.get(async (ctx) => {
|
|
22
|
-
const userId = ctx.params.id;
|
|
23
|
-
|
|
24
|
-
// ❌ 캐시 확인 전에 DB 쿼리 시작하고 대기
|
|
25
|
-
const user = await fetchUserFromDB(userId);
|
|
26
|
-
|
|
27
|
-
// 캐시에 있으면 DB 쿼리는 불필요했음
|
|
28
|
-
const cached = cache.get(`user:${userId}`);
|
|
29
|
-
if (cached) {
|
|
30
|
-
return ctx.ok(cached);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return ctx.ok(user);
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
**Correct (필요한 시점에 await):**
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
// spec/slots/user.slot.ts
|
|
41
|
-
import { Mandu } from "@mandujs/core";
|
|
42
|
-
|
|
43
|
-
export default Mandu.filling()
|
|
44
|
-
.get(async (ctx) => {
|
|
45
|
-
const userId = ctx.params.id;
|
|
46
|
-
|
|
47
|
-
// 캐시 먼저 확인
|
|
48
|
-
const cached = cache.get(`user:${userId}`);
|
|
49
|
-
if (cached) {
|
|
50
|
-
return ctx.ok(cached); // DB 쿼리 안 함
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ✅ 캐시 미스일 때만 DB 쿼리
|
|
54
|
-
const user = await fetchUserFromDB(userId);
|
|
55
|
-
cache.set(`user:${userId}`, user);
|
|
56
|
-
|
|
57
|
-
return ctx.ok(user);
|
|
58
|
-
});
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Promise 시작은 일찍, await는 늦게
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
export default Mandu.filling()
|
|
65
|
-
.get(async (ctx) => {
|
|
66
|
-
// ✅ Promise 시작 (블로킹 없음)
|
|
67
|
-
const userPromise = fetchUser(ctx.params.id);
|
|
68
|
-
const configPromise = fetchConfig();
|
|
69
|
-
|
|
70
|
-
// 동기 작업 수행
|
|
71
|
-
const requestId = generateRequestId();
|
|
72
|
-
const timestamp = Date.now();
|
|
73
|
-
logRequest(requestId, timestamp);
|
|
74
|
-
|
|
75
|
-
// ✅ 필요한 시점에 await
|
|
76
|
-
const [user, config] = await Promise.all([userPromise, configPromise]);
|
|
77
|
-
|
|
78
|
-
return ctx.ok({ user, config, requestId });
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## 조건부 분기에서의 활용
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
export default Mandu.filling()
|
|
86
|
-
.get(async (ctx) => {
|
|
87
|
-
const { type } = ctx.query;
|
|
88
|
-
|
|
89
|
-
if (type === "summary") {
|
|
90
|
-
// 요약만 필요하면 상세 데이터 불필요
|
|
91
|
-
const summary = await fetchSummary(ctx.params.id);
|
|
92
|
-
return ctx.ok({ summary });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 상세 데이터가 필요한 경우에만 fetch
|
|
96
|
-
const [summary, details] = await Promise.all([
|
|
97
|
-
fetchSummary(ctx.params.id),
|
|
98
|
-
fetchDetails(ctx.params.id),
|
|
99
|
-
]);
|
|
100
|
-
|
|
101
|
-
return ctx.ok({ summary, details });
|
|
102
|
-
});
|
|
103
|
-
```
|
|
1
|
+
---
|
|
2
|
+
title: Defer await to Where Actually Used
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: Eliminates unnecessary blocking
|
|
5
|
+
tags: performance, async, await, slot
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Defer await to Where Actually Used
|
|
9
|
+
|
|
10
|
+
**Impact: CRITICAL (Eliminates unnecessary blocking)**
|
|
11
|
+
|
|
12
|
+
await는 실제로 값이 필요한 시점까지 지연하세요. 함수 시작에서 모든 것을 await하면 불필요한 블로킹이 발생합니다.
|
|
13
|
+
|
|
14
|
+
**Incorrect (너무 일찍 await):**
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// spec/slots/user.slot.ts
|
|
18
|
+
import { Mandu } from "@mandujs/core";
|
|
19
|
+
|
|
20
|
+
export default Mandu.filling()
|
|
21
|
+
.get(async (ctx) => {
|
|
22
|
+
const userId = ctx.params.id;
|
|
23
|
+
|
|
24
|
+
// ❌ 캐시 확인 전에 DB 쿼리 시작하고 대기
|
|
25
|
+
const user = await fetchUserFromDB(userId);
|
|
26
|
+
|
|
27
|
+
// 캐시에 있으면 DB 쿼리는 불필요했음
|
|
28
|
+
const cached = cache.get(`user:${userId}`);
|
|
29
|
+
if (cached) {
|
|
30
|
+
return ctx.ok(cached);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return ctx.ok(user);
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Correct (필요한 시점에 await):**
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// spec/slots/user.slot.ts
|
|
41
|
+
import { Mandu } from "@mandujs/core";
|
|
42
|
+
|
|
43
|
+
export default Mandu.filling()
|
|
44
|
+
.get(async (ctx) => {
|
|
45
|
+
const userId = ctx.params.id;
|
|
46
|
+
|
|
47
|
+
// 캐시 먼저 확인
|
|
48
|
+
const cached = cache.get(`user:${userId}`);
|
|
49
|
+
if (cached) {
|
|
50
|
+
return ctx.ok(cached); // DB 쿼리 안 함
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ✅ 캐시 미스일 때만 DB 쿼리
|
|
54
|
+
const user = await fetchUserFromDB(userId);
|
|
55
|
+
cache.set(`user:${userId}`, user);
|
|
56
|
+
|
|
57
|
+
return ctx.ok(user);
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Promise 시작은 일찍, await는 늦게
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
export default Mandu.filling()
|
|
65
|
+
.get(async (ctx) => {
|
|
66
|
+
// ✅ Promise 시작 (블로킹 없음)
|
|
67
|
+
const userPromise = fetchUser(ctx.params.id);
|
|
68
|
+
const configPromise = fetchConfig();
|
|
69
|
+
|
|
70
|
+
// 동기 작업 수행
|
|
71
|
+
const requestId = generateRequestId();
|
|
72
|
+
const timestamp = Date.now();
|
|
73
|
+
logRequest(requestId, timestamp);
|
|
74
|
+
|
|
75
|
+
// ✅ 필요한 시점에 await
|
|
76
|
+
const [user, config] = await Promise.all([userPromise, configPromise]);
|
|
77
|
+
|
|
78
|
+
return ctx.ok({ user, config, requestId });
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 조건부 분기에서의 활용
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
export default Mandu.filling()
|
|
86
|
+
.get(async (ctx) => {
|
|
87
|
+
const { type } = ctx.query;
|
|
88
|
+
|
|
89
|
+
if (type === "summary") {
|
|
90
|
+
// 요약만 필요하면 상세 데이터 불필요
|
|
91
|
+
const summary = await fetchSummary(ctx.params.id);
|
|
92
|
+
return ctx.ok({ summary });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 상세 데이터가 필요한 경우에만 fetch
|
|
96
|
+
const [summary, details] = await Promise.all([
|
|
97
|
+
fetchSummary(ctx.params.id),
|
|
98
|
+
fetchDetails(ctx.params.id),
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
return ctx.ok({ summary, details });
|
|
102
|
+
});
|
|
103
|
+
```
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Use Promise.all() for Independent Operations
|
|
3
|
-
impact: CRITICAL
|
|
4
|
-
impactDescription: 2-10× improvement
|
|
5
|
-
tags: performance, async, parallel, promises, slot
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Use Promise.all() for Independent Operations
|
|
9
|
-
|
|
10
|
-
**Impact: CRITICAL (2-10× improvement)**
|
|
11
|
-
|
|
12
|
-
독립적인 비동기 작업은 `Promise.all()`로 병렬 실행하세요. 순차 실행은 각 await마다 전체 네트워크 지연을 추가합니다.
|
|
13
|
-
|
|
14
|
-
**Incorrect (순차 실행, 3번의 왕복):**
|
|
15
|
-
|
|
16
|
-
```typescript
|
|
17
|
-
// spec/slots/dashboard.slot.ts
|
|
18
|
-
import { Mandu } from "@mandujs/core";
|
|
19
|
-
|
|
20
|
-
export default Mandu.filling()
|
|
21
|
-
.get(async (ctx) => {
|
|
22
|
-
// ❌ 순차 실행: 300ms (100ms × 3)
|
|
23
|
-
const user = await fetchUser(ctx.get("userId"));
|
|
24
|
-
const posts = await fetchPosts(ctx.get("userId"));
|
|
25
|
-
const notifications = await fetchNotifications(ctx.get("userId"));
|
|
26
|
-
|
|
27
|
-
return ctx.ok({ user, posts, notifications });
|
|
28
|
-
});
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Correct (병렬 실행, 1번의 왕복):**
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
// spec/slots/dashboard.slot.ts
|
|
35
|
-
import { Mandu } from "@mandujs/core";
|
|
36
|
-
|
|
37
|
-
export default Mandu.filling()
|
|
38
|
-
.get(async (ctx) => {
|
|
39
|
-
const userId = ctx.get("userId");
|
|
40
|
-
|
|
41
|
-
// ✅ 병렬 실행: 100ms (가장 느린 것 기준)
|
|
42
|
-
const [user, posts, notifications] = await Promise.all([
|
|
43
|
-
fetchUser(userId),
|
|
44
|
-
fetchPosts(userId),
|
|
45
|
-
fetchNotifications(userId),
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
return ctx.ok({ user, posts, notifications });
|
|
49
|
-
});
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## 부분 의존성이 있는 경우
|
|
53
|
-
|
|
54
|
-
일부 작업이 다른 작업의 결과에 의존하는 경우:
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
export default Mandu.filling()
|
|
58
|
-
.get(async (ctx) => {
|
|
59
|
-
// 1단계: 독립적인 작업 병렬 실행
|
|
60
|
-
const [user, config] = await Promise.all([
|
|
61
|
-
fetchUser(ctx.get("userId")),
|
|
62
|
-
fetchConfig(),
|
|
63
|
-
]);
|
|
64
|
-
|
|
65
|
-
// 2단계: user에 의존하는 작업 병렬 실행
|
|
66
|
-
const [posts, followers] = await Promise.all([
|
|
67
|
-
fetchPosts(user.id),
|
|
68
|
-
fetchFollowers(user.id),
|
|
69
|
-
]);
|
|
70
|
-
|
|
71
|
-
return ctx.ok({ user, config, posts, followers });
|
|
72
|
-
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Promise.allSettled() 사용
|
|
76
|
-
|
|
77
|
-
일부 실패를 허용하는 경우:
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
const results = await Promise.allSettled([
|
|
81
|
-
fetchUser(userId),
|
|
82
|
-
fetchPosts(userId), // 실패해도 OK
|
|
83
|
-
fetchNotifications(userId), // 실패해도 OK
|
|
84
|
-
]);
|
|
85
|
-
|
|
86
|
-
const [userResult, postsResult, notificationsResult] = results;
|
|
87
|
-
|
|
88
|
-
return ctx.ok({
|
|
89
|
-
user: userResult.status === "fulfilled" ? userResult.value : null,
|
|
90
|
-
posts: postsResult.status === "fulfilled" ? postsResult.value : [],
|
|
91
|
-
notifications: notificationsResult.status === "fulfilled" ? notificationsResult.value : [],
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Reference: [MDN Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
|
|
1
|
+
---
|
|
2
|
+
title: Use Promise.all() for Independent Operations
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: 2-10× improvement
|
|
5
|
+
tags: performance, async, parallel, promises, slot
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Promise.all() for Independent Operations
|
|
9
|
+
|
|
10
|
+
**Impact: CRITICAL (2-10× improvement)**
|
|
11
|
+
|
|
12
|
+
독립적인 비동기 작업은 `Promise.all()`로 병렬 실행하세요. 순차 실행은 각 await마다 전체 네트워크 지연을 추가합니다.
|
|
13
|
+
|
|
14
|
+
**Incorrect (순차 실행, 3번의 왕복):**
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// spec/slots/dashboard.slot.ts
|
|
18
|
+
import { Mandu } from "@mandujs/core";
|
|
19
|
+
|
|
20
|
+
export default Mandu.filling()
|
|
21
|
+
.get(async (ctx) => {
|
|
22
|
+
// ❌ 순차 실행: 300ms (100ms × 3)
|
|
23
|
+
const user = await fetchUser(ctx.get("userId"));
|
|
24
|
+
const posts = await fetchPosts(ctx.get("userId"));
|
|
25
|
+
const notifications = await fetchNotifications(ctx.get("userId"));
|
|
26
|
+
|
|
27
|
+
return ctx.ok({ user, posts, notifications });
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Correct (병렬 실행, 1번의 왕복):**
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// spec/slots/dashboard.slot.ts
|
|
35
|
+
import { Mandu } from "@mandujs/core";
|
|
36
|
+
|
|
37
|
+
export default Mandu.filling()
|
|
38
|
+
.get(async (ctx) => {
|
|
39
|
+
const userId = ctx.get("userId");
|
|
40
|
+
|
|
41
|
+
// ✅ 병렬 실행: 100ms (가장 느린 것 기준)
|
|
42
|
+
const [user, posts, notifications] = await Promise.all([
|
|
43
|
+
fetchUser(userId),
|
|
44
|
+
fetchPosts(userId),
|
|
45
|
+
fetchNotifications(userId),
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
return ctx.ok({ user, posts, notifications });
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 부분 의존성이 있는 경우
|
|
53
|
+
|
|
54
|
+
일부 작업이 다른 작업의 결과에 의존하는 경우:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
export default Mandu.filling()
|
|
58
|
+
.get(async (ctx) => {
|
|
59
|
+
// 1단계: 독립적인 작업 병렬 실행
|
|
60
|
+
const [user, config] = await Promise.all([
|
|
61
|
+
fetchUser(ctx.get("userId")),
|
|
62
|
+
fetchConfig(),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
// 2단계: user에 의존하는 작업 병렬 실행
|
|
66
|
+
const [posts, followers] = await Promise.all([
|
|
67
|
+
fetchPosts(user.id),
|
|
68
|
+
fetchFollowers(user.id),
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
return ctx.ok({ user, config, posts, followers });
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Promise.allSettled() 사용
|
|
76
|
+
|
|
77
|
+
일부 실패를 허용하는 경우:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const results = await Promise.allSettled([
|
|
81
|
+
fetchUser(userId),
|
|
82
|
+
fetchPosts(userId), // 실패해도 OK
|
|
83
|
+
fetchNotifications(userId), // 실패해도 OK
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
const [userResult, postsResult, notificationsResult] = results;
|
|
87
|
+
|
|
88
|
+
return ctx.ok({
|
|
89
|
+
user: userResult.status === "fulfilled" ? userResult.value : null,
|
|
90
|
+
posts: postsResult.status === "fulfilled" ? postsResult.value : [],
|
|
91
|
+
notifications: notificationsResult.status === "fulfilled" ? notificationsResult.value : [],
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Reference: [MDN Promise.all()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
|