[FE-01] 피처 플래그 zod 스키마·DTO 타입 (BE 계약 SSOT)

작업 내용 (설계 의도)

변경 사항

BE 계약(../TDD.md “API 계약” 섹션)을 FE의 단일 진실 타입으로 확립한다. 후행 티켓(BFF·훅·컴포넌트·화면)이 전부 이 타입을 import하는 연관 병목이므로 한 티켓으로 묶어 wave 1에 배치한다. 근거 설계: ../design-fe-web.md “API 연동 표 / BE 계약 타입”.

  • web/lib/admin/feature-flags/schemas.ts 신설:
    • strategy discriminated union — z.discriminatedUnion("strategyType", [...])로 GLOBAL_TOGGLE(enabled)·PERCENTAGE_ROLLOUT(percentage 0~100)·ATTRIBUTE_MATCH(attribute,value)·VARIANT_BUCKETING(variants ≤4, weight 합 100) 표현.
    • 입력 스키마: CreateFeatureFlagInputSchema(key,type,description,strategy), UpdateFeatureFlagInputSchema(description,strategy).
    • 응답 스키마·타입: FeatureFlagResponseSchema/FeatureFlagResponse, FeatureFlagAuditLogResponseSchema/...Response(배열 원소), FeatureFlagSnapshot.
    • 감사 로그 total 포함 페이지 응답(senior-be 계약 개정 반영): FeatureFlagAuditLogPageSchema(wire) — total을 담는 형태로 파싱. BE 최종 형태가 Spring Page({content, totalElements, totalPages, number, size})든 경량({items, total})든 파싱하게 정의하고, 화면이 소비하는 canonical 타입 FeatureFlagAuditLogPageView({ logs, total, page, size, totalPages })를 별도로 노출한다. 배열 직반환 전용 스키마·폴백은 두지 않는다.
    • enum: FeatureFlagType(RELEASE/OPERATIONAL/EXPERIMENT/ENTITLEMENT), FeatureFlagStatus(ACTIVE/ARCHIVED), StrategyType, ChangeType.
  • 필드·타입은 TDD 계약과 1:1 (시각은 ISO 문자열, percentage number, variants name/weight).
  • 필드명 격리: wire→canonical 변환은 FE-04 api.ts가 담당한다. 여기서는 wire 스키마(total 포함 전제)와 canonical 타입만 정의 — BE 최종 필드명 확정 시 wire 스키마만 조정하고 canonical(화면 계약)은 고정.
  • 검증 없는 타입 단언(as) 금지 — 응답은 후행 훅이 .parse로 좁힌다. 여기서는 스키마·타입만 정의.

의존

  • 없음

다이어그램

클래스 의존

flowchart LR
    Zod["zod"] --> Schemas["feature-flags/schemas.ts"]
    Schemas --> BFF["BFF routes FE-03"]
    Schemas --> Hooks["훅/함수 FE-04"]
    Schemas --> Comp["컴포넌트 FE-05·06"]

테스트 케이스

  • 유효한 GLOBAL_TOGGLE/PERCENTAGE_ROLLOUT/ATTRIBUTE_MATCH/VARIANT_BUCKETING strategy 4종이 각각 파싱을 통과한다
  • percentage가 101이면 PERCENTAGE_ROLLOUT 파싱이 실패한다
  • variants weight 합이 90이면 VARIANT_BUCKETING 파싱이 실패한다
  • variants가 5개면 파싱이 실패한다(최대 4)
  • strategyType이 계약 외 값이면 discriminatedUnion 파싱이 실패한다
  • FeatureFlagResponse에 status가 “ACTIVE”/“ARCHIVED” 외면 파싱이 실패한다
  • total 포함 감사 페이지 응답이 스키마를 통과하고 total 값이 보존된다
  • total 필드가 없는 응답은 FeatureFlagAuditLogPageSchema 파싱이 실패한다