Skip to main content
Glama
VALIDATION_STRATEGY.md13.3 kB
# 검증 전략 (Validation Strategy) > **핵심 질문**: "검증을 어떻게 할 것인가?" > > **핵심 원칙**: 검증되지 않은 코드는 배포하지 않습니다. 코드를 작성하기 전에 검증 방법부터 정의합니다. --- ## 🎯 검증 우선 개발 프로세스 ``` 1. 요구사항 정의 2. ✅ 검증 방법 정의 (HOW TO VALIDATE) ← 여기서 시작! 3. 검증 케이스 작성 (테스트 코드) 4. 구현 5. 검증 실행 6. 통과 → 배포 / 실패 → 수정 후 재검증 ``` **핵심**: 2번 단계(검증 방법 정의)를 건너뛰지 않습니다. --- ## 📊 검증 5단계 (Validation Levels) ### Level 1: 타입 검증 (Type Validation) **목적**: 컴파일 타임에 타입 오류 검출 **방법**: ```typescript // ✅ DO: Zod를 사용한 런타임 타입 검증 import { z } from 'zod'; const NoteSchema = z.object({ id: z.string().regex(/^\d{8}T\d{6}Z$/), title: z.string().min(1).max(200), category: z.enum(['Projects', 'Areas', 'Resources', 'Archives']), tags: z.array(z.string()).optional(), created: z.string().datetime(), }); type Note = z.infer<typeof NoteSchema>; // 사용 시 검증 function createNote(data: unknown): Note { return NoteSchema.parse(data); // 실패 시 ZodError throw } ``` **자동화**: - `npm run typecheck`: TypeScript 타입 체크 - CI에서 빌드 시 자동 실행 **체크리스트**: - [ ] 모든 공개 인터페이스에 Zod 스키마 정의 - [ ] Front Matter 스키마에 런타임 검증 - [ ] MCP 툴 입력/출력에 검증 추가 --- ### Level 2: 단위 테스트 (Unit Tests) **목적**: 개별 함수/클래스의 동작 검증 **방법**: ```typescript // src/storage/__tests__/note-parser.test.ts import { parseNote } from '../note-parser'; describe('parseNote', () => { it('should parse valid note with front matter', () => { const markdown = `--- id: "20250927T103000Z" title: "Test Note" --- Note content`; const result = parseNote(markdown); expect(result.id).toBe('20250927T103000Z'); expect(result.title).toBe('Test Note'); expect(result.content).toBe('Note content'); }); it('should throw on invalid front matter', () => { const markdown = `--- id: "invalid" ---`; expect(() => parseNote(markdown)).toThrow(); }); }); ``` **자동화**: - `npm test`: Jest 단위 테스트 실행 - `npm run test:coverage`: 커버리지 리포트 - CI에서 모든 PR에 대해 자동 실행 **목표**: - 테스트 커버리지 **80%+** - 모든 공개 함수/메서드에 테스트 **체크리스트**: - [ ] 함수 작성 전 테스트 작성 (TDD) - [ ] Happy path + Edge cases 모두 테스트 - [ ] 에러 케이스 명시적으로 테스트 --- ### Level 3: 통합 테스트 (Integration Tests) **목적**: 여러 컴포넌트 간 상호작용 검증 **방법**: ```typescript // src/__tests__/integration/storage-index.test.ts import { StorageManager } from '../storage'; import { IndexManager } from '../index'; describe('Storage + Index Integration', () => { let storage: StorageManager; let index: IndexManager; beforeEach(async () => { storage = new StorageManager({ vaultPath: tempDir }); index = new IndexManager({ dbPath: tempDbPath }); await index.initialize(); }); it('should index note when created', async () => { // Create note via storage const note = await storage.createNote({ title: 'Test', content: 'Integration test', }); // Verify it's indexed const results = await index.search('Integration test'); expect(results).toHaveLength(1); expect(results[0].id).toBe(note.id); }); afterEach(async () => { await cleanup(); }); }); ``` **자동화**: - `npm run test:integration`: 통합 테스트 실행 - CI에서 PR merge 전 실행 **체크리스트**: - [ ] 패키지 간 경계(boundary) 테스트 - [ ] 데이터베이스/파일시스템 상호작용 테스트 - [ ] 실제 환경과 유사한 설정 사용 --- ### Level 4: E2E 테스트 (End-to-End Tests) **목적**: MCP 프로토콜을 통한 전체 시스템 검증 **방법**: ```typescript // src/__tests__/e2e/mcp-server.test.ts import { MCPClient } from '@modelcontextprotocol/sdk/client'; describe('MCP Server E2E', () => { let client: MCPClient; beforeEach(async () => { // Start MCP server client = await startMCPServer(); }); it('should create and search notes via MCP', async () => { // Create note via MCP tool const createResult = await client.callTool('create_note', { title: 'E2E Test', content: 'Testing MCP protocol', category: 'Resources', }); expect(createResult.success).toBe(true); const noteId = createResult.data.id; // Search via MCP tool const searchResult = await client.callTool('search_notes', { query: 'MCP protocol', }); expect(searchResult.results).toHaveLength(1); expect(searchResult.results[0].id).toBe(noteId); }); afterEach(async () => { await stopMCPServer(); }); }); ``` **자동화**: - `npm run test:e2e`: E2E 테스트 실행 - CI에서 릴리스 전 실행 **체크리스트**: - [ ] 모든 MCP 툴 테스트 - [ ] 에러 응답 형식 검증 - [ ] MCP 프로토콜 스펙 준수 확인 --- ### Level 5: 성능 & 보안 검증 (Performance & Security) **목적**: 비기능 요구사항 검증 #### 5.1 성능 검증 **방법**: ```typescript // src/__tests__/performance/search-benchmark.test.ts import { performance } from 'perf_hooks'; describe('Search Performance', () => { it('should complete search within 120ms (P95)', async () => { const iterations = 100; const latencies: number[] = []; for (let i = 0; i < iterations; i++) { const start = performance.now(); await index.search('test query'); const end = performance.now(); latencies.push(end - start); } latencies.sort((a, b) => a - b); const p95 = latencies[Math.floor(iterations * 0.95)]; expect(p95).toBeLessThan(120); // P95 < 120ms }); }); ``` **자동화**: - `npm run test:performance`: 성능 벤치마크 - CI에서 주기적으로 실행, 회귀 감지 **KPI 목표**: - [ ] 검색 P95 < 120ms (10k notes) - [ ] 증분 인덱싱 < 3초 - [ ] 전체 인덱스 재빌드 < 5분 (10k files) - [ ] 초기 부팅 < 8초 #### 5.2 보안 검증 **방법**: ```typescript // src/__tests__/security/sensitive-data.test.ts import { maskSensitiveData } from '../security'; describe('Sensitive Data Masking', () => { it('should mask API keys', () => { const input = 'API_KEY=sk-1234567890abcdef'; const masked = maskSensitiveData(input); expect(masked).toContain('API_KEY=***MASKED***'); expect(masked).not.toContain('sk-1234567890'); }); it('should mask email addresses', () => { const input = 'Contact: user@example.com'; const masked = maskSensitiveData(input); expect(masked).toContain('***@***.***'); }); }); ``` **자동화**: - `npm run test:security`: 보안 테스트 - 정기적인 보안 스캔 (npm audit, snyk) **체크리스트**: - [ ] 민감정보 마스킹 정탐율 >95% - [ ] 의존성 취약점 스캔 (npm audit) - [ ] 입력 검증 (SQL injection, XSS 방지) --- ## 🔄 CI/CD 검증 파이프라인 ```yaml # .github/workflows/validation.yml name: Validation Pipeline on: [push, pull_request] jobs: validate: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Node uses: actions/setup-node@v3 with: node-version: '18' # Level 1: Type Validation - name: Type Check run: npm run typecheck # Level 2: Unit Tests - name: Unit Tests run: npm run test:unit - name: Coverage Check run: npm run test:coverage # Fail if coverage < 80% # Level 3: Integration Tests - name: Integration Tests run: npm run test:integration # Level 4: E2E Tests - name: E2E Tests run: npm run test:e2e # Level 5: Performance Tests - name: Performance Tests run: npm run test:performance # Level 5: Security Scan - name: Security Audit run: npm audit --audit-level=moderate ``` --- ## ✅ 검증 체크리스트 (Validation Checklist) ### 코드 작성 전 (Before Writing Code) - [ ] 요구사항이 명확한가? - [ ] **검증 방법이 정의되었는가?** ← 가장 중요! - [ ] 검증 케이스를 나열했는가? - [ ] 실패 조건을 정의했는가? ### 구현 중 (During Implementation) - [ ] 테스트를 먼저 작성했는가? (TDD) - [ ] 타입 검증을 추가했는가? (Zod) - [ ] 에러 케이스를 처리했는가? - [ ] 로그를 추가했는가? ### PR 제출 전 (Before PR) - [ ] 모든 테스트가 통과하는가? - [ ] 커버리지가 80% 이상인가? - [ ] Lint/Type 에러가 없는가? - [ ] 문서를 업데이트했는가? ### 릴리스 전 (Before Release) - [ ] E2E 테스트가 통과하는가? - [ ] 성능 KPI를 만족하는가? - [ ] 보안 스캔이 통과하는가? - [ ] CHANGELOG를 업데이트했는가? --- ## 🚫 검증 실패 시 대응 (Failure Response) ### 원칙 1. **검증 실패 = 배포 금지** 2. **우회 금지**: "나중에 고치겠다"는 없습니다 3. **즉시 수정**: 실패한 검증은 즉시 수정합니다 ### 대응 프로세스 ``` 검증 실패 감지 ↓ 원인 분석 ↓ 수정 방법 결정 ↓ 수정 구현 ↓ 재검증 ↓ 통과 → 진행 / 실패 → 반복 ``` --- ## 📈 검증 메트릭 (Validation Metrics) ### 추적할 지표 - **테스트 커버리지**: 80%+ 유지 - **테스트 실행 시간**: < 5분 (전체) - **CI 통과율**: > 95% - **평균 수정 시간**: < 1일 (검증 실패 → 수정 완료) - **보안 취약점**: 0 (high/critical) ### 주간 리포트 ``` Week XX Validation Report: - Test Coverage: 85% (↑ 2%) - CI Success Rate: 98% - Failed Validations: 3 - Type errors: 1 (fixed) - Unit test failures: 2 (fixed) - Security Issues: 0 - Performance: All KPIs met ✓ ``` --- ## 🎓 검증 예제 (Validation Examples) ### 예제 1: 새로운 MCP 툴 추가 **요구사항**: `update_note` 툴 추가 **검증 방법 정의**: 1. 타입 검증: Zod 스키마로 입력 검증 2. 단위 테스트: 노트 업데이트 로직 테스트 3. 통합 테스트: Storage + Index 업데이트 확인 4. E2E 테스트: MCP를 통한 전체 플로우 테스트 5. 성능 테스트: 업데이트 지연시간 < 100ms **구현 순서**: ```typescript // 1. 타입 검증 (Zod 스키마) const UpdateNoteInputSchema = z.object({ id: z.string(), title: z.string().optional(), content: z.string().optional(), tags: z.array(z.string()).optional(), }); // 2. 단위 테스트 작성 describe('updateNote', () => { it('should update note title', async () => { // ... test implementation }); }); // 3. 구현 async function updateNote(input: unknown) { const validated = UpdateNoteInputSchema.parse(input); // ... implementation } // 4. 통합/E2E 테스트 // ... more tests // 5. 검증 실행 및 통과 확인 ``` --- ## 📚 관련 문서 - [GOALS.md](./GOALS.md) - 검증이 #1 목표임을 명시 - [DEVELOPMENT_GUIDELINES.md](./DEVELOPMENT_GUIDELINES.md) - TDD 워크플로우 - [TECHNICAL_SPEC.md](./TECHNICAL_SPEC.md) - 성능/보안 KPI --- ## 📊 실제 커버리지 현황 (v0.0.2) ### 전체 커버리지 ``` 전체 코드: 45.95% statements, 27.7% branches (목표: 60%+) 핵심 기능: 84.96% statements, 74.28% branches ✅ ``` ### 패키지별 커버리지 | 패키지 | Statements | Status | 주요 갭 | |-------|-----------|--------|---------| | **mcp-server/src/tools** | 84.96% | ✅ 우수 | 핵심 기능 잘 검증됨 | | **common/src** | 65.14% | ⚠️ 개선 필요 | 보안 함수 테스트 추가됨 (v0.0.2) | | **index-search/src** | 47.59% | ⚠️ 개선 필요 | link-graph 21.52% | | **storage-md/src** | 46.95% | ⚠️ 개선 필요 | watcher 13.79% | | **assoc-engine/src** | 0% | ❌ 미구현 | Epic E4 (Olima) | ### 검증 완료된 기능 (v0.0.2+) - ✅ **보안 검증**: 민감정보 마스킹 (21개 테스트) - ✅ **데이터 무결성**: 원자적 쓰기 (15개 테스트) - ✅ **MCP 프로토콜**: E2E 테스트 (13개 테스트) - ✅ **성능**: P95 < 1ms (목표 120ms 대비 120배 초과) ### CI 강제 수준 ```javascript // jest.config.js (v0.0.2+) coverageThreshold: { global: { statements: 45, // 기존: 30 → 신규: 45 (↑50%) functions: 40, // 기존: 25 → 신규: 40 (↑60%) lines: 45, // 기존: 30 → 신규: 45 (↑50%) branches: 27 // 기존: 25 → 신규: 27 (↑8%) }, 'packages/mcp-server/src/tools': { statements: 80, // 핵심 기능 높은 기준 유지 lines: 80 } } ``` --- ## 💡 핵심 메시지 > **"검증을 어떻게 할 것인가?"**를 먼저 생각하고, 코드는 나중에 작성합니다. > > 검증 방법이 명확하지 않다면, 요구사항이 명확하지 않은 것입니다. > > **현재 상태 (v0.0.2)**: 핵심 경로는 "Validation First" 달성 ✅ > **목표 (v0.1.0)**: 전체 코드에 "No Validation, No Code" 확대 🎯

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/inchan/memory-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server