Schema Verification Guide
docs/api/SCHEMA_VERIFY.md
Schema Verification Guide
Shop API의 서버 DTO와 클라이언트 Zod 스키마 간 불일치를 자동 검출하는 도구.
사용법
# 전체 검증 pnpm --filter @makitt/api verify-schemas # 특정 리소스만 pnpm --filter @makitt/api verify-schemas -- --resource=blog.blog # 특정 도메인 전체 pnpm --filter @makitt/api verify-schemas -- --domain=checkout # 특정 액션만 pnpm --filter @makitt/api verify-schemas -- --resource=action:checkout.create # 서버 최신 스펙으로 검증 (shop-api 실행 중이어야 함) pnpm --filter @makitt/api verify-schemas -- --fresh # 조합 pnpm --filter @makitt/api verify-schemas -- --domain=blog --fresh pnpm --filter @makitt/api verify-schemas -- --resource=product.product --fresh
옵션
| 옵션 | 설명 | 예시 |
|---|---|---|
--resource=<key> | 특정 리소스 또는 액션만 검증 | --resource=blog.blog, --resource=action:auth.login |
--domain=<id> | 특정 도메인 전체 검증 | --domain=product, --domain=checkout |
--fresh | 검증 전 서버에서 OpenAPI JSON 최신화 | |
--url=<url> | --fresh 시 사용할 서버 URL | --url=https://shop-api.dev.makitt.shop/v3/api-docs |
선결 조건
docs/api/shop-api-openapi.json 파일이 존재해야 함. 두 가지 방법:
# 방법 1: --fresh 옵션 (서버 실행 중일 때) pnpm --filter @makitt/api verify-schemas -- --fresh # 방법 2: 스크립트 직접 실행 ./scripts/extract-openapi.sh
서버 안 바꿨으면 기존 커밋된 JSON으로 검증 가능 (서버 불필요).
리포트 읽는 법
Total: 64 | ✅ Pass: 0 | ⚠️ Warn: 38 | ❌ Fail: 12 | ⏭️ Skip: 14
❌ blog.blog
Path: /shop/{shopId}/blogs/{blogId}
Fields: Zod=20, OpenAPI=20
⚠️ [missing_in_zod] seo.metaTitle: Server sends but Zod does not define
❌ [missing_in_openapi] seo.title: Zod expects but server does not send
심각도
| 아이콘 | 이슈 | 의미 | 영향 |
|---|---|---|---|
| ❌ | missing_in_openapi | Zod에 있지만 서버가 안 보냄 | HCS 바인딩 실패 — 해당 필드가 항상 undefined |
| ❌ | type_mismatch | 타입이 다름 | 런타임 타입 오류 가능 |
| ⚠️ | missing_in_zod | 서버가 보내지만 Zod에 없음 | Builder 자동완성에 안 뜸, HCS 바인딩 불가 |
| ⚠️ | nullable_mismatch | 서버는 null 가능인데 Zod는 required | 서버 @Schema 미지정 노이즈가 대부분 |
| ⏭️ | skip | 비교 불가 | z.any() 스키마 또는 OpenAPI 경로 없음 |
상태별 대응
| 상태 | 대응 |
|---|---|
| FAIL | 반드시 수정. 서버 DTO 변경 또는 Zod 스키마 수정 |
| WARN (missing_in_zod) | Zod에 필드 추가 권장 (HCS 바인딩에 필요하면) |
| WARN (nullable_mismatch) | 대부분 노이즈. 서버 @Schema(requiredMode = REQUIRED) 보강 시 해결 |
| SKIP | z.any() → 실제 스키마 정의하면 자동으로 검증 대상에 포함 |
새 리소스/액션 추가 시
리소스
resources/*.ts에서 apiPath와 apiMethod를 함께 넣으면 자동으로 검증 대상:
export const myResource: ResourceDefinition<typeof MySchema> = { key: 'myResource', displayName: '내 리소스', type: 'single', schema: MySchema, apiPath: '/shop/{shopId}/my-resources/{id}', // ← 이것만 추가 apiMethod: 'get', // ← 이것만 추가 fetcher: (shopId, params) => myApi.detail(shopId, params), };
액션
action-registry.ts에서 responseSchema + apiPath + apiMethod를 넣으면 검증 대상:
'myDomain.myAction': { schema: myInputSchema, responseSchema: MyResponseSchema, // ← 응답 스키마 apiPath: '/shop/{shopId}/my-resources', // ← 이것만 추가 apiMethod: 'post', // ← 이것만 추가 execute: async (params, context) => { ... }, },
apiPath를 안 넣으면 검증에서 스킵 (기존 동작 영향 없음).
검증 범위
| 비교 항목 | 지원 |
|---|---|
| 1depth 필드명 | ✅ |
| nested object 필드 (seo.title) | ✅ |
| array item 필드 (items[].name) | ✅ |
| 타입 비교 (string/number/boolean/object/array) | ✅ |
| nullable/optional | ✅ |
z.any() 스키마 | ⏭️ Skip |
파일 구조
makitt-root/
├── scripts/extract-openapi.sh # 서버 → JSON 추출
├── docs/api/
│ ├── shop-api-openapi.json # 커밋된 OpenAPI 스펙
│ ├── schema-mismatch-report.md # 불일치 리포트
│ └── SCHEMA_VERIFY.md # 이 문서
makitt-client/packages/api/src/verify/
├── cli.ts # CLI 실행 진입점
├── openapi-utils.ts # OpenAPI JSON 파싱
├── schema-verify.ts # 비교 엔진 + 리포트
└── schema-verify.test.ts # CI용 vitest 테스트
CI
pnpm --filter @makitt/api verify-schemas:ci
FAIL이 있으면 exit code 1로 실패.