@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,437 +1,437 @@
1
- # ๐Ÿงช AI ์‹œ๋Œ€ ํ…Œ์ŠคํŠธ ์ „๋žต
2
-
3
- ## ํ•ต์‹ฌ ์›์น™
4
-
5
- ```markdown
6
- โœ… ๋‹จ์ผ ์ฑ…์ž„ (SRP)
7
- โœ… ์ค‘๋ณต ์ฝ”๋“œ ์ œ๊ฑฐ (DRY)
8
- โœ… ์žฌ์‚ฌ์šฉ์„ฑ (Reusability)
9
- โœ… ๋‚ฎ์€ ๋ณต์žก๋„ (Low Complexity)
10
- โœ… ๊ณ„์•ฝ ์šฐ์„  ์„ค๊ณ„ (Contract-First)
11
- ```
12
-
13
- ## AI ์ฃผ๋„ ๊ฐœ๋ฐœ์—์„œ์˜ ํ…Œ์ŠคํŠธ ์šฐ์„ ์ˆœ์œ„
14
-
15
- ### 1. Contract Testing (์ตœ์šฐ์„ ) โญโญโญ
16
-
17
- **๊ฐœ๋…**: ์ฝ”๋“œ ์ž‘์„ฑ ์ „์— **ํƒ€์ž…/์Šคํ‚ค๋งˆ๋กœ ๊ณ„์•ฝ์„ ์ •์˜**
18
-
19
- **์ด์œ **: AI๊ฐ€ ๊ณ„์•ฝ์„ ๋”ฐ๋ผ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ, ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ์ž๋™ ๋ณด์žฅ๋จ
20
-
21
- #### Python (Pydantic)
22
-
23
- ```python
24
- # ๊ณ„์•ฝ ์ •์˜ (AI๊ฐ€ ์ด๋ฅผ ๋”ฐ๋ผ ๊ตฌํ˜„)
25
- from pydantic import BaseModel, Field, EmailStr
26
-
27
- class CreateUserRequest(BaseModel):
28
- """์‚ฌ์šฉ์ž ์ƒ์„ฑ ๊ณ„์•ฝ"""
29
- email: EmailStr
30
- username: str = Field(min_length=3, max_length=50)
31
- password: str = Field(min_length=8)
32
- age: int = Field(ge=0, le=150)
33
-
34
- class UserResponse(BaseModel):
35
- """์‚ฌ์šฉ์ž ์‘๋‹ต ๊ณ„์•ฝ"""
36
- id: str
37
- email: str
38
- username: str
39
- created_at: str
40
-
41
- # AI๊ฐ€ ์ด ๊ณ„์•ฝ์„ ์œ„๋ฐ˜ํ•  ์ˆ˜ ์—†์Œ (์ž๋™ ๊ฒ€์ฆ)
42
- ```
43
-
44
- #### TypeScript
45
-
46
- ```typescript
47
- // ๊ณ„์•ฝ ์ •์˜
48
- interface CreateUserRequest {
49
- email: string;
50
- username: string; // 3-50์ž
51
- password: string; // ์ตœ์†Œ 8์ž
52
- age: number; // 0-150
53
- }
54
-
55
- interface UserResponse {
56
- id: string;
57
- email: string;
58
- username: string;
59
- createdAt: string;
60
- }
61
-
62
- // Zod๋กœ ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ
63
- import { z } from 'zod';
64
-
65
- const createUserSchema = z.object({
66
- email: z.string().email(),
67
- username: z.string().min(3).max(50),
68
- password: z.string().min(8),
69
- age: z.number().min(0).max(150),
70
- });
71
- ```
72
-
73
- #### Dart (Flutter)
74
-
75
- ```dart
76
- // ๊ณ„์•ฝ ์ •์˜
77
- class CreateUserRequest {
78
- const CreateUserRequest({
79
- required this.email,
80
- required this.username,
81
- required this.password,
82
- required this.age,
83
- });
84
-
85
- final String email;
86
- final String username; // 3-50์ž
87
- final String password; // ์ตœ์†Œ 8์ž
88
- final int age; // 0-150
89
-
90
- // JSON ์ง๋ ฌํ™” (๊ณ„์•ฝ ๊ฐ•์ œ)
91
- Map<String, dynamic> toJson() => {
92
- 'email': email,
93
- 'username': username,
94
- 'password': password,
95
- 'age': age,
96
- };
97
- }
98
- ```
99
-
100
- ### 2. Integration Testing (๋†’์Œ) โญโญโญ
101
-
102
- **๊ฐœ๋…**: ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” **์‹ค์ œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ**
103
-
104
- **์ด์œ **: AI๊ฐ€ ๋†“์นœ ๋ชจ๋“ˆ ๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌ
105
-
106
- ```python
107
- # โœ… ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ: ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ํ๋ฆ„
108
- @pytest.mark.asyncio
109
- async def test_user_registration_flow():
110
- """
111
- ์‹œ๋‚˜๋ฆฌ์˜ค: ์‹ ๊ทœ ์‚ฌ์šฉ์ž ๊ฐ€์ž…
112
- 1. ์ด๋ฉ”์ผ ์ค‘๋ณต ์ฒดํฌ
113
- 2. ์‚ฌ์šฉ์ž ์ƒ์„ฑ
114
- 3. ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก
115
- 4. ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ
116
- """
117
- # Given: ์‹ ๊ทœ ์‚ฌ์šฉ์ž ์ •๋ณด
118
- request = CreateUserRequest(
119
- email="new@example.com",
120
- username="newuser",
121
- password="password123",
122
- age=25,
123
- )
124
-
125
- # When: ํšŒ์›๊ฐ€์ž… API ํ˜ธ์ถœ
126
- response = await client.post("/api/users", json=request.dict())
127
-
128
- # Then: ์‚ฌ์šฉ์ž ์ƒ์„ฑ ์„ฑ๊ณต
129
- assert response.status_code == 201
130
- data = response.json()
131
- assert data["email"] == "new@example.com"
132
-
133
- # And: ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ™•์ธ
134
- assert email_service.sent_count == 1
135
-
136
- # And: ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ ํ™•์ธ
137
- settings = await get_user_settings(data["id"])
138
- assert settings is not None
139
- ```
140
-
141
- ```typescript
142
- // โœ… ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ: React ์ปดํฌ๋„ŒํŠธ + API
143
- import { render, screen, waitFor } from '@testing-library/react';
144
- import userEvent from '@testing-library/user-event';
145
- import { UserRegistration } from './UserRegistration';
146
-
147
- test('user can register successfully', async () => {
148
- // Given: ํšŒ์›๊ฐ€์ž… ํผ ๋ Œ๋”๋ง
149
- render(<UserRegistration />);
150
-
151
- // When: ์‚ฌ์šฉ์ž๊ฐ€ ํผ ์ž…๋ ฅ
152
- await userEvent.type(screen.getByLabelText('Email'), 'new@example.com');
153
- await userEvent.type(screen.getByLabelText('Username'), 'newuser');
154
- await userEvent.type(screen.getByLabelText('Password'), 'password123');
155
- await userEvent.click(screen.getByRole('button', { name: 'Sign Up' }));
156
-
157
- // Then: ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
158
- await waitFor(() => {
159
- expect(screen.getByText('Welcome!')).toBeInTheDocument();
160
- });
161
- });
162
- ```
163
-
164
- ### 3. Property-Based Testing (์ค‘๊ฐ„) โญโญ
165
-
166
- **๊ฐœ๋…**: ์ž…๋ ฅ ๋ฒ”์œ„ ์ „์ฒด๋ฅผ **์ž๋™ ์ƒ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ**
167
-
168
- **์ด์œ **: AI๊ฐ€ ์ƒ๊ฐ ๋ชปํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ๋ฐœ๊ฒฌ
169
-
170
- ```python
171
- # โœ… Property-based testing (Hypothesis)
172
- from hypothesis import given, strategies as st
173
-
174
- @given(
175
- age=st.integers(min_value=0, max_value=150),
176
- email=st.emails(),
177
- username=st.text(min_size=3, max_size=50),
178
- )
179
- def test_user_creation_with_any_valid_input(age, email, username):
180
- """๋ชจ๋“  ์œ ํšจํ•œ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๊ฐ€๋Šฅ"""
181
- user = create_user(email=email, username=username, age=age)
182
- assert user.age == age
183
- assert user.email == email
184
- ```
185
-
186
- ```typescript
187
- // โœ… Property-based testing (fast-check)
188
- import fc from 'fast-check';
189
-
190
- test('discount calculation always returns valid percentage', () => {
191
- fc.assert(
192
- fc.property(
193
- fc.float({ min: 0, max: 10000 }), // ๊ฐ€๊ฒฉ
194
- fc.float({ min: 0, max: 1 }), // ํ• ์ธ์œจ
195
- (price, rate) => {
196
- const discount = calculateDiscount(price, rate);
197
- return discount >= 0 && discount <= price;
198
- }
199
- )
200
- );
201
- });
202
- ```
203
-
204
- ### 4. Unit Testing (๋‚ฎ์Œ, ์„ ํƒ์ ) โญ
205
-
206
- **๊ฐœ๋…**: ๊ฐœ๋ณ„ ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ ํ…Œ์ŠคํŠธ
207
-
208
- **์–ธ์ œ ์ž‘์„ฑ**: **๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ** ์„ ํƒ์ ์œผ๋กœ
209
-
210
- ```python
211
- # โœ… Unit Test: ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™
212
- def test_tier_selection_score_calculation():
213
- """
214
- ๋Œ€์žฅ๊ธˆ ์„ ๋ฐœ ์ ์ˆ˜ ๊ณ„์‚ฐ (๋ณต์žกํ•œ ๊ฐ€์ค‘์น˜)
215
- - ํ”ผ๋“œ ร—1.15
216
- - OCR ร—1.2
217
- - ์ข‹์•„์š” ร—1.0
218
- - ๋ถ๋งˆํฌ ร—1.0
219
- - ์—ฐ๊ณ„ ร—1.5
220
- """
221
- score = calculate_selection_score(
222
- feeds=10, # 10 ร— 1.15 = 11.5
223
- ocr_count=5, # 5 ร— 1.2 = 6
224
- likes=20, # 20 ร— 1.0 = 20
225
- bookmarks=8, # 8 ร— 1.0 = 8
226
- partnerships=2, # 2 ร— 1.5 = 3
227
- )
228
- assert score == 48.5
229
-
230
- # โŒ ๋ถˆํ•„์š”ํ•œ Unit Test: ๋‹จ์ˆœ CRUD
231
- def test_get_user_by_id():
232
- """Integration Test๋กœ ์ถฉ๋ถ„"""
233
- user = get_user("user-123")
234
- assert user.id == "user-123" # ์˜๋ฏธ ์—†์Œ
235
- ```
236
-
237
- ### 5. E2E Testing (์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ) โญโญ
238
-
239
- **๊ฐœ๋…**: ์‚ฌ์šฉ์ž ๊ด€์ ์˜ ์ „์ฒด ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ
240
-
241
- **์–ธ์ œ**: ์ฃผ์š” ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ๋งŒ ์„ ํƒ์ ์œผ๋กœ
242
-
243
- ```typescript
244
- // โœ… E2E Test: Playwright/Cypress
245
- test('user can complete full registration flow', async ({ page }) => {
246
- // 1. ํ™ˆํŽ˜์ด์ง€ ์ ‘์†
247
- await page.goto('https://app.example.com');
248
-
249
- // 2. ํšŒ์›๊ฐ€์ž… ํด๋ฆญ
250
- await page.click('text=Sign Up');
251
-
252
- // 3. ํผ ์ž…๋ ฅ
253
- await page.fill('input[name="email"]', 'test@example.com');
254
- await page.fill('input[name="username"]', 'testuser');
255
- await page.fill('input[name="password"]', 'password123');
256
-
257
- // 4. ์ œ์ถœ
258
- await page.click('button[type="submit"]');
259
-
260
- // 5. ๋Œ€์‹œ๋ณด๋“œ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ™•์ธ
261
- await expect(page).toHaveURL('/dashboard');
262
- await expect(page.locator('h1')).toContainText('Welcome, testuser!');
263
- });
264
- ```
265
-
266
- ## ํ…Œ์ŠคํŠธ ์šฐ์„ ์ˆœ์œ„ ๊ฒฐ์ • ํŠธ๋ฆฌ
267
-
268
- ```
269
- ์ƒˆ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ์‹œ:
270
-
271
- 1. Contract ์ •์˜ํ–ˆ๋Š”๊ฐ€?
272
- No โ†’ Contract ๋จผ์ € ์ž‘์„ฑ (Pydantic/Zod/Dart class)
273
- Yes โ†’ โฌ‡๏ธ
274
-
275
- 2. ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด ํ˜‘๋ ฅํ•˜๋Š”๊ฐ€?
276
- Yes โ†’ Integration Test ์ž‘์„ฑ โญโญโญ
277
- No โ†’ โฌ‡๏ธ
278
-
279
- 3. ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ธ๊ฐ€? (๋ณต์žก๋„ > 10)
280
- Yes โ†’ Unit Test ์ž‘์„ฑ โญ
281
- No โ†’ โฌ‡๏ธ
282
-
283
- 4. ํ•ต์‹ฌ ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ์ธ๊ฐ€?
284
- Yes โ†’ E2E Test ์ž‘์„ฑ โญโญ
285
- No โ†’ ์™„๋ฃŒ โœ…
286
- ```
287
-
288
- ## AI ์‹œ๋Œ€์˜ TDD ๋Œ€์•ˆ: ATDD (AI-Test-Driven Development)
289
-
290
- ```markdown
291
- # ์ƒˆ๋กœ์šด ๊ฐœ๋ฐœ ํ๋ฆ„
292
-
293
- 1. **์š”๊ตฌ์‚ฌํ•ญ ๋ช…ํ™•ํ™”** (๊ฐœ๋ฐœ์ž)
294
- "ํ”„๋ฆฌ๋ฏธ์—„ ์‚ฌ์šฉ์ž๋Š” 10% ํ• ์ธ์„ ๋ฐ›๋Š”๋‹ค"
295
-
296
- 2. **Contract ์ •์˜** (๊ฐœ๋ฐœ์ž)
297
- interface DiscountRequest {
298
- userId: string;
299
- orderTotal: number;
300
- }
301
-
302
- interface DiscountResponse {
303
- originalPrice: number;
304
- discountedPrice: number;
305
- discountRate: number;
306
- }
307
-
308
- 3. **ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ์ž‘์„ฑ** (๊ฐœ๋ฐœ์ž or AI)
309
- test('premium user gets 10% discount', () => {
310
- // Given: ํ”„๋ฆฌ๋ฏธ์—„ ์œ ์ €, 100์› ์ฃผ๋ฌธ
311
- // When: ํ• ์ธ ๊ณ„์‚ฐ
312
- // Then: 90์› (10% ํ• ์ธ)
313
- })
314
-
315
- 4. **AI๊ฐ€ ๊ตฌํ˜„** (AI)
316
- - Contract๋ฅผ ๋”ฐ๋ผ ์ฝ”๋“œ ์ƒ์„ฑ
317
- - ํ…Œ์ŠคํŠธ ํ†ต๊ณผํ•˜๋Š” ์ฝ”๋“œ ์ž‘์„ฑ
318
-
319
- 5. **ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ** (์ž๋™)
320
- - CI/CD์—์„œ ์ „์ฒด ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ
321
-
322
- 6. **๋ฆฌํŒฉํ† ๋ง** (AI + ๊ฐœ๋ฐœ์ž)
323
- - ๋ณต์žก๋„, ์ค‘๋ณต ์ œ๊ฑฐ
324
- - SRP ์ค€์ˆ˜ ํ™•์ธ
325
- ```
326
-
327
- ## ์–ธ์–ด๋ณ„ ๋„๊ตฌ
328
-
329
- ### Python
330
- ```bash
331
- # Contract Testing
332
- pip install pydantic
333
-
334
- # Integration Testing
335
- pip install pytest pytest-asyncio httpx
336
-
337
- # Property-Based Testing
338
- pip install hypothesis
339
-
340
- # Coverage
341
- pip install pytest-cov
342
- ```
343
-
344
- ### TypeScript/JavaScript
345
- ```bash
346
- # Contract Testing
347
- npm install zod
348
-
349
- # Integration Testing
350
- npm install @testing-library/react @testing-library/user-event
351
-
352
- # Property-Based Testing
353
- npm install fast-check
354
-
355
- # E2E Testing
356
- npm install playwright
357
- ```
358
-
359
- ### Dart/Flutter
360
- ```bash
361
- # Integration Testing
362
- flutter pub add integration_test
363
-
364
- # Widget Testing
365
- flutter test
366
-
367
- # E2E Testing (Flutter Driver)
368
- flutter drive --target=test_driver/app.dart
369
- ```
370
-
371
- ## ์•ˆํ‹ฐํŒจํ„ด
372
-
373
- ```python
374
- # โŒ ๊ตฌํ˜„ ์„ธ๋ถ€์‚ฌํ•ญ ํ…Œ์ŠคํŠธ (๊นจ์ง€๊ธฐ ์‰ฌ์›€)
375
- def test_internal_cache_structure():
376
- service = UserService()
377
- assert service._cache == {} # ๋‚ด๋ถ€ ๊ตฌํ˜„์— ์˜์กด
378
-
379
- # โœ… ๊ณต๊ฐœ API ํ…Œ์ŠคํŠธ (๊ฒฌ๊ณ ํ•จ)
380
- def test_user_data_is_cached_after_first_call():
381
- service = UserService()
382
- user1 = service.get_user("123")
383
- user2 = service.get_user("123")
384
- assert user1 is user2 # ๋™์ž‘๋งŒ ๊ฒ€์ฆ
385
- ```
386
-
387
- ```typescript
388
- // โŒ ๋ชจ๋“  ํ•จ์ˆ˜์— Unit Test (๊ณผ๋„ํ•จ)
389
- test('add function adds two numbers', () => {
390
- expect(add(1, 2)).toBe(3); // ์˜๋ฏธ ์—†์Œ
391
- });
392
-
393
- // โœ… ๋ณต์žกํ•œ ๋กœ์ง๋งŒ ํ…Œ์ŠคํŠธ
394
- test('calculate shipping cost with multiple conditions', () => {
395
- const cost = calculateShipping({
396
- weight: 10,
397
- distance: 500,
398
- isPremium: true,
399
- isExpress: false,
400
- });
401
- expect(cost).toBe(45); // ๋ณต์žกํ•œ ๊ทœ์น™ ๊ฒ€์ฆ
402
- });
403
- ```
404
-
405
- ## ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ชฉํ‘œ
406
-
407
- ```markdown
408
- # ํ˜„์‹ค์ ์ธ ๋ชฉํ‘œ
409
-
410
- - Contract Coverage: 100% (๋ชจ๋“  API๋Š” ์Šคํ‚ค๋งˆ ์ •์˜)
411
- - Integration Coverage: 80% (์ฃผ์š” ๋น„์ฆˆ๋‹ˆ์Šค ํ๋ฆ„)
412
- - Unit Coverage: ์„ ํƒ์  (๋ณต์žกํ•œ ๋กœ์ง๋งŒ)
413
- - E2E Coverage: 20-30% (ํ•ต์‹ฌ ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ)
414
-
415
- # โŒ ํ”ผํ•ด์•ผ ํ•  ๊ฒƒ
416
- - 100% Unit Test Coverage (์‹œ๊ฐ„ ๋‚ญ๋น„)
417
- - ๋‹จ์ˆœ CRUD์— Unit Test (Integration์œผ๋กœ ์ถฉ๋ถ„)
418
- - ๋ชจ๋“  ์—ฃ์ง€ ์ผ€์ด์Šค ์ˆ˜๋™ ํ…Œ์ŠคํŠธ (Property-based ์‚ฌ์šฉ)
419
- ```
420
-
421
- ## ํ•ต์‹ฌ ์š”์•ฝ
422
-
423
- ```markdown
424
- AI ์‹œ๋Œ€ ํ…Œ์ŠคํŠธ ์ „๋žต:
425
-
426
- 1. โœ… Contract-First (ํƒ€์ž…/์Šคํ‚ค๋งˆ ๋จผ์ €)
427
- 2. โœ… Integration Testing (์‹ค์ œ ์‹œ๋‚˜๋ฆฌ์˜ค)
428
- 3. โš ๏ธ Unit Testing (๋ณต์žกํ•œ ๋กœ์ง๋งŒ)
429
- 4. โŒ ์ „ํ†ต์  TDD (AI ์‹œ๋Œ€์—” ๋น„ํšจ์œจ)
430
-
431
- ๋ชฉํ‘œ:
432
- - ๋‹จ์ผ ์ฑ…์ž„ (SRP)
433
- - ์ค‘๋ณต ์ œ๊ฑฐ (DRY)
434
- - ์žฌ์‚ฌ์šฉ์„ฑ
435
- - ๋‚ฎ์€ ๋ณต์žก๋„
436
- - ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ
437
- ```
1
+ # ๐Ÿงช AI ์‹œ๋Œ€ ํ…Œ์ŠคํŠธ ์ „๋žต
2
+
3
+ ## ํ•ต์‹ฌ ์›์น™
4
+
5
+ ```markdown
6
+ โœ… ๋‹จ์ผ ์ฑ…์ž„ (SRP)
7
+ โœ… ์ค‘๋ณต ์ฝ”๋“œ ์ œ๊ฑฐ (DRY)
8
+ โœ… ์žฌ์‚ฌ์šฉ์„ฑ (Reusability)
9
+ โœ… ๋‚ฎ์€ ๋ณต์žก๋„ (Low Complexity)
10
+ โœ… ๊ณ„์•ฝ ์šฐ์„  ์„ค๊ณ„ (Contract-First)
11
+ ```
12
+
13
+ ## AI ์ฃผ๋„ ๊ฐœ๋ฐœ์—์„œ์˜ ํ…Œ์ŠคํŠธ ์šฐ์„ ์ˆœ์œ„
14
+
15
+ ### 1. Contract Testing (์ตœ์šฐ์„ ) โญโญโญ
16
+
17
+ **๊ฐœ๋…**: ์ฝ”๋“œ ์ž‘์„ฑ ์ „์— **ํƒ€์ž…/์Šคํ‚ค๋งˆ๋กœ ๊ณ„์•ฝ์„ ์ •์˜**
18
+
19
+ **์ด์œ **: AI๊ฐ€ ๊ณ„์•ฝ์„ ๋”ฐ๋ผ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ, ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ์ž๋™ ๋ณด์žฅ๋จ
20
+
21
+ #### Python (Pydantic)
22
+
23
+ ```python
24
+ # ๊ณ„์•ฝ ์ •์˜ (AI๊ฐ€ ์ด๋ฅผ ๋”ฐ๋ผ ๊ตฌํ˜„)
25
+ from pydantic import BaseModel, Field, EmailStr
26
+
27
+ class CreateUserRequest(BaseModel):
28
+ """์‚ฌ์šฉ์ž ์ƒ์„ฑ ๊ณ„์•ฝ"""
29
+ email: EmailStr
30
+ username: str = Field(min_length=3, max_length=50)
31
+ password: str = Field(min_length=8)
32
+ age: int = Field(ge=0, le=150)
33
+
34
+ class UserResponse(BaseModel):
35
+ """์‚ฌ์šฉ์ž ์‘๋‹ต ๊ณ„์•ฝ"""
36
+ id: str
37
+ email: str
38
+ username: str
39
+ created_at: str
40
+
41
+ # AI๊ฐ€ ์ด ๊ณ„์•ฝ์„ ์œ„๋ฐ˜ํ•  ์ˆ˜ ์—†์Œ (์ž๋™ ๊ฒ€์ฆ)
42
+ ```
43
+
44
+ #### TypeScript
45
+
46
+ ```typescript
47
+ // ๊ณ„์•ฝ ์ •์˜
48
+ interface CreateUserRequest {
49
+ email: string;
50
+ username: string; // 3-50์ž
51
+ password: string; // ์ตœ์†Œ 8์ž
52
+ age: number; // 0-150
53
+ }
54
+
55
+ interface UserResponse {
56
+ id: string;
57
+ email: string;
58
+ username: string;
59
+ createdAt: string;
60
+ }
61
+
62
+ // Zod๋กœ ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ
63
+ import { z } from 'zod';
64
+
65
+ const createUserSchema = z.object({
66
+ email: z.string().email(),
67
+ username: z.string().min(3).max(50),
68
+ password: z.string().min(8),
69
+ age: z.number().min(0).max(150),
70
+ });
71
+ ```
72
+
73
+ #### Dart (Flutter)
74
+
75
+ ```dart
76
+ // ๊ณ„์•ฝ ์ •์˜
77
+ class CreateUserRequest {
78
+ const CreateUserRequest({
79
+ required this.email,
80
+ required this.username,
81
+ required this.password,
82
+ required this.age,
83
+ });
84
+
85
+ final String email;
86
+ final String username; // 3-50์ž
87
+ final String password; // ์ตœ์†Œ 8์ž
88
+ final int age; // 0-150
89
+
90
+ // JSON ์ง๋ ฌํ™” (๊ณ„์•ฝ ๊ฐ•์ œ)
91
+ Map<String, dynamic> toJson() => {
92
+ 'email': email,
93
+ 'username': username,
94
+ 'password': password,
95
+ 'age': age,
96
+ };
97
+ }
98
+ ```
99
+
100
+ ### 2. Integration Testing (๋†’์Œ) โญโญโญ
101
+
102
+ **๊ฐœ๋…**: ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” **์‹ค์ œ ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ**
103
+
104
+ **์ด์œ **: AI๊ฐ€ ๋†“์นœ ๋ชจ๋“ˆ ๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌ
105
+
106
+ ```python
107
+ # โœ… ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ: ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ํ๋ฆ„
108
+ @pytest.mark.asyncio
109
+ async def test_user_registration_flow():
110
+ """
111
+ ์‹œ๋‚˜๋ฆฌ์˜ค: ์‹ ๊ทœ ์‚ฌ์šฉ์ž ๊ฐ€์ž…
112
+ 1. ์ด๋ฉ”์ผ ์ค‘๋ณต ์ฒดํฌ
113
+ 2. ์‚ฌ์šฉ์ž ์ƒ์„ฑ
114
+ 3. ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก
115
+ 4. ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ
116
+ """
117
+ # Given: ์‹ ๊ทœ ์‚ฌ์šฉ์ž ์ •๋ณด
118
+ request = CreateUserRequest(
119
+ email="new@example.com",
120
+ username="newuser",
121
+ password="password123",
122
+ age=25,
123
+ )
124
+
125
+ # When: ํšŒ์›๊ฐ€์ž… API ํ˜ธ์ถœ
126
+ response = await client.post("/api/users", json=request.dict())
127
+
128
+ # Then: ์‚ฌ์šฉ์ž ์ƒ์„ฑ ์„ฑ๊ณต
129
+ assert response.status_code == 201
130
+ data = response.json()
131
+ assert data["email"] == "new@example.com"
132
+
133
+ # And: ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก ํ™•์ธ
134
+ assert email_service.sent_count == 1
135
+
136
+ # And: ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ ํ™•์ธ
137
+ settings = await get_user_settings(data["id"])
138
+ assert settings is not None
139
+ ```
140
+
141
+ ```typescript
142
+ // โœ… ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ: React ์ปดํฌ๋„ŒํŠธ + API
143
+ import { render, screen, waitFor } from '@testing-library/react';
144
+ import userEvent from '@testing-library/user-event';
145
+ import { UserRegistration } from './UserRegistration';
146
+
147
+ test('user can register successfully', async () => {
148
+ // Given: ํšŒ์›๊ฐ€์ž… ํผ ๋ Œ๋”๋ง
149
+ render(<UserRegistration />);
150
+
151
+ // When: ์‚ฌ์šฉ์ž๊ฐ€ ํผ ์ž…๋ ฅ
152
+ await userEvent.type(screen.getByLabelText('Email'), 'new@example.com');
153
+ await userEvent.type(screen.getByLabelText('Username'), 'newuser');
154
+ await userEvent.type(screen.getByLabelText('Password'), 'password123');
155
+ await userEvent.click(screen.getByRole('button', { name: 'Sign Up' }));
156
+
157
+ // Then: ์„ฑ๊ณต ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
158
+ await waitFor(() => {
159
+ expect(screen.getByText('Welcome!')).toBeInTheDocument();
160
+ });
161
+ });
162
+ ```
163
+
164
+ ### 3. Property-Based Testing (์ค‘๊ฐ„) โญโญ
165
+
166
+ **๊ฐœ๋…**: ์ž…๋ ฅ ๋ฒ”์œ„ ์ „์ฒด๋ฅผ **์ž๋™ ์ƒ์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ**
167
+
168
+ **์ด์œ **: AI๊ฐ€ ์ƒ๊ฐ ๋ชปํ•œ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ๋ฐœ๊ฒฌ
169
+
170
+ ```python
171
+ # โœ… Property-based testing (Hypothesis)
172
+ from hypothesis import given, strategies as st
173
+
174
+ @given(
175
+ age=st.integers(min_value=0, max_value=150),
176
+ email=st.emails(),
177
+ username=st.text(min_size=3, max_size=50),
178
+ )
179
+ def test_user_creation_with_any_valid_input(age, email, username):
180
+ """๋ชจ๋“  ์œ ํšจํ•œ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ์ž ์ƒ์„ฑ ๊ฐ€๋Šฅ"""
181
+ user = create_user(email=email, username=username, age=age)
182
+ assert user.age == age
183
+ assert user.email == email
184
+ ```
185
+
186
+ ```typescript
187
+ // โœ… Property-based testing (fast-check)
188
+ import fc from 'fast-check';
189
+
190
+ test('discount calculation always returns valid percentage', () => {
191
+ fc.assert(
192
+ fc.property(
193
+ fc.float({ min: 0, max: 10000 }), // ๊ฐ€๊ฒฉ
194
+ fc.float({ min: 0, max: 1 }), // ํ• ์ธ์œจ
195
+ (price, rate) => {
196
+ const discount = calculateDiscount(price, rate);
197
+ return discount >= 0 && discount <= price;
198
+ }
199
+ )
200
+ );
201
+ });
202
+ ```
203
+
204
+ ### 4. Unit Testing (๋‚ฎ์Œ, ์„ ํƒ์ ) โญ
205
+
206
+ **๊ฐœ๋…**: ๊ฐœ๋ณ„ ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ ํ…Œ์ŠคํŠธ
207
+
208
+ **์–ธ์ œ ์ž‘์„ฑ**: **๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ** ์„ ํƒ์ ์œผ๋กœ
209
+
210
+ ```python
211
+ # โœ… Unit Test: ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™
212
+ def test_tier_selection_score_calculation():
213
+ """
214
+ ๋Œ€์žฅ๊ธˆ ์„ ๋ฐœ ์ ์ˆ˜ ๊ณ„์‚ฐ (๋ณต์žกํ•œ ๊ฐ€์ค‘์น˜)
215
+ - ํ”ผ๋“œ ร—1.15
216
+ - OCR ร—1.2
217
+ - ์ข‹์•„์š” ร—1.0
218
+ - ๋ถ๋งˆํฌ ร—1.0
219
+ - ์—ฐ๊ณ„ ร—1.5
220
+ """
221
+ score = calculate_selection_score(
222
+ feeds=10, # 10 ร— 1.15 = 11.5
223
+ ocr_count=5, # 5 ร— 1.2 = 6
224
+ likes=20, # 20 ร— 1.0 = 20
225
+ bookmarks=8, # 8 ร— 1.0 = 8
226
+ partnerships=2, # 2 ร— 1.5 = 3
227
+ )
228
+ assert score == 48.5
229
+
230
+ # โŒ ๋ถˆํ•„์š”ํ•œ Unit Test: ๋‹จ์ˆœ CRUD
231
+ def test_get_user_by_id():
232
+ """Integration Test๋กœ ์ถฉ๋ถ„"""
233
+ user = get_user("user-123")
234
+ assert user.id == "user-123" # ์˜๋ฏธ ์—†์Œ
235
+ ```
236
+
237
+ ### 5. E2E Testing (์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ) โญโญ
238
+
239
+ **๊ฐœ๋…**: ์‚ฌ์šฉ์ž ๊ด€์ ์˜ ์ „์ฒด ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ
240
+
241
+ **์–ธ์ œ**: ์ฃผ์š” ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ๋งŒ ์„ ํƒ์ ์œผ๋กœ
242
+
243
+ ```typescript
244
+ // โœ… E2E Test: Playwright/Cypress
245
+ test('user can complete full registration flow', async ({ page }) => {
246
+ // 1. ํ™ˆํŽ˜์ด์ง€ ์ ‘์†
247
+ await page.goto('https://app.example.com');
248
+
249
+ // 2. ํšŒ์›๊ฐ€์ž… ํด๋ฆญ
250
+ await page.click('text=Sign Up');
251
+
252
+ // 3. ํผ ์ž…๋ ฅ
253
+ await page.fill('input[name="email"]', 'test@example.com');
254
+ await page.fill('input[name="username"]', 'testuser');
255
+ await page.fill('input[name="password"]', 'password123');
256
+
257
+ // 4. ์ œ์ถœ
258
+ await page.click('button[type="submit"]');
259
+
260
+ // 5. ๋Œ€์‹œ๋ณด๋“œ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ํ™•์ธ
261
+ await expect(page).toHaveURL('/dashboard');
262
+ await expect(page.locator('h1')).toContainText('Welcome, testuser!');
263
+ });
264
+ ```
265
+
266
+ ## ํ…Œ์ŠคํŠธ ์šฐ์„ ์ˆœ์œ„ ๊ฒฐ์ • ํŠธ๋ฆฌ
267
+
268
+ ```
269
+ ์ƒˆ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ์‹œ:
270
+
271
+ 1. Contract ์ •์˜ํ–ˆ๋Š”๊ฐ€?
272
+ No โ†’ Contract ๋จผ์ € ์ž‘์„ฑ (Pydantic/Zod/Dart class)
273
+ Yes โ†’ โฌ‡๏ธ
274
+
275
+ 2. ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด ํ˜‘๋ ฅํ•˜๋Š”๊ฐ€?
276
+ Yes โ†’ Integration Test ์ž‘์„ฑ โญโญโญ
277
+ No โ†’ โฌ‡๏ธ
278
+
279
+ 3. ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ธ๊ฐ€? (๋ณต์žก๋„ > 10)
280
+ Yes โ†’ Unit Test ์ž‘์„ฑ โญ
281
+ No โ†’ โฌ‡๏ธ
282
+
283
+ 4. ํ•ต์‹ฌ ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ์ธ๊ฐ€?
284
+ Yes โ†’ E2E Test ์ž‘์„ฑ โญโญ
285
+ No โ†’ ์™„๋ฃŒ โœ…
286
+ ```
287
+
288
+ ## AI ์‹œ๋Œ€์˜ TDD ๋Œ€์•ˆ: ATDD (AI-Test-Driven Development)
289
+
290
+ ```markdown
291
+ # ์ƒˆ๋กœ์šด ๊ฐœ๋ฐœ ํ๋ฆ„
292
+
293
+ 1. **์š”๊ตฌ์‚ฌํ•ญ ๋ช…ํ™•ํ™”** (๊ฐœ๋ฐœ์ž)
294
+ "ํ”„๋ฆฌ๋ฏธ์—„ ์‚ฌ์šฉ์ž๋Š” 10% ํ• ์ธ์„ ๋ฐ›๋Š”๋‹ค"
295
+
296
+ 2. **Contract ์ •์˜** (๊ฐœ๋ฐœ์ž)
297
+ interface DiscountRequest {
298
+ userId: string;
299
+ orderTotal: number;
300
+ }
301
+
302
+ interface DiscountResponse {
303
+ originalPrice: number;
304
+ discountedPrice: number;
305
+ discountRate: number;
306
+ }
307
+
308
+ 3. **ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ์ž‘์„ฑ** (๊ฐœ๋ฐœ์ž or AI)
309
+ test('premium user gets 10% discount', () => {
310
+ // Given: ํ”„๋ฆฌ๋ฏธ์—„ ์œ ์ €, 100์› ์ฃผ๋ฌธ
311
+ // When: ํ• ์ธ ๊ณ„์‚ฐ
312
+ // Then: 90์› (10% ํ• ์ธ)
313
+ })
314
+
315
+ 4. **AI๊ฐ€ ๊ตฌํ˜„** (AI)
316
+ - Contract๋ฅผ ๋”ฐ๋ผ ์ฝ”๋“œ ์ƒ์„ฑ
317
+ - ํ…Œ์ŠคํŠธ ํ†ต๊ณผํ•˜๋Š” ์ฝ”๋“œ ์ž‘์„ฑ
318
+
319
+ 5. **ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ** (์ž๋™)
320
+ - CI/CD์—์„œ ์ „์ฒด ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ
321
+
322
+ 6. **๋ฆฌํŒฉํ† ๋ง** (AI + ๊ฐœ๋ฐœ์ž)
323
+ - ๋ณต์žก๋„, ์ค‘๋ณต ์ œ๊ฑฐ
324
+ - SRP ์ค€์ˆ˜ ํ™•์ธ
325
+ ```
326
+
327
+ ## ์–ธ์–ด๋ณ„ ๋„๊ตฌ
328
+
329
+ ### Python
330
+ ```bash
331
+ # Contract Testing
332
+ pip install pydantic
333
+
334
+ # Integration Testing
335
+ pip install pytest pytest-asyncio httpx
336
+
337
+ # Property-Based Testing
338
+ pip install hypothesis
339
+
340
+ # Coverage
341
+ pip install pytest-cov
342
+ ```
343
+
344
+ ### TypeScript/JavaScript
345
+ ```bash
346
+ # Contract Testing
347
+ npm install zod
348
+
349
+ # Integration Testing
350
+ npm install @testing-library/react @testing-library/user-event
351
+
352
+ # Property-Based Testing
353
+ npm install fast-check
354
+
355
+ # E2E Testing
356
+ npm install playwright
357
+ ```
358
+
359
+ ### Dart/Flutter
360
+ ```bash
361
+ # Integration Testing
362
+ flutter pub add integration_test
363
+
364
+ # Widget Testing
365
+ flutter test
366
+
367
+ # E2E Testing (Flutter Driver)
368
+ flutter drive --target=test_driver/app.dart
369
+ ```
370
+
371
+ ## ์•ˆํ‹ฐํŒจํ„ด
372
+
373
+ ```python
374
+ # โŒ ๊ตฌํ˜„ ์„ธ๋ถ€์‚ฌํ•ญ ํ…Œ์ŠคํŠธ (๊นจ์ง€๊ธฐ ์‰ฌ์›€)
375
+ def test_internal_cache_structure():
376
+ service = UserService()
377
+ assert service._cache == {} # ๋‚ด๋ถ€ ๊ตฌํ˜„์— ์˜์กด
378
+
379
+ # โœ… ๊ณต๊ฐœ API ํ…Œ์ŠคํŠธ (๊ฒฌ๊ณ ํ•จ)
380
+ def test_user_data_is_cached_after_first_call():
381
+ service = UserService()
382
+ user1 = service.get_user("123")
383
+ user2 = service.get_user("123")
384
+ assert user1 is user2 # ๋™์ž‘๋งŒ ๊ฒ€์ฆ
385
+ ```
386
+
387
+ ```typescript
388
+ // โŒ ๋ชจ๋“  ํ•จ์ˆ˜์— Unit Test (๊ณผ๋„ํ•จ)
389
+ test('add function adds two numbers', () => {
390
+ expect(add(1, 2)).toBe(3); // ์˜๋ฏธ ์—†์Œ
391
+ });
392
+
393
+ // โœ… ๋ณต์žกํ•œ ๋กœ์ง๋งŒ ํ…Œ์ŠคํŠธ
394
+ test('calculate shipping cost with multiple conditions', () => {
395
+ const cost = calculateShipping({
396
+ weight: 10,
397
+ distance: 500,
398
+ isPremium: true,
399
+ isExpress: false,
400
+ });
401
+ expect(cost).toBe(45); // ๋ณต์žกํ•œ ๊ทœ์น™ ๊ฒ€์ฆ
402
+ });
403
+ ```
404
+
405
+ ## ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ชฉํ‘œ
406
+
407
+ ```markdown
408
+ # ํ˜„์‹ค์ ์ธ ๋ชฉํ‘œ
409
+
410
+ - Contract Coverage: 100% (๋ชจ๋“  API๋Š” ์Šคํ‚ค๋งˆ ์ •์˜)
411
+ - Integration Coverage: 80% (์ฃผ์š” ๋น„์ฆˆ๋‹ˆ์Šค ํ๋ฆ„)
412
+ - Unit Coverage: ์„ ํƒ์  (๋ณต์žกํ•œ ๋กœ์ง๋งŒ)
413
+ - E2E Coverage: 20-30% (ํ•ต์‹ฌ ์‚ฌ์šฉ์ž ํ”Œ๋กœ์šฐ)
414
+
415
+ # โŒ ํ”ผํ•ด์•ผ ํ•  ๊ฒƒ
416
+ - 100% Unit Test Coverage (์‹œ๊ฐ„ ๋‚ญ๋น„)
417
+ - ๋‹จ์ˆœ CRUD์— Unit Test (Integration์œผ๋กœ ์ถฉ๋ถ„)
418
+ - ๋ชจ๋“  ์—ฃ์ง€ ์ผ€์ด์Šค ์ˆ˜๋™ ํ…Œ์ŠคํŠธ (Property-based ์‚ฌ์šฉ)
419
+ ```
420
+
421
+ ## ํ•ต์‹ฌ ์š”์•ฝ
422
+
423
+ ```markdown
424
+ AI ์‹œ๋Œ€ ํ…Œ์ŠคํŠธ ์ „๋žต:
425
+
426
+ 1. โœ… Contract-First (ํƒ€์ž…/์Šคํ‚ค๋งˆ ๋จผ์ €)
427
+ 2. โœ… Integration Testing (์‹ค์ œ ์‹œ๋‚˜๋ฆฌ์˜ค)
428
+ 3. โš ๏ธ Unit Testing (๋ณต์žกํ•œ ๋กœ์ง๋งŒ)
429
+ 4. โŒ ์ „ํ†ต์  TDD (AI ์‹œ๋Œ€์—” ๋น„ํšจ์œจ)
430
+
431
+ ๋ชฉํ‘œ:
432
+ - ๋‹จ์ผ ์ฑ…์ž„ (SRP)
433
+ - ์ค‘๋ณต ์ œ๊ฑฐ (DRY)
434
+ - ์žฌ์‚ฌ์šฉ์„ฑ
435
+ - ๋‚ฎ์€ ๋ณต์žก๋„
436
+ - ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ
437
+ ```