@su-record/vibe 0.4.6 → 0.4.7

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.
@@ -0,0 +1,388 @@
1
+ # BDD + Contract Testing Guide
2
+
3
+ **AI 주도 개발에 최적화된 테스팅 전략**
4
+
5
+ ---
6
+
7
+ ## 개요
8
+
9
+ BDD (Behavior-Driven Development)와 Contract Testing은 **AI 에이전트가 요구사항을 정확히 이해하고 검증 가능한 코드를 생성**하는 데 필수적입니다.
10
+
11
+ ### 왜 AI 주도 개발에 유용한가?
12
+
13
+ 1. **명확한 입출력 계약** → AI가 정확히 구현 가능
14
+ 2. **자동 검증** → AI 생성 코드의 품질 즉시 확인
15
+ 3. **회귀 방지** → AI 수정이 기존 기능을 깨뜨리지 않음
16
+ 4. **문서화 자동화** → 테스트가 곧 실행 가능한 명세
17
+
18
+ ---
19
+
20
+ ## Workflow
21
+
22
+ ```
23
+ SPEC 작성 (요구사항)
24
+
25
+ Feature 파일 생성 (Gherkin)
26
+
27
+ Contract 테스트 생성 (API 스키마)
28
+
29
+ 테스트 실행 (Red)
30
+
31
+ AI 에이전트 구현
32
+
33
+ 테스트 실행 (Green)
34
+
35
+ 리팩토링
36
+
37
+ 테스트 재실행 (Green 유지)
38
+ ```
39
+
40
+ ---
41
+
42
+ ## 1. BDD (Behavior-Driven Development)
43
+
44
+ ### Gherkin 문법
45
+
46
+ ```gherkin
47
+ Feature: 사용자 로그인
48
+ As a 사용자
49
+ I want to 로그인하고 싶다
50
+ So that 개인화된 서비스를 이용할 수 있다
51
+
52
+ Scenario: 유효한 credentials로 로그인 성공
53
+ Given 사용자가 "test@example.com"과 "password123"으로 등록되어 있다
54
+ When "test@example.com"과 "password123"으로 로그인을 시도한다
55
+ Then 로그인에 성공한다
56
+ And JWT 토큰을 받는다
57
+ And 홈 화면으로 리디렉션된다
58
+
59
+ Scenario: 잘못된 비밀번호로 로그인 실패
60
+ Given 사용자가 "test@example.com"으로 등록되어 있다
61
+ When "test@example.com"과 "wrong-password"로 로그인을 시도한다
62
+ Then 로그인에 실패한다
63
+ And "Invalid credentials" 에러 메시지를 받는다
64
+ ```
65
+
66
+ ### Step Definitions (Python)
67
+
68
+ ```python
69
+ from pytest_bdd import scenarios, given, when, then, parsers
70
+
71
+ scenarios('features/login.feature')
72
+
73
+ @given(parsers.parse('사용자가 "{email}"과 "{password}"로 등록되어 있다'))
74
+ def user_exists(context, email, password):
75
+ context.user = create_user(email=email, password=password)
76
+
77
+ @when(parsers.parse('"{email}"과 "{password}"로 로그인을 시도한다'))
78
+ def attempt_login(context, email, password):
79
+ context.response = login(email=email, password=password)
80
+
81
+ @then('로그인에 성공한다')
82
+ def login_succeeds(context):
83
+ assert context.response.status_code == 200
84
+
85
+ @then('JWT 토큰을 받는다')
86
+ def receives_token(context):
87
+ assert 'access_token' in context.response.json()
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 2. Contract Testing
93
+
94
+ ### API 계약 정의
95
+
96
+ ```json
97
+ {
98
+ "request": {
99
+ "method": "POST",
100
+ "path": "/api/auth/login",
101
+ "body": {
102
+ "email": "string (email format)",
103
+ "password": "string (min 8 chars)"
104
+ }
105
+ },
106
+ "response": {
107
+ "status": 200,
108
+ "body": {
109
+ "access_token": "string (JWT)",
110
+ "refresh_token": "string (JWT)",
111
+ "user": {
112
+ "id": "uuid",
113
+ "email": "string",
114
+ "name": "string"
115
+ }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### Contract 테스트 (Python)
122
+
123
+ ```python
124
+ import pytest
125
+ from jsonschema import validate
126
+
127
+ RESPONSE_SCHEMA = {
128
+ "type": "object",
129
+ "required": ["access_token", "refresh_token", "user"],
130
+ "properties": {
131
+ "access_token": {"type": "string", "pattern": "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+$"},
132
+ "refresh_token": {"type": "string"},
133
+ "user": {
134
+ "type": "object",
135
+ "required": ["id", "email", "name"],
136
+ "properties": {
137
+ "id": {"type": "string", "format": "uuid"},
138
+ "email": {"type": "string", "format": "email"},
139
+ "name": {"type": "string"}
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ def test_login_response_contract():
146
+ """로그인 응답이 계약을 준수하는지 검증"""
147
+ response = client.post("/api/auth/login", json={
148
+ "email": "test@example.com",
149
+ "password": "password123"
150
+ })
151
+
152
+ assert response.status_code == 200
153
+
154
+ # 응답 스키마 검증
155
+ validate(instance=response.json(), schema=RESPONSE_SCHEMA)
156
+
157
+ # JWT 토큰 형식 검증
158
+ token = response.json()["access_token"]
159
+ assert len(token.split('.')) == 3 # JWT는 3개 부분으로 구성
160
+ ```
161
+
162
+ ---
163
+
164
+ ## 3. AI 에이전트 활용
165
+
166
+ ### SPEC → Feature 자동 생성
167
+
168
+ AI 에이전트가 SPEC의 Acceptance Criteria를 Gherkin Scenario로 자동 변환:
169
+
170
+ **SPEC**:
171
+ ```markdown
172
+ ### REQ-001: 사용자 로그인
173
+ **WHEN** 유효한 credentials로 로그인
174
+ **THEN** JWT 토큰을 받는다
175
+
176
+ #### Acceptance Criteria
177
+ - [ ] 이메일과 비밀번호로 로그인 가능
178
+ - [ ] access_token과 refresh_token 반환
179
+ - [ ] 잘못된 credentials는 400 에러
180
+ ```
181
+
182
+ **Generated Feature**:
183
+ ```gherkin
184
+ Scenario: 유효한 credentials로 로그인 성공
185
+ Given 사용자가 등록되어 있다
186
+ When 유효한 이메일과 비밀번호로 로그인한다
187
+ Then JWT access_token을 받는다
188
+ And JWT refresh_token을 받는다
189
+ ```
190
+
191
+ ### API 스키마 → Contract 자동 생성
192
+
193
+ AI 에이전트가 SPEC의 API 계약을 Contract 테스트로 자동 변환:
194
+
195
+ **SPEC**:
196
+ ```markdown
197
+ ### Endpoint: 로그인
198
+ POST /api/auth/login
199
+ Request: { email, password }
200
+ Response: { access_token, refresh_token, user }
201
+ ```
202
+
203
+ **Generated Contract Test**:
204
+ ```python
205
+ def test_login_contract():
206
+ response = client.post("/api/auth/login", json=valid_credentials)
207
+ assert response.status_code == 200
208
+ validate(response.json(), LOGIN_RESPONSE_SCHEMA)
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 4. Vibe 명령어
214
+
215
+ ### Feature 파일 생성
216
+
217
+ ```bash
218
+ vibe feature "user login"
219
+ # → .vibe/features/user-login.feature 생성
220
+ ```
221
+
222
+ ### Contract 테스트 생성
223
+
224
+ ```bash
225
+ vibe contract "user login"
226
+ # → tests/contract/test_user_login_contract.py 생성
227
+ ```
228
+
229
+ ### 테스트 실행
230
+
231
+ ```bash
232
+ vibe test "user login"
233
+ # → BDD + Contract 테스트 실행
234
+ ```
235
+
236
+ ### 검증
237
+
238
+ ```bash
239
+ vibe verify "user login"
240
+ # → SPEC Acceptance Criteria 100% 충족 확인
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 5. Best Practices
246
+
247
+ ### ✅ DO
248
+
249
+ 1. **SPEC 먼저 작성** → Feature → Contract → 구현
250
+ 2. **Given-When-Then** 명확히 분리
251
+ 3. **계약은 구체적으로** (타입, 형식, 제약 명시)
252
+ 4. **독립적인 시나리오** (순서 무관하게 실행 가능)
253
+ 5. **에러 케이스 포함** (Happy path + Sad path)
254
+
255
+ ### ❌ DON'T
256
+
257
+ 1. **구현 세부사항 노출 금지** (Step Definitions에만 위치)
258
+ 2. **UI 테스트와 혼동 금지** (BDD는 비즈니스 로직)
259
+ 3. **과도한 Background 금지** (중복 제거만)
260
+ 4. **계약 위반 허용 금지** (스키마 변경 시 버전 업)
261
+
262
+ ---
263
+
264
+ ## 6. Coverage Mapping
265
+
266
+ | SPEC REQ | Feature Scenario | Contract Test | Implementation | Status |
267
+ |----------|------------------|---------------|----------------|--------|
268
+ | REQ-001 | ✅ Scenario 1, 2 | ✅ test_login_contract | ✅ POST /api/auth/login | ✅ |
269
+ | REQ-002 | ✅ Scenario 3 | ✅ test_refresh_contract | ⬜ POST /api/auth/refresh | ⬜ |
270
+
271
+ **Coverage**: 1 / 2 (50%)
272
+
273
+ ---
274
+
275
+ ## 7. CI/CD Integration
276
+
277
+ ```yaml
278
+ # .github/workflows/test.yml
279
+ name: BDD + Contract Tests
280
+
281
+ on: [pull_request]
282
+
283
+ jobs:
284
+ test:
285
+ runs-on: ubuntu-latest
286
+ steps:
287
+ - uses: actions/checkout@v2
288
+
289
+ - name: Run BDD tests
290
+ run: pytest tests/features/ -v --gherkin-terminal-reporter
291
+
292
+ - name: Run Contract tests
293
+ run: pytest tests/contract/ -v
294
+
295
+ - name: Upload coverage
296
+ run: |
297
+ pytest --cov=app --cov-report=xml
298
+ codecov -f coverage.xml
299
+ ```
300
+
301
+ ---
302
+
303
+ ## 8. Examples
304
+
305
+ ### Python (FastAPI)
306
+
307
+ ```bash
308
+ # 프로젝트 구조
309
+ project/
310
+ ├── tests/
311
+ │ ├── features/ # Gherkin feature files
312
+ │ │ └── login.feature
313
+ │ ├── step_defs/ # Step definitions
314
+ │ │ └── test_login.py
315
+ │ └── contract/ # Contract tests
316
+ │ └── test_login_contract.py
317
+ ```
318
+
319
+ ### Flutter (Dart)
320
+
321
+ ```bash
322
+ # 프로젝트 구조
323
+ project/
324
+ ├── integration_test/
325
+ │ ├── features/ # Gherkin feature files
326
+ │ │ └── login.feature
327
+ │ └── step_definitions/ # Step definitions
328
+ │ └── login_test.dart
329
+ └── test/
330
+ └── contract/ # Contract tests
331
+ └── login_contract_test.dart
332
+ ```
333
+
334
+ ### React (TypeScript)
335
+
336
+ ```bash
337
+ # 프로젝트 구조
338
+ project/
339
+ ├── tests/
340
+ │ ├── features/ # Gherkin feature files
341
+ │ │ └── login.feature
342
+ │ ├── steps/ # Step definitions
343
+ │ │ └── login.steps.ts
344
+ │ └── contract/ # Contract tests
345
+ │ └── login.contract.test.ts
346
+ ```
347
+
348
+ ---
349
+
350
+ ## 9. Tools & Frameworks
351
+
352
+ ### BDD
353
+
354
+ | Language | Framework |
355
+ |----------|-----------|
356
+ | Python | pytest-bdd, behave |
357
+ | JavaScript | Cucumber.js, Jest-Cucumber |
358
+ | TypeScript | Cucumber.js, Playwright |
359
+ | Dart | flutter_gherkin |
360
+ | Java | Cucumber-JVM |
361
+ | Ruby | Cucumber |
362
+
363
+ ### Contract Testing
364
+
365
+ | Type | Framework |
366
+ |------|-----------|
367
+ | Consumer-Driven | Pact, Spring Cloud Contract |
368
+ | Provider | Postman, Dredd |
369
+ | Schema Validation | JSON Schema, Zod, Ajv |
370
+ | Mock Server | MSW, WireMock, http-mock-adapter |
371
+
372
+ ---
373
+
374
+ ## Summary
375
+
376
+ BDD + Contract Testing은 **AI 에이전트가 SPEC을 정확히 구현하고 자동 검증**할 수 있게 합니다:
377
+
378
+ 1. **명확한 요구사항** → Gherkin으로 비즈니스 언어 표현
379
+ 2. **계약 기반 개발** → API 스키마로 Frontend/Backend 독립 개발
380
+ 3. **자동화된 검증** → AI 생성 코드 품질 즉시 확인
381
+ 4. **회귀 방지** → 기존 기능 보호
382
+
383
+ **Vibe에서 사용**:
384
+ ```bash
385
+ vibe spec "feature" → vibe feature "feature" → vibe contract "feature" → vibe run
386
+ ```
387
+
388
+ **Test-First → AI 구현 → Verify → Done** 🎉
@@ -0,0 +1,276 @@
1
+ # ✅ 최종 검증 체크리스트
2
+
3
+ ## 5.1 향상된 코드 품질 체크
4
+
5
+ ### 최우선 순위
6
+
7
+ ```typescript
8
+ const topPriority = {
9
+ obeysTheGoldenRule: true, // ✅ 요청 범위만 수정
10
+ preservesWorkingCode: true, // ✅ 기존 코드 보존
11
+ respectsExistingStyle: true, // ✅ 기존 스타일 유지
12
+ };
13
+ ```
14
+
15
+ ### 타입 안전성
16
+
17
+ ```typescript
18
+ const typeSafety = {
19
+ noAnyType: true, // ✅ any 타입 사용 금지
20
+ strictNullCheck: true, // ✅ null/undefined 체크
21
+ properTypeGuards: true, // ✅ 타입 가드 사용
22
+ genericTypesWhenNeeded: true, // ✅ 제네릭 타입 활용
23
+ };
24
+ ```
25
+
26
+ ### 코드 구조 & 복잡도
27
+
28
+ ```typescript
29
+ const codeStructure = {
30
+ singleResponsibility: true, // ✅ 단일 책임 원칙
31
+ functionUnder20Lines: true, // ✅ 함수 20줄 이하
32
+ maxNesting3Levels: true, // ✅ 최대 중첩 3단계
33
+ cyclomaticComplexity: 10, // ✅ 순환 복잡도 ≤ 10
34
+ cognitiveComplexity: 15, // ✅ 인지 복잡도 ≤ 15
35
+ maxParameters: 5, // ✅ 매개변수 최대 5개
36
+ componentUnder50Lines: true, // ✅ 컴포넌트 JSX 50줄 이하
37
+ };
38
+ ```
39
+
40
+ ### Halstead 메트릭
41
+
42
+ ```typescript
43
+ const halsteadMetrics = {
44
+ vocabulary: true, // ✅ 연산자/피연산자 다양성
45
+ difficulty: true, // ✅ 코드 이해 난이도
46
+ effort: true, // ✅ 정신적 노력
47
+ lowComplexity: true, // ✅ 낮은 복잡도 유지
48
+ };
49
+ ```
50
+
51
+ ### 결합도 & 응집도
52
+
53
+ ```typescript
54
+ const couplingCohesion = {
55
+ looseCoupling: true, // ✅ 느슨한 결합 (≤ 7 dependencies)
56
+ highCohesion: true, // ✅ 높은 응집도 (관련 기능만 모음)
57
+ noCircularDeps: true, // ✅ 순환 의존성 없음
58
+ dependencyInjection: true, // ✅ 의존성 주입 패턴
59
+ };
60
+ ```
61
+
62
+ ### 에러 처리
63
+
64
+ ```typescript
65
+ const errorHandling = {
66
+ hasErrorHandling: true, // ✅ try-catch/error state
67
+ hasLoadingState: true, // ✅ 로딩 상태
68
+ hasFallbackUI: true, // ✅ 폴백 UI
69
+ properErrorMessages: true, // ✅ 명확한 에러 메시지
70
+ errorBoundaries: true, // ✅ Error Boundary 사용
71
+ };
72
+ ```
73
+
74
+ ### 접근성 (Accessibility)
75
+
76
+ ```typescript
77
+ const accessibility = {
78
+ hasAriaLabels: true, // ✅ ARIA 레이블
79
+ keyboardAccessible: true, // ✅ 키보드 접근성
80
+ semanticHTML: true, // ✅ 시맨틱 HTML
81
+ focusManagement: true, // ✅ 포커스 관리
82
+ screenReaderFriendly: true, // ✅ 스크린 리더 지원
83
+ };
84
+ ```
85
+
86
+ ### 성능
87
+
88
+ ```typescript
89
+ const performance = {
90
+ noUnnecessaryRenders: true, // ✅ 불필요한 리렌더 방지
91
+ memoizedExpensive: true, // ✅ 무거운 연산 메모이제이션
92
+ lazyLoading: true, // ✅ 지연 로딩
93
+ batchOperations: true, // ✅ API 호출 배치 처리
94
+ optimizedImages: true, // ✅ 이미지 최적화
95
+ codesplitting: true, // ✅ 코드 스플리팅
96
+ };
97
+ ```
98
+
99
+ ### 유지보수성
100
+
101
+ ```typescript
102
+ const maintainability = {
103
+ hasJSDoc: true, // ✅ 주요 함수 문서화
104
+ noMagicNumbers: true, // ✅ 매직 넘버 없음
105
+ consistentNaming: true, // ✅ 일관된 네이밍
106
+ properComments: true, // ✅ 적절한 주석
107
+ testable: true, // ✅ 테스트 가능한 구조
108
+ };
109
+ ```
110
+
111
+ ### 보안
112
+
113
+ ```typescript
114
+ const security = {
115
+ noHardcodedSecrets: true, // ✅ 비밀 정보 하드코딩 금지
116
+ inputValidation: true, // ✅ 입력값 검증
117
+ xssPrevention: true, // ✅ XSS 방지
118
+ csrfProtection: true, // ✅ CSRF 보호
119
+ sqlInjectionPrevention: true, // ✅ SQL Injection 방지
120
+ };
121
+ ```
122
+
123
+ ## 5.2 프로젝트 체크
124
+
125
+ ### 의존성 관리
126
+
127
+ ```typescript
128
+ const dependencies = {
129
+ noUnusedDeps: true, // ✅ 미사용 패키지 없음
130
+ noDuplicateDeps: true, // ✅ 중복 기능 패키지 없음
131
+ upToDateDeps: true, // ✅ 최신 버전 유지
132
+ securePackages: true, // ✅ 보안 취약점 없음
133
+ };
134
+ ```
135
+
136
+ ### 파일 구조
137
+
138
+ ```typescript
139
+ const fileStructure = {
140
+ consistentStructure: true, // ✅ 일관된 폴더 구조
141
+ noCircularDeps: true, // ✅ 순환 참조 없음
142
+ logicalGrouping: true, // ✅ 논리적 그룹핑
143
+ clearNaming: true, // ✅ 명확한 파일명
144
+ };
145
+ ```
146
+
147
+ ### 번들 최적화
148
+
149
+ ```typescript
150
+ const bundleOptimization = {
151
+ treeShaking: true, // ✅ Tree shaking
152
+ codeSplitting: true, // ✅ Code splitting
153
+ lazyLoading: true, // ✅ Lazy loading
154
+ minification: true, // ✅ 최소화
155
+ compression: true, // ✅ 압축 (gzip/brotli)
156
+ };
157
+ ```
158
+
159
+ ## 체크리스트 사용법
160
+
161
+ ### 코드 작성 전
162
+
163
+ ```
164
+ [ ] 요구사항 명확히 이해
165
+ [ ] 기존 코드 패턴 파악
166
+ [ ] 영향 범위 확인
167
+ [ ] 테스트 계획 수립
168
+ ```
169
+
170
+ ### 코드 작성 중
171
+
172
+ ```
173
+ [ ] 단일 책임 원칙 준수
174
+ [ ] 함수 길이 20줄 이하 유지
175
+ [ ] 중첩 깊이 3단계 이하
176
+ [ ] 매직 넘버 상수화
177
+ [ ] 타입 안전성 확보
178
+ ```
179
+
180
+ ### 코드 작성 후
181
+
182
+ ```
183
+ [ ] 타입 체크 통과
184
+ [ ] 린터 경고 없음
185
+ [ ] 테스트 작성 및 통과
186
+ [ ] 문서화 완료
187
+ [ ] 코드 리뷰 준비
188
+ ```
189
+
190
+ ### 커밋 전
191
+
192
+ ```
193
+ [ ] 불필요한 코드 제거
194
+ [ ] 콘솔 로그 제거
195
+ [ ] 주석 정리
196
+ [ ] 포맷팅 적용
197
+ [ ] 의미 있는 커밋 메시지 작성
198
+ ```
199
+
200
+ ## 자동 검증 도구
201
+
202
+ ### ESLint 설정
203
+
204
+ ```javascript
205
+ // .eslintrc.js
206
+ module.exports = {
207
+ rules: {
208
+ 'complexity': ['error', 10],
209
+ 'max-depth': ['error', 3],
210
+ 'max-lines-per-function': ['error', 20],
211
+ 'max-params': ['error', 5],
212
+ 'no-magic-numbers': ['warn', { ignore: [0, 1, -1] }],
213
+ '@typescript-eslint/no-explicit-any': 'error',
214
+ },
215
+ };
216
+ ```
217
+
218
+ ### TypeScript 설정
219
+
220
+ ```json
221
+ // tsconfig.json
222
+ {
223
+ "compilerOptions": {
224
+ "strict": true,
225
+ "noImplicitAny": true,
226
+ "strictNullChecks": true,
227
+ "noUnusedLocals": true,
228
+ "noUnusedParameters": true,
229
+ }
230
+ }
231
+ ```
232
+
233
+ ### Git Hooks (pre-commit)
234
+
235
+ ```bash
236
+ #!/bin/sh
237
+ # .husky/pre-commit
238
+
239
+ # 타입 체크
240
+ npm run type-check
241
+
242
+ # 린팅
243
+ npm run lint
244
+
245
+ # 테스트
246
+ npm run test
247
+
248
+ # 포맷팅 확인
249
+ npm run format:check
250
+ ```
251
+
252
+ ## 등급 기준
253
+
254
+ | 등급 | 점수 | 설명 |
255
+ |------|------|------|
256
+ | A+ | 95-100 | 완벽한 코드 품질 |
257
+ | A | 90-94 | 우수한 품질 |
258
+ | B+ | 85-89 | 양호한 품질 |
259
+ | B | 80-84 | 개선 권장 |
260
+ | C+ | 75-79 | 개선 필요 |
261
+ | C | 70-74 | 즉시 개선 필요 |
262
+ | F | < 70 | 리팩토링 필요 |
263
+
264
+ ## 빠른 체크 (1분)
265
+
266
+ ```
267
+ ✅ 요청 범위만 수정했는가?
268
+ ✅ any 타입이 없는가?
269
+ ✅ 함수가 20줄 이하인가?
270
+ ✅ 중첩이 3단계 이하인가?
271
+ ✅ 에러 처리를 했는가?
272
+ ✅ 매직 넘버를 상수화했는가?
273
+ ✅ 테스트를 작성했는가?
274
+ ```
275
+
276
+ 7개 모두 Yes → 배포 가능 ✅