@su-record/vibe 2.4.17 โ†’ 2.4.18

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 (81) hide show
  1. package/.claude/settings.json +48 -48
  2. package/.claude/settings.local.json +28 -28
  3. package/.claude/vibe/constitution.md +184 -184
  4. package/.claude/vibe/rules/core/communication-guide.md +104 -104
  5. package/.claude/vibe/rules/core/development-philosophy.md +52 -52
  6. package/.claude/vibe/rules/core/quick-start.md +120 -120
  7. package/.claude/vibe/rules/languages/dart-flutter.md +509 -509
  8. package/.claude/vibe/rules/languages/go.md +396 -396
  9. package/.claude/vibe/rules/languages/java-spring.md +586 -586
  10. package/.claude/vibe/rules/languages/kotlin-android.md +491 -491
  11. package/.claude/vibe/rules/languages/python-django.md +371 -371
  12. package/.claude/vibe/rules/languages/python-fastapi.md +386 -386
  13. package/.claude/vibe/rules/languages/rust.md +425 -425
  14. package/.claude/vibe/rules/languages/swift-ios.md +516 -516
  15. package/.claude/vibe/rules/languages/typescript-nextjs.md +441 -441
  16. package/.claude/vibe/rules/languages/typescript-node.md +375 -375
  17. package/.claude/vibe/rules/languages/typescript-nuxt.md +521 -521
  18. package/.claude/vibe/rules/languages/typescript-react-native.md +446 -446
  19. package/.claude/vibe/rules/languages/typescript-react.md +525 -525
  20. package/.claude/vibe/rules/languages/typescript-vue.md +353 -353
  21. package/.claude/vibe/rules/quality/bdd-contract-testing.md +388 -388
  22. package/.claude/vibe/rules/quality/checklist.md +276 -276
  23. package/.claude/vibe/rules/quality/testing-strategy.md +437 -437
  24. package/.claude/vibe/rules/standards/anti-patterns.md +369 -369
  25. package/.claude/vibe/rules/standards/code-structure.md +291 -291
  26. package/.claude/vibe/rules/standards/complexity-metrics.md +312 -312
  27. package/.claude/vibe/rules/standards/naming-conventions.md +198 -198
  28. package/.claude/vibe/setup.sh +31 -31
  29. package/.claude/vibe/templates/constitution-template.md +184 -184
  30. package/.claude/vibe/templates/contract-backend-template.md +517 -517
  31. package/.claude/vibe/templates/contract-frontend-template.md +594 -594
  32. package/.claude/vibe/templates/feature-template.md +96 -96
  33. package/.claude/vibe/templates/spec-template.md +199 -199
  34. package/CLAUDE.md +333 -333
  35. package/LICENSE +21 -21
  36. package/README.md +205 -205
  37. package/agents/compounder.md +261 -261
  38. package/agents/diagrammer.md +178 -178
  39. package/agents/e2e-tester.md +266 -266
  40. package/agents/explorer.md +48 -48
  41. package/agents/implementer.md +53 -53
  42. package/agents/research/best-practices-agent.md +139 -139
  43. package/agents/research/codebase-patterns-agent.md +147 -147
  44. package/agents/research/framework-docs-agent.md +181 -181
  45. package/agents/research/security-advisory-agent.md +167 -167
  46. package/agents/review/architecture-reviewer.md +107 -107
  47. package/agents/review/complexity-reviewer.md +116 -116
  48. package/agents/review/data-integrity-reviewer.md +88 -88
  49. package/agents/review/git-history-reviewer.md +103 -103
  50. package/agents/review/performance-reviewer.md +86 -86
  51. package/agents/review/python-reviewer.md +152 -152
  52. package/agents/review/rails-reviewer.md +139 -139
  53. package/agents/review/react-reviewer.md +144 -144
  54. package/agents/review/security-reviewer.md +80 -80
  55. package/agents/review/simplicity-reviewer.md +140 -140
  56. package/agents/review/test-coverage-reviewer.md +116 -116
  57. package/agents/review/typescript-reviewer.md +127 -127
  58. package/agents/searcher.md +54 -54
  59. package/agents/simplifier.md +119 -119
  60. package/agents/tester.md +49 -49
  61. package/agents/ui-previewer.md +137 -137
  62. package/commands/vibe.analyze.md +260 -260
  63. package/commands/vibe.reason.md +223 -223
  64. package/commands/vibe.review.md +213 -213
  65. package/commands/vibe.run.md +935 -935
  66. package/commands/vibe.spec.md +442 -442
  67. package/commands/vibe.utils.md +101 -101
  68. package/commands/vibe.verify.md +282 -282
  69. package/dist/cli/collaborator.js +52 -52
  70. package/dist/cli/detect.js +32 -32
  71. package/dist/cli/index.js +0 -0
  72. package/dist/cli/llm.js +144 -144
  73. package/hooks/hooks.json +195 -195
  74. package/package.json +87 -87
  75. package/skills/context7-usage.md +82 -82
  76. package/skills/git-worktree.md +181 -181
  77. package/skills/multi-llm-orchestration.md +97 -97
  78. package/skills/parallel-research.md +77 -77
  79. package/skills/priority-todos.md +239 -239
  80. package/skills/tool-fallback.md +126 -126
  81. package/skills/vibe-capabilities.md +127 -127
@@ -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 | ๋ชจ๋“ˆ ์˜์กด์„ฑ ์ œํ•œ |