@su-record/vibe 0.1.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/LICENSE +21 -0
- package/README.md +448 -0
- package/agents/backend-python-expert.md +453 -0
- package/agents/database-postgres-expert.md +538 -0
- package/agents/frontend-flutter-expert.md +487 -0
- package/agents/frontend-react-expert.md +424 -0
- package/agents/quality-reviewer.md +542 -0
- package/agents/specification-agent.md +505 -0
- package/bin/sutory +332 -0
- package/bin/vibe +338 -0
- package/mcp/dist/__tests__/complexity.test.js +126 -0
- package/mcp/dist/__tests__/memory.test.js +120 -0
- package/mcp/dist/__tests__/python-dart-complexity.test.js +146 -0
- package/mcp/dist/index.js +230 -0
- package/mcp/dist/lib/ContextCompressor.js +305 -0
- package/mcp/dist/lib/MemoryManager.js +334 -0
- package/mcp/dist/lib/ProjectCache.js +126 -0
- package/mcp/dist/lib/PythonParser.js +241 -0
- package/mcp/dist/tools/browser/browserPool.js +76 -0
- package/mcp/dist/tools/browser/browserUtils.js +135 -0
- package/mcp/dist/tools/browser/inspectNetworkRequests.js +140 -0
- package/mcp/dist/tools/browser/monitorConsoleLogs.js +97 -0
- package/mcp/dist/tools/convention/analyzeComplexity.js +248 -0
- package/mcp/dist/tools/convention/applyQualityRules.js +102 -0
- package/mcp/dist/tools/convention/checkCouplingCohesion.js +233 -0
- package/mcp/dist/tools/convention/complexityMetrics.js +133 -0
- package/mcp/dist/tools/convention/dartComplexity.js +117 -0
- package/mcp/dist/tools/convention/getCodingGuide.js +64 -0
- package/mcp/dist/tools/convention/languageDetector.js +50 -0
- package/mcp/dist/tools/convention/pythonComplexity.js +109 -0
- package/mcp/dist/tools/convention/suggestImprovements.js +257 -0
- package/mcp/dist/tools/convention/validateCodeQuality.js +177 -0
- package/mcp/dist/tools/memory/autoSaveContext.js +79 -0
- package/mcp/dist/tools/memory/database.js +123 -0
- package/mcp/dist/tools/memory/deleteMemory.js +39 -0
- package/mcp/dist/tools/memory/listMemories.js +38 -0
- package/mcp/dist/tools/memory/memoryConfig.js +27 -0
- package/mcp/dist/tools/memory/memorySQLite.js +138 -0
- package/mcp/dist/tools/memory/memoryUtils.js +34 -0
- package/mcp/dist/tools/memory/migrate.js +113 -0
- package/mcp/dist/tools/memory/prioritizeMemory.js +109 -0
- package/mcp/dist/tools/memory/recallMemory.js +40 -0
- package/mcp/dist/tools/memory/restoreSessionContext.js +69 -0
- package/mcp/dist/tools/memory/saveMemory.js +34 -0
- package/mcp/dist/tools/memory/searchMemories.js +37 -0
- package/mcp/dist/tools/memory/startSession.js +100 -0
- package/mcp/dist/tools/memory/updateMemory.js +46 -0
- package/mcp/dist/tools/planning/analyzeRequirements.js +166 -0
- package/mcp/dist/tools/planning/createUserStories.js +119 -0
- package/mcp/dist/tools/planning/featureRoadmap.js +202 -0
- package/mcp/dist/tools/planning/generatePrd.js +156 -0
- package/mcp/dist/tools/prompt/analyzePrompt.js +145 -0
- package/mcp/dist/tools/prompt/enhancePrompt.js +105 -0
- package/mcp/dist/tools/semantic/findReferences.js +195 -0
- package/mcp/dist/tools/semantic/findSymbol.js +200 -0
- package/mcp/dist/tools/thinking/analyzeProblem.js +50 -0
- package/mcp/dist/tools/thinking/breakDownProblem.js +140 -0
- package/mcp/dist/tools/thinking/createThinkingChain.js +39 -0
- package/mcp/dist/tools/thinking/formatAsPlan.js +73 -0
- package/mcp/dist/tools/thinking/stepByStepAnalysis.js +58 -0
- package/mcp/dist/tools/thinking/thinkAloudProcess.js +75 -0
- package/mcp/dist/tools/time/getCurrentTime.js +61 -0
- package/mcp/dist/tools/ui/previewUiAscii.js +232 -0
- package/mcp/dist/types/tool.js +2 -0
- package/mcp/package.json +53 -0
- package/package.json +49 -0
- package/scripts/install-mcp.js +48 -0
- package/scripts/install.sh +70 -0
- package/skills/core/communication-guide.md +104 -0
- package/skills/core/development-philosophy.md +53 -0
- package/skills/core/quick-start.md +121 -0
- package/skills/languages/dart-flutter.md +509 -0
- package/skills/languages/python-fastapi.md +386 -0
- package/skills/languages/typescript-nextjs.md +441 -0
- package/skills/languages/typescript-react-native.md +446 -0
- package/skills/languages/typescript-react.md +525 -0
- package/skills/quality/checklist.md +276 -0
- package/skills/quality/testing-strategy.md +437 -0
- package/skills/standards/anti-patterns.md +369 -0
- package/skills/standards/code-structure.md +291 -0
- package/skills/standards/complexity-metrics.md +312 -0
- package/skills/standards/naming-conventions.md +198 -0
- package/skills/tools/mcp-hi-ai-guide.md +665 -0
- package/skills/tools/mcp-workflow.md +51 -0
- package/templates/constitution-template.md +193 -0
- package/templates/plan-template.md +237 -0
- package/templates/spec-template.md +142 -0
- package/templates/tasks-template.md +132 -0
|
@@ -0,0 +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 | 모듈 의존성 제한 |
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# 📖 자동 네이밍 규칙
|
|
2
|
+
|
|
3
|
+
## 기본 규칙
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
변수: 명사 (userList, userData)
|
|
7
|
+
함수: 동사+명사 (fetchData, updateUser)
|
|
8
|
+
이벤트: handle 접두사 (handleClick, handleSubmit)
|
|
9
|
+
Boolean: is/has/can 접두사 (isLoading, hasError, canEdit)
|
|
10
|
+
상수: UPPER_SNAKE_CASE (MAX_RETRY_COUNT, API_TIMEOUT)
|
|
11
|
+
컴포넌트: PascalCase (UserProfile, HeaderSection)
|
|
12
|
+
훅: use 접두사 (useUserData, useAuth)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 변수 네이밍
|
|
16
|
+
|
|
17
|
+
### ✅ 좋은 예
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
const userList = [...];
|
|
21
|
+
const totalAmount = 0;
|
|
22
|
+
const currentPage = 1;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### ❌ 나쁜 예
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
const list = [...]; // 무엇의 리스트?
|
|
29
|
+
const total = 0; // 무엇의 총합?
|
|
30
|
+
const page = 1; // 명확하지 않음
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 함수 네이밍
|
|
34
|
+
|
|
35
|
+
### ✅ 좋은 예
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
function fetchUserData() { }
|
|
39
|
+
function updateProfile() { }
|
|
40
|
+
function validateEmail() { }
|
|
41
|
+
function calculateTotal() { }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### ❌ 나쁜 예
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
function user() { } // 동사 없음
|
|
48
|
+
function data() { } // 불명확
|
|
49
|
+
function process() { } // 무엇을 처리?
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 이벤트 핸들러
|
|
53
|
+
|
|
54
|
+
### ✅ 좋은 예
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
function handleClick() { }
|
|
58
|
+
function handleSubmit() { }
|
|
59
|
+
function handleInputChange() { }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### ❌ 나쁜 예
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
function onClick() { } // handle 접두사 권장
|
|
66
|
+
function submit() { } // 이벤트임이 불명확
|
|
67
|
+
function change() { } // 무엇의 변경?
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Boolean 변수
|
|
71
|
+
|
|
72
|
+
### ✅ 좋은 예
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const isLoading = false;
|
|
76
|
+
const hasError = false;
|
|
77
|
+
const canEdit = true;
|
|
78
|
+
const shouldUpdate = false;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### ❌ 나쁜 예
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const loading = false; // is 접두사 권장
|
|
85
|
+
const error = false; // has 권장
|
|
86
|
+
const editable = true; // can 권장
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 상수
|
|
90
|
+
|
|
91
|
+
### ✅ 좋은 예
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const MAX_RETRY_COUNT = 3;
|
|
95
|
+
const API_TIMEOUT_MS = 5000;
|
|
96
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### ❌ 나쁜 예
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const maxRetry = 3; // UPPER_SNAKE_CASE 사용
|
|
103
|
+
const timeout = 5000; // 단위 명시 부족
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 컴포넌트 & 클래스
|
|
107
|
+
|
|
108
|
+
### ✅ 좋은 예
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
class UserProfile { }
|
|
112
|
+
class DataRepository { }
|
|
113
|
+
function ProfileCard() { }
|
|
114
|
+
function NavigationBar() { }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### ❌ 나쁜 예
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
class userProfile { } // PascalCase 사용
|
|
121
|
+
class data { } // 불명확
|
|
122
|
+
function profile() { } // PascalCase 권장
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## 커스텀 훅 (React)
|
|
126
|
+
|
|
127
|
+
### ✅ 좋은 예
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
function useUserData() { }
|
|
131
|
+
function useAuth() { }
|
|
132
|
+
function useLocalStorage() { }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### ❌ 나쁜 예
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
function getUserData() { } // use 접두사 필수
|
|
139
|
+
function auth() { } // use 접두사 필수
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 타입 & 인터페이스 (TypeScript)
|
|
143
|
+
|
|
144
|
+
### ✅ 좋은 예
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
interface User { }
|
|
148
|
+
type UserRole = 'admin' | 'user';
|
|
149
|
+
interface ApiResponse<T> { }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### ❌ 나쁜 예
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
interface IUser { } // I 접두사 불필요 (TypeScript)
|
|
156
|
+
type user = { }; // PascalCase 사용
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 파일 네이밍
|
|
160
|
+
|
|
161
|
+
### ✅ 좋은 예
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
user-profile.component.tsx
|
|
165
|
+
user.service.ts
|
|
166
|
+
auth.utils.ts
|
|
167
|
+
constants.ts
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### ❌ 나쁜 예
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
UserProfile.tsx // kebab-case 권장
|
|
174
|
+
user_service.ts // kebab-case 권장
|
|
175
|
+
utils.ts // 불명확
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 약어 사용 원칙
|
|
179
|
+
|
|
180
|
+
- 일반적인 약어만 사용 (URL, API, ID, HTML, CSS)
|
|
181
|
+
- 프로젝트 특정 약어는 문서화 필수
|
|
182
|
+
- 의미가 명확하지 않으면 전체 단어 사용
|
|
183
|
+
|
|
184
|
+
### ✅ 좋은 예
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const userId = '123';
|
|
188
|
+
const apiEndpoint = '/users';
|
|
189
|
+
const htmlContent = '<div>';
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### ❌ 나쁜 예
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const usrId = '123'; // 불명확한 약어
|
|
196
|
+
const endpt = '/users'; // 과도한 축약
|
|
197
|
+
const cnt = '<div>'; // content로 명확히
|
|
198
|
+
```
|