@su-record/vibe 2.3.0 → 2.3.2
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/.claude/settings.json +35 -35
- package/.claude/settings.local.json +24 -25
- package/.claude/vibe/constitution.md +184 -184
- package/.claude/vibe/rules/core/communication-guide.md +104 -104
- package/.claude/vibe/rules/core/development-philosophy.md +52 -52
- package/.claude/vibe/rules/core/quick-start.md +120 -120
- package/.claude/vibe/rules/languages/dart-flutter.md +509 -509
- package/.claude/vibe/rules/languages/go.md +396 -396
- package/.claude/vibe/rules/languages/java-spring.md +586 -586
- package/.claude/vibe/rules/languages/kotlin-android.md +491 -491
- package/.claude/vibe/rules/languages/python-django.md +371 -371
- package/.claude/vibe/rules/languages/python-fastapi.md +386 -386
- package/.claude/vibe/rules/languages/rust.md +425 -425
- package/.claude/vibe/rules/languages/swift-ios.md +516 -516
- package/.claude/vibe/rules/languages/typescript-nextjs.md +441 -441
- package/.claude/vibe/rules/languages/typescript-node.md +375 -375
- package/.claude/vibe/rules/languages/typescript-nuxt.md +521 -521
- package/.claude/vibe/rules/languages/typescript-react-native.md +446 -446
- package/.claude/vibe/rules/languages/typescript-react.md +525 -525
- package/.claude/vibe/rules/languages/typescript-vue.md +353 -353
- package/.claude/vibe/rules/quality/bdd-contract-testing.md +388 -388
- package/.claude/vibe/rules/quality/checklist.md +276 -276
- package/.claude/vibe/rules/quality/testing-strategy.md +437 -437
- package/.claude/vibe/rules/standards/anti-patterns.md +369 -369
- package/.claude/vibe/rules/standards/code-structure.md +291 -291
- package/.claude/vibe/rules/standards/complexity-metrics.md +312 -312
- package/.claude/vibe/rules/standards/naming-conventions.md +198 -198
- package/.claude/vibe/setup.sh +31 -31
- package/.claude/vibe/templates/constitution-template.md +184 -184
- package/.claude/vibe/templates/contract-backend-template.md +517 -517
- package/.claude/vibe/templates/contract-frontend-template.md +594 -594
- package/.claude/vibe/templates/feature-template.md +96 -96
- package/.claude/vibe/templates/spec-template.md +199 -199
- package/CLAUDE.md +345 -323
- package/LICENSE +21 -21
- package/README.md +744 -724
- package/agents/compounder.md +261 -261
- package/agents/diagrammer.md +178 -178
- package/agents/e2e-tester.md +266 -266
- package/agents/explorer.md +48 -48
- package/agents/implementer.md +53 -53
- package/agents/research/best-practices-agent.md +139 -139
- package/agents/research/codebase-patterns-agent.md +147 -147
- package/agents/research/framework-docs-agent.md +181 -181
- package/agents/research/security-advisory-agent.md +167 -167
- package/agents/review/architecture-reviewer.md +107 -107
- package/agents/review/complexity-reviewer.md +116 -116
- package/agents/review/data-integrity-reviewer.md +88 -88
- package/agents/review/git-history-reviewer.md +103 -103
- package/agents/review/performance-reviewer.md +86 -86
- package/agents/review/python-reviewer.md +152 -152
- package/agents/review/rails-reviewer.md +139 -139
- package/agents/review/react-reviewer.md +144 -144
- package/agents/review/security-reviewer.md +80 -80
- package/agents/review/simplicity-reviewer.md +140 -140
- package/agents/review/test-coverage-reviewer.md +116 -116
- package/agents/review/typescript-reviewer.md +127 -127
- package/agents/searcher.md +54 -54
- package/agents/simplifier.md +119 -119
- package/agents/tester.md +49 -49
- package/agents/ui-previewer.md +137 -137
- package/commands/vibe.analyze.md +245 -180
- package/commands/vibe.reason.md +223 -183
- package/commands/vibe.review.md +200 -136
- package/commands/vibe.run.md +838 -836
- package/commands/vibe.spec.md +419 -383
- package/commands/vibe.utils.md +101 -101
- package/commands/vibe.verify.md +282 -241
- package/dist/cli/index.js +385 -385
- package/dist/lib/MemoryManager.d.ts.map +1 -1
- package/dist/lib/MemoryManager.js +119 -114
- package/dist/lib/MemoryManager.js.map +1 -1
- package/dist/lib/PythonParser.js +108 -108
- package/dist/lib/gemini-mcp.js +15 -15
- package/dist/lib/gemini-oauth.js +35 -35
- package/dist/lib/gpt-mcp.js +17 -17
- package/dist/lib/gpt-oauth.js +44 -44
- package/dist/tools/analytics/getUsageAnalytics.js +12 -12
- package/dist/tools/index.d.ts +50 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +61 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/memory/createMemoryTimeline.js +10 -10
- package/dist/tools/memory/getMemoryGraph.js +12 -12
- package/dist/tools/memory/getSessionContext.js +9 -9
- package/dist/tools/memory/linkMemories.js +14 -14
- package/dist/tools/memory/listMemories.js +4 -4
- package/dist/tools/memory/recallMemory.js +4 -4
- package/dist/tools/memory/saveMemory.js +4 -4
- package/dist/tools/memory/searchMemoriesAdvanced.js +22 -22
- package/dist/tools/planning/generatePrd.js +46 -46
- package/dist/tools/prompt/enhancePromptGemini.js +160 -160
- package/dist/tools/reasoning/applyReasoningFramework.js +56 -56
- package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
- package/hooks/hooks.json +121 -103
- package/package.json +73 -69
- package/skills/git-worktree.md +178 -178
- package/skills/priority-todos.md +236 -236
|
@@ -1,291 +1,291 @@
|
|
|
1
|
-
# 🏗️ 코드 구조 자동화 규칙
|
|
2
|
-
|
|
3
|
-
## 컴포넌트 구조 (엄격한 순서)
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
// 1. Import 문
|
|
7
|
-
import React, { useState, useEffect } from 'react';
|
|
8
|
-
|
|
9
|
-
// 2. 타입/인터페이스 정의
|
|
10
|
-
interface Props {
|
|
11
|
-
userId: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// 3. 컴포넌트 정의
|
|
15
|
-
function UserProfile({ userId }: Props) {
|
|
16
|
-
// 4. State & Refs
|
|
17
|
-
const [user, setUser] = useState<User | null>(null);
|
|
18
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
19
|
-
|
|
20
|
-
// 5. Custom Hooks
|
|
21
|
-
const { isAuthenticated } = useAuth();
|
|
22
|
-
const { data, loading } = useUserData(userId);
|
|
23
|
-
|
|
24
|
-
// 6. Event Handlers
|
|
25
|
-
const handleSubmit = (e: FormEvent) => {
|
|
26
|
-
e.preventDefault();
|
|
27
|
-
// ...
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// 7. Effects
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
// ...
|
|
33
|
-
}, [userId]);
|
|
34
|
-
|
|
35
|
-
// 8. Early returns
|
|
36
|
-
if (loading) return <Spinner />;
|
|
37
|
-
if (!user) return <NotFound />;
|
|
38
|
-
|
|
39
|
-
// 9. Main return JSX
|
|
40
|
-
return (
|
|
41
|
-
<div>
|
|
42
|
-
{/* ... */}
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## 함수 분리 기준
|
|
49
|
-
|
|
50
|
-
### 1. 함수 길이 기준
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
// ❌ 20줄 초과 - 분리 필요
|
|
54
|
-
function processUserData(user: User) {
|
|
55
|
-
// 30줄의 복잡한 로직
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ✅ 단일 책임으로 분리
|
|
59
|
-
function processUserData(user: User) {
|
|
60
|
-
const validated = validateUser(user);
|
|
61
|
-
const transformed = transformUserData(validated);
|
|
62
|
-
return saveUserData(transformed);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function validateUser(user: User) { /* ... */ }
|
|
66
|
-
function transformUserData(user: User) { /* ... */ }
|
|
67
|
-
function saveUserData(user: User) { /* ... */ }
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 2. 컴포넌트 JSX 길이 기준
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
// ❌ JSX 50줄 초과 - 분리 필요
|
|
74
|
-
function Dashboard() {
|
|
75
|
-
return (
|
|
76
|
-
<div>
|
|
77
|
-
{/* 60줄의 복잡한 JSX */}
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ✅ 서브 컴포넌트 추출
|
|
83
|
-
function Dashboard() {
|
|
84
|
-
return (
|
|
85
|
-
<div>
|
|
86
|
-
<DashboardHeader />
|
|
87
|
-
<DashboardContent />
|
|
88
|
-
<DashboardFooter />
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function DashboardHeader() { /* ... */ }
|
|
94
|
-
function DashboardContent() { /* ... */ }
|
|
95
|
-
function DashboardFooter() { /* ... */ }
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### 3. 중첩 깊이 기준
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// ❌ 중첩 3단계 초과
|
|
102
|
-
function processData(data: Data) {
|
|
103
|
-
if (data) {
|
|
104
|
-
if (data.isValid) {
|
|
105
|
-
if (data.user) {
|
|
106
|
-
if (data.user.isActive) {
|
|
107
|
-
// 너무 깊은 중첩
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ✅ Early return으로 평탄화
|
|
115
|
-
function processData(data: Data) {
|
|
116
|
-
if (!data) return null;
|
|
117
|
-
if (!data.isValid) return null;
|
|
118
|
-
if (!data.user) return null;
|
|
119
|
-
if (!data.user.isActive) return null;
|
|
120
|
-
|
|
121
|
-
// 로직 실행
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### 4. Cyclomatic Complexity > 10
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
// ❌ 복잡도 높음 (15)
|
|
129
|
-
function calculatePrice(item: Item) {
|
|
130
|
-
let price = item.basePrice;
|
|
131
|
-
if (item.discount) price *= 0.9;
|
|
132
|
-
if (item.bulk) price *= 0.8;
|
|
133
|
-
if (item.seasonal) price *= 0.95;
|
|
134
|
-
if (item.member) price *= 0.85;
|
|
135
|
-
if (item.firstTime) price *= 0.9;
|
|
136
|
-
// ... 더 많은 조건
|
|
137
|
-
return price;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// ✅ 복잡도 감소 (3)
|
|
141
|
-
function calculatePrice(item: Item) {
|
|
142
|
-
const basePrice = item.basePrice;
|
|
143
|
-
const discounts = getApplicableDiscounts(item);
|
|
144
|
-
return applyDiscounts(basePrice, discounts);
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### 5. Cognitive Complexity > 15
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// ❌ 인지 복잡도 높음
|
|
152
|
-
function processOrder(order: Order) {
|
|
153
|
-
if (order.isPremium) {
|
|
154
|
-
for (let item of order.items) {
|
|
155
|
-
if (item.category === 'electronics') {
|
|
156
|
-
if (item.price > 1000) {
|
|
157
|
-
// 중첩된 복잡한 로직
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// ✅ 인지 복잡도 감소
|
|
165
|
-
function processOrder(order: Order) {
|
|
166
|
-
if (!order.isPremium) return;
|
|
167
|
-
|
|
168
|
-
const electronics = filterElectronics(order.items);
|
|
169
|
-
const expensive = filterExpensive(electronics);
|
|
170
|
-
|
|
171
|
-
processItems(expensive);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## 파일 구조 표준
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
// 📁 user-profile.component.tsx
|
|
179
|
-
|
|
180
|
-
// 1. Imports
|
|
181
|
-
import { ... } from 'react';
|
|
182
|
-
import { ... } from '@/lib';
|
|
183
|
-
|
|
184
|
-
// 2. Types
|
|
185
|
-
interface UserProfileProps { }
|
|
186
|
-
type UserRole = 'admin' | 'user';
|
|
187
|
-
|
|
188
|
-
// 3. Constants
|
|
189
|
-
const MAX_BIO_LENGTH = 500;
|
|
190
|
-
const DEFAULT_AVATAR = '/avatar.png';
|
|
191
|
-
|
|
192
|
-
// 4. Helper Functions (내부 전용)
|
|
193
|
-
function formatUserName(name: string) { }
|
|
194
|
-
|
|
195
|
-
// 5. Main Component
|
|
196
|
-
export function UserProfile() { }
|
|
197
|
-
|
|
198
|
-
// 6. Sub Components (export하지 않음)
|
|
199
|
-
function ProfileHeader() { }
|
|
200
|
-
function ProfileContent() { }
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## 모듈 구성 원칙
|
|
204
|
-
|
|
205
|
-
### 1. 응집도 (Cohesion)
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
// ✅ 높은 응집도 - 관련 기능만 모음
|
|
209
|
-
// 📁 user.service.ts
|
|
210
|
-
export class UserService {
|
|
211
|
-
getUser(id: string) { }
|
|
212
|
-
updateUser(id: string, data: User) { }
|
|
213
|
-
deleteUser(id: string) { }
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ❌ 낮은 응집도 - 관련 없는 기능 혼재
|
|
217
|
-
// 📁 utils.ts (안티패턴)
|
|
218
|
-
export class Utils {
|
|
219
|
-
validateEmail(email: string) { }
|
|
220
|
-
formatCurrency(amount: number) { }
|
|
221
|
-
uploadFile(file: File) { }
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 2. 결합도 (Coupling)
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// ✅ 느슨한 결합 - 인터페이스 의존
|
|
229
|
-
interface Storage {
|
|
230
|
-
save(key: string, value: unknown): void;
|
|
231
|
-
load(key: string): unknown;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
class UserService {
|
|
235
|
-
constructor(private storage: Storage) { }
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ❌ 강한 결합 - 구현체 직접 의존
|
|
239
|
-
class UserService {
|
|
240
|
-
private storage = new LocalStorage(); // 직접 의존
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## 함수 매개변수 제한
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
// ❌ 매개변수 5개 초과
|
|
248
|
-
function createUser(
|
|
249
|
-
name: string,
|
|
250
|
-
email: string,
|
|
251
|
-
age: number,
|
|
252
|
-
address: string,
|
|
253
|
-
phone: string,
|
|
254
|
-
role: string
|
|
255
|
-
) { }
|
|
256
|
-
|
|
257
|
-
// ✅ 객체로 그룹화
|
|
258
|
-
interface CreateUserParams {
|
|
259
|
-
name: string;
|
|
260
|
-
email: string;
|
|
261
|
-
age: number;
|
|
262
|
-
address: string;
|
|
263
|
-
phone: string;
|
|
264
|
-
role: string;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function createUser(params: CreateUserParams) { }
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## 순환 의존성 방지
|
|
271
|
-
|
|
272
|
-
```typescript
|
|
273
|
-
// ❌ 순환 의존성
|
|
274
|
-
// fileA.ts
|
|
275
|
-
import { funcB } from './fileB';
|
|
276
|
-
export function funcA() { funcB(); }
|
|
277
|
-
|
|
278
|
-
// fileB.ts
|
|
279
|
-
import { funcA } from './fileA'; // 순환!
|
|
280
|
-
export function funcB() { funcA(); }
|
|
281
|
-
|
|
282
|
-
// ✅ 공통 모듈 분리
|
|
283
|
-
// shared.ts
|
|
284
|
-
export function sharedFunc() { }
|
|
285
|
-
|
|
286
|
-
// fileA.ts
|
|
287
|
-
import { sharedFunc } from './shared';
|
|
288
|
-
|
|
289
|
-
// fileB.ts
|
|
290
|
-
import { sharedFunc } from './shared';
|
|
291
|
-
```
|
|
1
|
+
# 🏗️ 코드 구조 자동화 규칙
|
|
2
|
+
|
|
3
|
+
## 컴포넌트 구조 (엄격한 순서)
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// 1. Import 문
|
|
7
|
+
import React, { useState, useEffect } from 'react';
|
|
8
|
+
|
|
9
|
+
// 2. 타입/인터페이스 정의
|
|
10
|
+
interface Props {
|
|
11
|
+
userId: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 3. 컴포넌트 정의
|
|
15
|
+
function UserProfile({ userId }: Props) {
|
|
16
|
+
// 4. State & Refs
|
|
17
|
+
const [user, setUser] = useState<User | null>(null);
|
|
18
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
19
|
+
|
|
20
|
+
// 5. Custom Hooks
|
|
21
|
+
const { isAuthenticated } = useAuth();
|
|
22
|
+
const { data, loading } = useUserData(userId);
|
|
23
|
+
|
|
24
|
+
// 6. Event Handlers
|
|
25
|
+
const handleSubmit = (e: FormEvent) => {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
// ...
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// 7. Effects
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
// ...
|
|
33
|
+
}, [userId]);
|
|
34
|
+
|
|
35
|
+
// 8. Early returns
|
|
36
|
+
if (loading) return <Spinner />;
|
|
37
|
+
if (!user) return <NotFound />;
|
|
38
|
+
|
|
39
|
+
// 9. Main return JSX
|
|
40
|
+
return (
|
|
41
|
+
<div>
|
|
42
|
+
{/* ... */}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 함수 분리 기준
|
|
49
|
+
|
|
50
|
+
### 1. 함수 길이 기준
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// ❌ 20줄 초과 - 분리 필요
|
|
54
|
+
function processUserData(user: User) {
|
|
55
|
+
// 30줄의 복잡한 로직
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ✅ 단일 책임으로 분리
|
|
59
|
+
function processUserData(user: User) {
|
|
60
|
+
const validated = validateUser(user);
|
|
61
|
+
const transformed = transformUserData(validated);
|
|
62
|
+
return saveUserData(transformed);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validateUser(user: User) { /* ... */ }
|
|
66
|
+
function transformUserData(user: User) { /* ... */ }
|
|
67
|
+
function saveUserData(user: User) { /* ... */ }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. 컴포넌트 JSX 길이 기준
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// ❌ JSX 50줄 초과 - 분리 필요
|
|
74
|
+
function Dashboard() {
|
|
75
|
+
return (
|
|
76
|
+
<div>
|
|
77
|
+
{/* 60줄의 복잡한 JSX */}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ✅ 서브 컴포넌트 추출
|
|
83
|
+
function Dashboard() {
|
|
84
|
+
return (
|
|
85
|
+
<div>
|
|
86
|
+
<DashboardHeader />
|
|
87
|
+
<DashboardContent />
|
|
88
|
+
<DashboardFooter />
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DashboardHeader() { /* ... */ }
|
|
94
|
+
function DashboardContent() { /* ... */ }
|
|
95
|
+
function DashboardFooter() { /* ... */ }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. 중첩 깊이 기준
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// ❌ 중첩 3단계 초과
|
|
102
|
+
function processData(data: Data) {
|
|
103
|
+
if (data) {
|
|
104
|
+
if (data.isValid) {
|
|
105
|
+
if (data.user) {
|
|
106
|
+
if (data.user.isActive) {
|
|
107
|
+
// 너무 깊은 중첩
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ✅ Early return으로 평탄화
|
|
115
|
+
function processData(data: Data) {
|
|
116
|
+
if (!data) return null;
|
|
117
|
+
if (!data.isValid) return null;
|
|
118
|
+
if (!data.user) return null;
|
|
119
|
+
if (!data.user.isActive) return null;
|
|
120
|
+
|
|
121
|
+
// 로직 실행
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 4. Cyclomatic Complexity > 10
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// ❌ 복잡도 높음 (15)
|
|
129
|
+
function calculatePrice(item: Item) {
|
|
130
|
+
let price = item.basePrice;
|
|
131
|
+
if (item.discount) price *= 0.9;
|
|
132
|
+
if (item.bulk) price *= 0.8;
|
|
133
|
+
if (item.seasonal) price *= 0.95;
|
|
134
|
+
if (item.member) price *= 0.85;
|
|
135
|
+
if (item.firstTime) price *= 0.9;
|
|
136
|
+
// ... 더 많은 조건
|
|
137
|
+
return price;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ✅ 복잡도 감소 (3)
|
|
141
|
+
function calculatePrice(item: Item) {
|
|
142
|
+
const basePrice = item.basePrice;
|
|
143
|
+
const discounts = getApplicableDiscounts(item);
|
|
144
|
+
return applyDiscounts(basePrice, discounts);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 5. Cognitive Complexity > 15
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// ❌ 인지 복잡도 높음
|
|
152
|
+
function processOrder(order: Order) {
|
|
153
|
+
if (order.isPremium) {
|
|
154
|
+
for (let item of order.items) {
|
|
155
|
+
if (item.category === 'electronics') {
|
|
156
|
+
if (item.price > 1000) {
|
|
157
|
+
// 중첩된 복잡한 로직
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ✅ 인지 복잡도 감소
|
|
165
|
+
function processOrder(order: Order) {
|
|
166
|
+
if (!order.isPremium) return;
|
|
167
|
+
|
|
168
|
+
const electronics = filterElectronics(order.items);
|
|
169
|
+
const expensive = filterExpensive(electronics);
|
|
170
|
+
|
|
171
|
+
processItems(expensive);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## 파일 구조 표준
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// 📁 user-profile.component.tsx
|
|
179
|
+
|
|
180
|
+
// 1. Imports
|
|
181
|
+
import { ... } from 'react';
|
|
182
|
+
import { ... } from '@/lib';
|
|
183
|
+
|
|
184
|
+
// 2. Types
|
|
185
|
+
interface UserProfileProps { }
|
|
186
|
+
type UserRole = 'admin' | 'user';
|
|
187
|
+
|
|
188
|
+
// 3. Constants
|
|
189
|
+
const MAX_BIO_LENGTH = 500;
|
|
190
|
+
const DEFAULT_AVATAR = '/avatar.png';
|
|
191
|
+
|
|
192
|
+
// 4. Helper Functions (내부 전용)
|
|
193
|
+
function formatUserName(name: string) { }
|
|
194
|
+
|
|
195
|
+
// 5. Main Component
|
|
196
|
+
export function UserProfile() { }
|
|
197
|
+
|
|
198
|
+
// 6. Sub Components (export하지 않음)
|
|
199
|
+
function ProfileHeader() { }
|
|
200
|
+
function ProfileContent() { }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## 모듈 구성 원칙
|
|
204
|
+
|
|
205
|
+
### 1. 응집도 (Cohesion)
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// ✅ 높은 응집도 - 관련 기능만 모음
|
|
209
|
+
// 📁 user.service.ts
|
|
210
|
+
export class UserService {
|
|
211
|
+
getUser(id: string) { }
|
|
212
|
+
updateUser(id: string, data: User) { }
|
|
213
|
+
deleteUser(id: string) { }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ❌ 낮은 응집도 - 관련 없는 기능 혼재
|
|
217
|
+
// 📁 utils.ts (안티패턴)
|
|
218
|
+
export class Utils {
|
|
219
|
+
validateEmail(email: string) { }
|
|
220
|
+
formatCurrency(amount: number) { }
|
|
221
|
+
uploadFile(file: File) { }
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 2. 결합도 (Coupling)
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// ✅ 느슨한 결합 - 인터페이스 의존
|
|
229
|
+
interface Storage {
|
|
230
|
+
save(key: string, value: unknown): void;
|
|
231
|
+
load(key: string): unknown;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
class UserService {
|
|
235
|
+
constructor(private storage: Storage) { }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ❌ 강한 결합 - 구현체 직접 의존
|
|
239
|
+
class UserService {
|
|
240
|
+
private storage = new LocalStorage(); // 직접 의존
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## 함수 매개변수 제한
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// ❌ 매개변수 5개 초과
|
|
248
|
+
function createUser(
|
|
249
|
+
name: string,
|
|
250
|
+
email: string,
|
|
251
|
+
age: number,
|
|
252
|
+
address: string,
|
|
253
|
+
phone: string,
|
|
254
|
+
role: string
|
|
255
|
+
) { }
|
|
256
|
+
|
|
257
|
+
// ✅ 객체로 그룹화
|
|
258
|
+
interface CreateUserParams {
|
|
259
|
+
name: string;
|
|
260
|
+
email: string;
|
|
261
|
+
age: number;
|
|
262
|
+
address: string;
|
|
263
|
+
phone: string;
|
|
264
|
+
role: string;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function createUser(params: CreateUserParams) { }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 순환 의존성 방지
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// ❌ 순환 의존성
|
|
274
|
+
// fileA.ts
|
|
275
|
+
import { funcB } from './fileB';
|
|
276
|
+
export function funcA() { funcB(); }
|
|
277
|
+
|
|
278
|
+
// fileB.ts
|
|
279
|
+
import { funcA } from './fileA'; // 순환!
|
|
280
|
+
export function funcB() { funcA(); }
|
|
281
|
+
|
|
282
|
+
// ✅ 공통 모듈 분리
|
|
283
|
+
// shared.ts
|
|
284
|
+
export function sharedFunc() { }
|
|
285
|
+
|
|
286
|
+
// fileA.ts
|
|
287
|
+
import { sharedFunc } from './shared';
|
|
288
|
+
|
|
289
|
+
// fileB.ts
|
|
290
|
+
import { sharedFunc } from './shared';
|
|
291
|
+
```
|