@su-record/vibe 2.0.0 โ†’ 2.0.1

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.
Files changed (65) hide show
  1. package/.claude/agents/explorer.md +48 -48
  2. package/.claude/agents/implementer.md +53 -53
  3. package/.claude/agents/searcher.md +54 -54
  4. package/.claude/agents/simplifier.md +119 -119
  5. package/.claude/agents/tester.md +49 -49
  6. package/.claude/commands/vibe.analyze.md +239 -239
  7. package/.claude/commands/vibe.continue.md +88 -88
  8. package/.claude/commands/vibe.diagram.md +178 -178
  9. package/.claude/commands/vibe.reason.md +306 -306
  10. package/.claude/commands/vibe.run.md +760 -760
  11. package/.claude/commands/vibe.spec.md +339 -339
  12. package/.claude/commands/vibe.tool.md +153 -153
  13. package/.claude/commands/vibe.ui.md +137 -137
  14. package/.claude/commands/vibe.verify.md +238 -238
  15. package/.claude/settings.json +152 -152
  16. package/.claude/settings.local.json +4 -57
  17. package/.vibe/config.json +9 -0
  18. package/.vibe/constitution.md +184 -184
  19. package/.vibe/rules/core/communication-guide.md +104 -104
  20. package/.vibe/rules/core/development-philosophy.md +52 -52
  21. package/.vibe/rules/core/quick-start.md +120 -120
  22. package/.vibe/rules/quality/bdd-contract-testing.md +388 -388
  23. package/.vibe/rules/quality/checklist.md +276 -276
  24. package/.vibe/rules/quality/testing-strategy.md +437 -437
  25. package/.vibe/rules/standards/anti-patterns.md +369 -369
  26. package/.vibe/rules/standards/code-structure.md +291 -291
  27. package/.vibe/rules/standards/complexity-metrics.md +312 -312
  28. package/.vibe/rules/standards/naming-conventions.md +198 -198
  29. package/.vibe/rules/tools/mcp-hi-ai-guide.md +665 -665
  30. package/.vibe/rules/tools/mcp-workflow.md +51 -51
  31. package/.vibe/setup.sh +31 -31
  32. package/CLAUDE.md +122 -122
  33. package/LICENSE +21 -21
  34. package/README.md +568 -568
  35. package/dist/cli/index.d.ts.map +1 -1
  36. package/dist/cli/index.js +391 -406
  37. package/dist/cli/index.js.map +1 -1
  38. package/dist/lib/MemoryManager.js +92 -92
  39. package/dist/lib/PythonParser.js +108 -108
  40. package/dist/lib/gemini-mcp.js +15 -15
  41. package/dist/lib/gemini-oauth.js +35 -35
  42. package/dist/lib/gpt-mcp.js +17 -17
  43. package/dist/lib/gpt-oauth.js +44 -44
  44. package/dist/tools/analytics/getUsageAnalytics.js +12 -12
  45. package/dist/tools/memory/createMemoryTimeline.js +10 -10
  46. package/dist/tools/memory/getMemoryGraph.js +12 -12
  47. package/dist/tools/memory/getSessionContext.js +9 -9
  48. package/dist/tools/memory/linkMemories.js +14 -14
  49. package/dist/tools/memory/listMemories.js +4 -4
  50. package/dist/tools/memory/recallMemory.js +4 -4
  51. package/dist/tools/memory/saveMemory.js +4 -4
  52. package/dist/tools/memory/searchMemoriesAdvanced.js +22 -22
  53. package/dist/tools/planning/generatePrd.js +46 -46
  54. package/dist/tools/prompt/enhancePromptGemini.js +160 -160
  55. package/dist/tools/reasoning/applyReasoningFramework.js +56 -56
  56. package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
  57. package/package.json +67 -67
  58. package/templates/constitution-template.md +184 -184
  59. package/templates/contract-backend-template.md +517 -517
  60. package/templates/contract-frontend-template.md +594 -594
  61. package/templates/feature-template.md +96 -96
  62. package/templates/hooks-template.json +103 -103
  63. package/templates/spec-template.md +199 -199
  64. package/dist/lib/vibe-mcp.d.ts.map +0 -1
  65. package/dist/lib/vibe-mcp.js.map +0 -1
@@ -1,312 +1,312 @@
1
- # ๐Ÿ”ฌ ์†Œํ”„ํŠธ์›จ์–ด ์—”์ง€๋‹ˆ์–ด๋ง ๋ณต์žก๋„ ์ธก์ •
2
-
3
- ## 4.1 ๋ณต์žก๋„ ๋ฉ”ํŠธ๋ฆญ
4
-
5
- ### Cyclomatic Complexity (์ˆœํ™˜ ๋ณต์žก๋„)
6
-
7
- **์ •์˜**: ์ฝ”๋“œ์˜ ๋…๋ฆฝ์ ์ธ ์‹คํ–‰ ๊ฒฝ๋กœ ์ˆ˜
8
-
9
- **๋ชฉํ‘œ**: โ‰ค 10
10
-
11
- ```typescript
12
- // โŒ ๋†’์€ ์ˆœํ™˜ ๋ณต์žก๋„ (6)
13
- function processUser(user) {
14
- if (user.isActive) { // +1
15
- if (user.hasPermission) { // +1
16
- if (user.email) { // +1
17
- if (user.verified) { // +1
18
- return processData();
19
- }
20
- }
21
- }
22
- }
23
- return null;
24
- }
25
-
26
- // โœ… ๋‚ฎ์€ ์ˆœํ™˜ ๋ณต์žก๋„ (4) - Early returns ์‚ฌ์šฉ
27
- function processUser(user) {
28
- if (!user.isActive) return null; // +1
29
- if (!user.hasPermission) return null; // +1
30
- if (!user.email) return null; // +1
31
- if (!user.verified) return null; // +1
32
-
33
- return processData();
34
- }
35
- ```
36
-
37
- ### Cognitive Complexity (์ธ์ง€ ๋ณต์žก๋„)
38
-
39
- **์ •์˜**: ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ •์‹ ์  ๋…ธ๋ ฅ
40
-
41
- **๋ชฉํ‘œ**: โ‰ค 15
42
-
43
- ```typescript
44
- // โŒ ๋†’์€ ์ธ์ง€ ๋ณต์žก๋„
45
- function calculateDiscount(user, items) {
46
- let discount = 0;
47
- if (user.isPremium) { // +1
48
- for (let item of items) { // +1 (nesting)
49
- if (item.category === 'electronics') { // +2 (nested if)
50
- discount += item.price * 0.1;
51
- } else if (item.category === 'books') { // +1
52
- discount += item.price * 0.05;
53
- }
54
- }
55
- }
56
- return discount;
57
- }
58
-
59
- // โœ… ๋‚ฎ์€ ์ธ์ง€ ๋ณต์žก๋„ - ํ•จ์ˆ˜ ๋ถ„๋ฆฌ
60
- function calculateDiscount(user, items) {
61
- if (!user.isPremium) return 0; // +1
62
- return items.reduce((total, item) => total + getItemDiscount(item), 0);
63
- }
64
-
65
- function getItemDiscount(item) {
66
- const discountRates = {
67
- electronics: 0.1,
68
- books: 0.05,
69
- };
70
- return item.price * (discountRates[item.category] || 0);
71
- }
72
- ```
73
-
74
- ### Halstead Metrics (ํ• ์Šคํ…Œ๋“œ ๋ฉ”ํŠธ๋ฆญ)
75
-
76
- **์ธก์ • ํ•ญ๋ชฉ**:
77
- - **Operators**: ์—ฐ์‚ฐ์ž (=, +, -, *, if, for ๋“ฑ)
78
- - **Operands**: ํ”ผ์—ฐ์‚ฐ์ž (๋ณ€์ˆ˜, ์ƒ์ˆ˜, ํ•จ์ˆ˜๋ช…)
79
- - **Vocabulary**: ๊ณ ์œ  ์—ฐ์‚ฐ์ž + ๊ณ ์œ  ํ”ผ์—ฐ์‚ฐ์ž
80
- - **Length**: ์ „์ฒด ํ† ํฐ ์ˆ˜
81
- - **Difficulty**: ์ฝ”๋“œ ์ดํ•ด ๋‚œ์ด๋„
82
- - **Effort**: ์ฝ”๋“œ ์ž‘์„ฑ์— ํ•„์š”ํ•œ ์ •์‹ ์  ๋…ธ๋ ฅ
83
-
84
- ```typescript
85
- // Halstead ๋ฉ”ํŠธ๋ฆญ ์ธก์ • ์˜ˆ์‹œ
86
- function calculateArea(radius: number): number {
87
- const pi = 3.14159;
88
- return pi * radius * radius;
89
- }
90
-
91
- /*
92
- Operators: =, *, const, function, :, return (6๊ฐœ)
93
- Operands: calculateArea, radius, number, pi, 3.14159 (5๊ฐœ)
94
- Vocabulary: 6 + 5 = 11
95
- Length: ์ „์ฒด ํ† ํฐ ์ˆ˜
96
- Difficulty: Vocabulary์™€ operand ๋ฐ˜๋ณต์œผ๋กœ ๊ณ„์‚ฐ
97
- Effort: Difficulty ร— Volume
98
- */
99
- ```
100
-
101
- ## 4.2 ๊ฒฐํ•ฉ๋„ & ์‘์ง‘๋„
102
-
103
- ### ๋А์Šจํ•œ ๊ฒฐํ•ฉ (Loose Coupling)
104
-
105
- **๋ชฉํ‘œ**: ๋ชจ๋“ˆ ๊ฐ„ ์˜์กด์„ฑ ์ตœ์†Œํ™”
106
-
107
- ```typescript
108
- // โŒ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ - ์ง์ ‘ ์˜์กด์„ฑ
109
- class UserService {
110
- constructor() {
111
- this.database = new PostgreSQLDatabase(); // ์ง์ ‘ ์˜์กด
112
- this.emailService = new SendGridEmail(); // ์ง์ ‘ ์˜์กด
113
- }
114
- }
115
-
116
- // โœ… ๋А์Šจํ•œ ๊ฒฐํ•ฉ - ์˜์กด์„ฑ ์ฃผ์ž…
117
- interface IDatabase {
118
- save(data: unknown): Promise<void>;
119
- load(id: string): Promise<unknown>;
120
- }
121
-
122
- interface IEmailService {
123
- send(to: string, message: string): Promise<void>;
124
- }
125
-
126
- class UserService {
127
- constructor(
128
- private database: IDatabase,
129
- private emailService: IEmailService
130
- ) {}
131
- }
132
-
133
- // ์‚ฌ์šฉ
134
- const userService = new UserService(
135
- new PostgreSQLDatabase(),
136
- new SendGridEmail()
137
- );
138
- ```
139
-
140
- ### ๋†’์€ ์‘์ง‘๋„ (High Cohesion)
141
-
142
- **๋ชฉํ‘œ**: ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ๋งŒ ๋ชจ์Œ
143
-
144
- ```typescript
145
- // โŒ ๋‚ฎ์€ ์‘์ง‘๋„ - ๊ด€๋ จ ์—†๋Š” ๊ธฐ๋Šฅ๋“ค
146
- class Utils {
147
- validateEmail(email: string) { /* */ }
148
- formatCurrency(amount: number) { /* */ }
149
- sendNotification(message: string) { /* */ }
150
- calculateTax(income: number) { /* */ }
151
- }
152
-
153
- // โœ… ๋†’์€ ์‘์ง‘๋„ - ๊ด€๋ จ ๊ธฐ๋Šฅ๋งŒ
154
- class EmailValidator {
155
- validateFormat(email: string) { /* */ }
156
- validateDomain(email: string) { /* */ }
157
- validateMX(email: string) { /* */ }
158
- }
159
-
160
- class CurrencyFormatter {
161
- formatKRW(amount: number) { /* */ }
162
- formatUSD(amount: number) { /* */ }
163
- parseAmount(formatted: string) { /* */ }
164
- }
165
-
166
- class TaxCalculator {
167
- calculateIncomeTax(income: number) { /* */ }
168
- calculateVAT(amount: number) { /* */ }
169
- calculateTotal(income: number) { /* */ }
170
- }
171
- ```
172
-
173
- ## ๋ณต์žก๋„ ๊ฐ์†Œ ์ „๋žต
174
-
175
- ### 1. Early Return ํŒจํ„ด
176
-
177
- ```typescript
178
- // โŒ ์ค‘์ฒฉ๋œ if๋ฌธ
179
- function processOrder(order: Order) {
180
- if (order) {
181
- if (order.isValid) {
182
- if (order.items.length > 0) {
183
- if (order.user.isActive) {
184
- return processItems(order.items);
185
- }
186
- }
187
- }
188
- }
189
- return null;
190
- }
191
-
192
- // โœ… Early return
193
- function processOrder(order: Order) {
194
- if (!order) return null;
195
- if (!order.isValid) return null;
196
- if (order.items.length === 0) return null;
197
- if (!order.user.isActive) return null;
198
-
199
- return processItems(order.items);
200
- }
201
- ```
202
-
203
- ### 2. ์ „๋žต ํŒจํ„ด (Strategy Pattern)
204
-
205
- ```typescript
206
- // โŒ ๋ณต์žกํ•œ if-else ์ฒด์ธ
207
- function calculateShipping(type: string, weight: number) {
208
- if (type === 'express') {
209
- return weight * 5 + 10;
210
- } else if (type === 'standard') {
211
- return weight * 3 + 5;
212
- } else if (type === 'economy') {
213
- return weight * 2;
214
- }
215
- return 0;
216
- }
217
-
218
- // โœ… ์ „๋žต ํŒจํ„ด
219
- interface ShippingStrategy {
220
- calculate(weight: number): number;
221
- }
222
-
223
- class ExpressShipping implements ShippingStrategy {
224
- calculate(weight: number) {
225
- return weight * 5 + 10;
226
- }
227
- }
228
-
229
- class StandardShipping implements ShippingStrategy {
230
- calculate(weight: number) {
231
- return weight * 3 + 5;
232
- }
233
- }
234
-
235
- const strategies: Record<string, ShippingStrategy> = {
236
- express: new ExpressShipping(),
237
- standard: new StandardShipping(),
238
- };
239
-
240
- function calculateShipping(type: string, weight: number) {
241
- const strategy = strategies[type];
242
- return strategy ? strategy.calculate(weight) : 0;
243
- }
244
- ```
245
-
246
- ### 3. ํ•จ์ˆ˜ ์ถ”์ถœ (Extract Function)
247
-
248
- ```typescript
249
- // โŒ ๊ธด ํ•จ์ˆ˜
250
- function processUserRegistration(userData: UserData) {
251
- // 20์ค„: ์ด๋ฉ”์ผ ๊ฒ€์ฆ
252
- // 15์ค„: ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ
253
- // 10์ค„: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ
254
- // 5์ค„: ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก
255
- }
256
-
257
- // โœ… ํ•จ์ˆ˜ ์ถ”์ถœ
258
- function processUserRegistration(userData: UserData) {
259
- validateEmail(userData.email);
260
- const hashedPassword = hashPassword(userData.password);
261
- const user = saveToDatabase({ ...userData, password: hashedPassword });
262
- sendWelcomeEmail(user.email);
263
- return user;
264
- }
265
-
266
- function validateEmail(email: string) { /* ... */ }
267
- function hashPassword(password: string) { /* ... */ }
268
- function saveToDatabase(data: UserData) { /* ... */ }
269
- function sendWelcomeEmail(email: string) { /* ... */ }
270
- ```
271
-
272
- ## ์ธก์ • ๋„๊ตฌ
273
-
274
- ### TypeScript/JavaScript
275
-
276
- ```bash
277
- # ESLint (๋ณต์žก๋„ ์ธก์ • ํ”Œ๋Ÿฌ๊ทธ์ธ)
278
- npm install eslint-plugin-complexity
279
-
280
- # .eslintrc.js
281
- {
282
- "rules": {
283
- "complexity": ["error", 10],
284
- "max-depth": ["error", 3],
285
- "max-lines-per-function": ["error", 20]
286
- }
287
- }
288
- ```
289
-
290
- ### Python
291
-
292
- ```bash
293
- # Radon (๋ณต์žก๋„ ์ธก์ • ๋„๊ตฌ)
294
- pip install radon
295
-
296
- # Cyclomatic Complexity
297
- radon cc app/ -a -nc
298
-
299
- # Maintainability Index
300
- radon mi app/
301
- ```
302
-
303
- ## ๋ชฉํ‘œ ๋ฉ”ํŠธ๋ฆญ ์š”์•ฝ
304
-
305
- | ๋ฉ”ํŠธ๋ฆญ | ๋ชฉํ‘œ | ์„ค๋ช… |
306
- |--------|------|------|
307
- | Cyclomatic Complexity | โ‰ค 10 | ๋…๋ฆฝ์  ์‹คํ–‰ ๊ฒฝ๋กœ |
308
- | Cognitive Complexity | โ‰ค 15 | ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›€ |
309
- | Function Length | โ‰ค 20 lines | ์งง๊ณ  ์ง‘์ค‘๋œ ํ•จ์ˆ˜ |
310
- | Nesting Depth | โ‰ค 3 levels | ํ‰ํƒ„ํ•œ ๊ตฌ์กฐ |
311
- | Parameters | โ‰ค 5 | ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ œํ•œ |
312
- | Dependencies | โ‰ค 7 | ๋ชจ๋“ˆ ์˜์กด์„ฑ ์ œํ•œ |
1
+ # ๐Ÿ”ฌ ์†Œํ”„ํŠธ์›จ์–ด ์—”์ง€๋‹ˆ์–ด๋ง ๋ณต์žก๋„ ์ธก์ •
2
+
3
+ ## 4.1 ๋ณต์žก๋„ ๋ฉ”ํŠธ๋ฆญ
4
+
5
+ ### Cyclomatic Complexity (์ˆœํ™˜ ๋ณต์žก๋„)
6
+
7
+ **์ •์˜**: ์ฝ”๋“œ์˜ ๋…๋ฆฝ์ ์ธ ์‹คํ–‰ ๊ฒฝ๋กœ ์ˆ˜
8
+
9
+ **๋ชฉํ‘œ**: โ‰ค 10
10
+
11
+ ```typescript
12
+ // โŒ ๋†’์€ ์ˆœํ™˜ ๋ณต์žก๋„ (6)
13
+ function processUser(user) {
14
+ if (user.isActive) { // +1
15
+ if (user.hasPermission) { // +1
16
+ if (user.email) { // +1
17
+ if (user.verified) { // +1
18
+ return processData();
19
+ }
20
+ }
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+
26
+ // โœ… ๋‚ฎ์€ ์ˆœํ™˜ ๋ณต์žก๋„ (4) - Early returns ์‚ฌ์šฉ
27
+ function processUser(user) {
28
+ if (!user.isActive) return null; // +1
29
+ if (!user.hasPermission) return null; // +1
30
+ if (!user.email) return null; // +1
31
+ if (!user.verified) return null; // +1
32
+
33
+ return processData();
34
+ }
35
+ ```
36
+
37
+ ### Cognitive Complexity (์ธ์ง€ ๋ณต์žก๋„)
38
+
39
+ **์ •์˜**: ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ •์‹ ์  ๋…ธ๋ ฅ
40
+
41
+ **๋ชฉํ‘œ**: โ‰ค 15
42
+
43
+ ```typescript
44
+ // โŒ ๋†’์€ ์ธ์ง€ ๋ณต์žก๋„
45
+ function calculateDiscount(user, items) {
46
+ let discount = 0;
47
+ if (user.isPremium) { // +1
48
+ for (let item of items) { // +1 (nesting)
49
+ if (item.category === 'electronics') { // +2 (nested if)
50
+ discount += item.price * 0.1;
51
+ } else if (item.category === 'books') { // +1
52
+ discount += item.price * 0.05;
53
+ }
54
+ }
55
+ }
56
+ return discount;
57
+ }
58
+
59
+ // โœ… ๋‚ฎ์€ ์ธ์ง€ ๋ณต์žก๋„ - ํ•จ์ˆ˜ ๋ถ„๋ฆฌ
60
+ function calculateDiscount(user, items) {
61
+ if (!user.isPremium) return 0; // +1
62
+ return items.reduce((total, item) => total + getItemDiscount(item), 0);
63
+ }
64
+
65
+ function getItemDiscount(item) {
66
+ const discountRates = {
67
+ electronics: 0.1,
68
+ books: 0.05,
69
+ };
70
+ return item.price * (discountRates[item.category] || 0);
71
+ }
72
+ ```
73
+
74
+ ### Halstead Metrics (ํ• ์Šคํ…Œ๋“œ ๋ฉ”ํŠธ๋ฆญ)
75
+
76
+ **์ธก์ • ํ•ญ๋ชฉ**:
77
+ - **Operators**: ์—ฐ์‚ฐ์ž (=, +, -, *, if, for ๋“ฑ)
78
+ - **Operands**: ํ”ผ์—ฐ์‚ฐ์ž (๋ณ€์ˆ˜, ์ƒ์ˆ˜, ํ•จ์ˆ˜๋ช…)
79
+ - **Vocabulary**: ๊ณ ์œ  ์—ฐ์‚ฐ์ž + ๊ณ ์œ  ํ”ผ์—ฐ์‚ฐ์ž
80
+ - **Length**: ์ „์ฒด ํ† ํฐ ์ˆ˜
81
+ - **Difficulty**: ์ฝ”๋“œ ์ดํ•ด ๋‚œ์ด๋„
82
+ - **Effort**: ์ฝ”๋“œ ์ž‘์„ฑ์— ํ•„์š”ํ•œ ์ •์‹ ์  ๋…ธ๋ ฅ
83
+
84
+ ```typescript
85
+ // Halstead ๋ฉ”ํŠธ๋ฆญ ์ธก์ • ์˜ˆ์‹œ
86
+ function calculateArea(radius: number): number {
87
+ const pi = 3.14159;
88
+ return pi * radius * radius;
89
+ }
90
+
91
+ /*
92
+ Operators: =, *, const, function, :, return (6๊ฐœ)
93
+ Operands: calculateArea, radius, number, pi, 3.14159 (5๊ฐœ)
94
+ Vocabulary: 6 + 5 = 11
95
+ Length: ์ „์ฒด ํ† ํฐ ์ˆ˜
96
+ Difficulty: Vocabulary์™€ operand ๋ฐ˜๋ณต์œผ๋กœ ๊ณ„์‚ฐ
97
+ Effort: Difficulty ร— Volume
98
+ */
99
+ ```
100
+
101
+ ## 4.2 ๊ฒฐํ•ฉ๋„ & ์‘์ง‘๋„
102
+
103
+ ### ๋А์Šจํ•œ ๊ฒฐํ•ฉ (Loose Coupling)
104
+
105
+ **๋ชฉํ‘œ**: ๋ชจ๋“ˆ ๊ฐ„ ์˜์กด์„ฑ ์ตœ์†Œํ™”
106
+
107
+ ```typescript
108
+ // โŒ ๊ฐ•ํ•œ ๊ฒฐํ•ฉ - ์ง์ ‘ ์˜์กด์„ฑ
109
+ class UserService {
110
+ constructor() {
111
+ this.database = new PostgreSQLDatabase(); // ์ง์ ‘ ์˜์กด
112
+ this.emailService = new SendGridEmail(); // ์ง์ ‘ ์˜์กด
113
+ }
114
+ }
115
+
116
+ // โœ… ๋А์Šจํ•œ ๊ฒฐํ•ฉ - ์˜์กด์„ฑ ์ฃผ์ž…
117
+ interface IDatabase {
118
+ save(data: unknown): Promise<void>;
119
+ load(id: string): Promise<unknown>;
120
+ }
121
+
122
+ interface IEmailService {
123
+ send(to: string, message: string): Promise<void>;
124
+ }
125
+
126
+ class UserService {
127
+ constructor(
128
+ private database: IDatabase,
129
+ private emailService: IEmailService
130
+ ) {}
131
+ }
132
+
133
+ // ์‚ฌ์šฉ
134
+ const userService = new UserService(
135
+ new PostgreSQLDatabase(),
136
+ new SendGridEmail()
137
+ );
138
+ ```
139
+
140
+ ### ๋†’์€ ์‘์ง‘๋„ (High Cohesion)
141
+
142
+ **๋ชฉํ‘œ**: ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ๋งŒ ๋ชจ์Œ
143
+
144
+ ```typescript
145
+ // โŒ ๋‚ฎ์€ ์‘์ง‘๋„ - ๊ด€๋ จ ์—†๋Š” ๊ธฐ๋Šฅ๋“ค
146
+ class Utils {
147
+ validateEmail(email: string) { /* */ }
148
+ formatCurrency(amount: number) { /* */ }
149
+ sendNotification(message: string) { /* */ }
150
+ calculateTax(income: number) { /* */ }
151
+ }
152
+
153
+ // โœ… ๋†’์€ ์‘์ง‘๋„ - ๊ด€๋ จ ๊ธฐ๋Šฅ๋งŒ
154
+ class EmailValidator {
155
+ validateFormat(email: string) { /* */ }
156
+ validateDomain(email: string) { /* */ }
157
+ validateMX(email: string) { /* */ }
158
+ }
159
+
160
+ class CurrencyFormatter {
161
+ formatKRW(amount: number) { /* */ }
162
+ formatUSD(amount: number) { /* */ }
163
+ parseAmount(formatted: string) { /* */ }
164
+ }
165
+
166
+ class TaxCalculator {
167
+ calculateIncomeTax(income: number) { /* */ }
168
+ calculateVAT(amount: number) { /* */ }
169
+ calculateTotal(income: number) { /* */ }
170
+ }
171
+ ```
172
+
173
+ ## ๋ณต์žก๋„ ๊ฐ์†Œ ์ „๋žต
174
+
175
+ ### 1. Early Return ํŒจํ„ด
176
+
177
+ ```typescript
178
+ // โŒ ์ค‘์ฒฉ๋œ if๋ฌธ
179
+ function processOrder(order: Order) {
180
+ if (order) {
181
+ if (order.isValid) {
182
+ if (order.items.length > 0) {
183
+ if (order.user.isActive) {
184
+ return processItems(order.items);
185
+ }
186
+ }
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+
192
+ // โœ… Early return
193
+ function processOrder(order: Order) {
194
+ if (!order) return null;
195
+ if (!order.isValid) return null;
196
+ if (order.items.length === 0) return null;
197
+ if (!order.user.isActive) return null;
198
+
199
+ return processItems(order.items);
200
+ }
201
+ ```
202
+
203
+ ### 2. ์ „๋žต ํŒจํ„ด (Strategy Pattern)
204
+
205
+ ```typescript
206
+ // โŒ ๋ณต์žกํ•œ if-else ์ฒด์ธ
207
+ function calculateShipping(type: string, weight: number) {
208
+ if (type === 'express') {
209
+ return weight * 5 + 10;
210
+ } else if (type === 'standard') {
211
+ return weight * 3 + 5;
212
+ } else if (type === 'economy') {
213
+ return weight * 2;
214
+ }
215
+ return 0;
216
+ }
217
+
218
+ // โœ… ์ „๋žต ํŒจํ„ด
219
+ interface ShippingStrategy {
220
+ calculate(weight: number): number;
221
+ }
222
+
223
+ class ExpressShipping implements ShippingStrategy {
224
+ calculate(weight: number) {
225
+ return weight * 5 + 10;
226
+ }
227
+ }
228
+
229
+ class StandardShipping implements ShippingStrategy {
230
+ calculate(weight: number) {
231
+ return weight * 3 + 5;
232
+ }
233
+ }
234
+
235
+ const strategies: Record<string, ShippingStrategy> = {
236
+ express: new ExpressShipping(),
237
+ standard: new StandardShipping(),
238
+ };
239
+
240
+ function calculateShipping(type: string, weight: number) {
241
+ const strategy = strategies[type];
242
+ return strategy ? strategy.calculate(weight) : 0;
243
+ }
244
+ ```
245
+
246
+ ### 3. ํ•จ์ˆ˜ ์ถ”์ถœ (Extract Function)
247
+
248
+ ```typescript
249
+ // โŒ ๊ธด ํ•จ์ˆ˜
250
+ function processUserRegistration(userData: UserData) {
251
+ // 20์ค„: ์ด๋ฉ”์ผ ๊ฒ€์ฆ
252
+ // 15์ค„: ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹ฑ
253
+ // 10์ค„: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ €์žฅ
254
+ // 5์ค„: ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก
255
+ }
256
+
257
+ // โœ… ํ•จ์ˆ˜ ์ถ”์ถœ
258
+ function processUserRegistration(userData: UserData) {
259
+ validateEmail(userData.email);
260
+ const hashedPassword = hashPassword(userData.password);
261
+ const user = saveToDatabase({ ...userData, password: hashedPassword });
262
+ sendWelcomeEmail(user.email);
263
+ return user;
264
+ }
265
+
266
+ function validateEmail(email: string) { /* ... */ }
267
+ function hashPassword(password: string) { /* ... */ }
268
+ function saveToDatabase(data: UserData) { /* ... */ }
269
+ function sendWelcomeEmail(email: string) { /* ... */ }
270
+ ```
271
+
272
+ ## ์ธก์ • ๋„๊ตฌ
273
+
274
+ ### TypeScript/JavaScript
275
+
276
+ ```bash
277
+ # ESLint (๋ณต์žก๋„ ์ธก์ • ํ”Œ๋Ÿฌ๊ทธ์ธ)
278
+ npm install eslint-plugin-complexity
279
+
280
+ # .eslintrc.js
281
+ {
282
+ "rules": {
283
+ "complexity": ["error", 10],
284
+ "max-depth": ["error", 3],
285
+ "max-lines-per-function": ["error", 20]
286
+ }
287
+ }
288
+ ```
289
+
290
+ ### Python
291
+
292
+ ```bash
293
+ # Radon (๋ณต์žก๋„ ์ธก์ • ๋„๊ตฌ)
294
+ pip install radon
295
+
296
+ # Cyclomatic Complexity
297
+ radon cc app/ -a -nc
298
+
299
+ # Maintainability Index
300
+ radon mi app/
301
+ ```
302
+
303
+ ## ๋ชฉํ‘œ ๋ฉ”ํŠธ๋ฆญ ์š”์•ฝ
304
+
305
+ | ๋ฉ”ํŠธ๋ฆญ | ๋ชฉํ‘œ | ์„ค๋ช… |
306
+ |--------|------|------|
307
+ | Cyclomatic Complexity | โ‰ค 10 | ๋…๋ฆฝ์  ์‹คํ–‰ ๊ฒฝ๋กœ |
308
+ | Cognitive Complexity | โ‰ค 15 | ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›€ |
309
+ | Function Length | โ‰ค 20 lines | ์งง๊ณ  ์ง‘์ค‘๋œ ํ•จ์ˆ˜ |
310
+ | Nesting Depth | โ‰ค 3 levels | ํ‰ํƒ„ํ•œ ๊ตฌ์กฐ |
311
+ | Parameters | โ‰ค 5 | ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ œํ•œ |
312
+ | Dependencies | โ‰ค 7 | ๋ชจ๋“ˆ ์˜์กด์„ฑ ์ œํ•œ |