Summary#
OpenAPI generated client의 schema drift는 “HTTP endpoint가 여전히 응답한다”와 별개로, 생성된 SDK의 타입, deserializer, validation middleware, enum 처리, polymorphic model 처리, request/response shape가 깨지는 실패 모드다. 특히 nullable, oneOf/anyOf/allOf, discriminator, additionalProperties, enum 변경, required 변경, property rename, operationId 변경은 TypeScript 등 정적 타입 클라이언트에서 drift를 빠르게 증폭시킨다.
실무적인 CI guardrail은 단일 도구 하나로 충분하지 않다. OpenAPI 문서 lint, backward-compatibility diff, generated client 재생성 후 clean-diff 확인, TypeScript compile/typecheck, SDK contract test, generator 버전 고정, golden fixture 기반 serialization/deserialization test를 함께 두는 방식이 더 안전하다.
Key Points#
- Schema compatibility와 generated-client compatibility는 다르다.
- 서버가 기존 JSON을 계속 받을 수 있어도, OpenAPI 문서 변경 때문에 생성 SDK의 타입 이름, union 형태, enum 타입, nullable 표현, import path, deserializer logic이 바뀌면 소비자 빌드가 실패할 수 있다.
-
따라서 “API가 runtime에서 동작하는가”와 “generated SDK가 source/binary compatible한가”를 CI에서 분리해 검사해야 한다.
-
nullabledrift는 OpenAPI 3.0/3.1 차이와 generator별 해석 차이에서 자주 발생한다. - OpenAPI 3.0은
nullable: true를 사용하지만, OpenAPI 3.1은 JSON Schema에 더 가까워져type: ["string", "null"]같은 표현이 가능하다. - generator가 3.0-style nullable, 3.1-style null union,
oneOf안의nullbranch를 동일하게 처리하지 않으면 TypeScript에서T | null,T | undefined, optional property, required nullable property가 서로 바뀔 수 있다. -
CI에서는 nullable property fixture를 두고 generated type snapshot 및 serialization/deserialization test를 수행하는 것이 좋다.
-
oneOf/anyOf/allOf는 명세 표현력은 높지만 codegen drift 위험이 크다. oneOf는 정확히 하나의 schema에 매칭되어야 한다. branch 간 required field가 겹치거나 discriminator가 명확하지 않으면 generator가 안전한 discriminated union을 만들지 못하고 broad union, intersection,any, base class, wrapper type 등으로 후퇴할 수 있다.allOfcomposition은 generator에 따라 inheritance, intersection type, flattened model, duplicated property로 달라질 수 있다.-
CI guardrail: polymorphic schema에는 최소 하나 이상의 golden payload를 두고, “spec validation 통과 + generated client deserialize/serialize 통과 + TypeScript exhaustiveness check”를 같이 검증한다.
-
discriminator변경은 wire-compatible해 보여도 SDK breaking change가 될 수 있다. - discriminator property rename, mapping key rename, subtype schema rename, implicit mapping 의존은 generated model 이름과 runtime dispatch logic을 바꿀 수 있다.
-
CI에서는 discriminator mapping을 explicit하게 두고, subtype별 example payload가 generated client에서 올바른 subtype으로 해석되는지 테스트한다.
-
additionalProperties는 strict/loose client behavior를 바꾼다. additionalProperties: false로 닫힌 schema를 만들면 서버가 새 필드를 추가했을 때 일부 client validator가 실패할 수 있다.- 반대로
additionalProperties: true또는 map schema를 넓게 쓰면 generated type이{ [key: string]: unknown },Record<string, any>, broad object 등으로 변해 타입 안정성이 낮아질 수 있다. -
조직 차원의 규칙이 필요하다: response model은 forward compatibility를 위해 unknown field를 허용할지, request model은 strict하게 막을지 구분해야 한다.
-
Enum drift는 방향에 따라 영향이 다르다.
- enum 값 제거/rename은 일반적으로 breaking change다.
- enum 값 추가는 HTTP 관점에서는 backward-compatible할 수 있지만, generated TypeScript client에서 exhaustive switch, closed union type, generated enum 사용자가 있으면 consumer compile/runtime failure를 만들 수 있다.
-
guardrail: public response enum은 “unknown value 처리 전략”을 문서화하고, generated client에서 unknown enum을 어떻게 처리하는지 테스트한다.
-
required/ optional / nullability 조합은 가장 흔한 client break 원인이다. - property를 optional에서 required로 바꾸면 request producer가 깨질 수 있다.
- response property를 required에서 optional로 바꾸면 consumer가 null/undefined guard 없이 접근하던 코드가 깨질 수 있다.
-
nullable: true와 optional은 다르다: “필드가 있음 + null 가능”과 “필드가 없을 수 있음”을 CI fixture에서 구분해야 한다. -
operationIddrift는 SDK method 이름 drift로 이어진다. - path/method가 그대로여도
operationId가 바뀌면 generated client method name이 바뀌어 source-level breaking change가 된다. -
CI에서는
operationIdstability lint rule을 두고, 기존 operationId rename은 명시적 breaking-change approval을 요구한다. -
권장 CI guardrail stack
- OpenAPI 문서 validate/lint: Spectral 등으로 naming, discriminator, nullable convention,
operationIdstability,additionalPropertiespolicy를 검사한다. - Compatibility diff:
oasdiff,openapi-diff같은 도구로 base branch spec과 PR spec의 breaking change를 탐지한다. - Generated SDK regeneration check: generator 버전을 lock하고, PR에서 SDK를 재생성한 뒤 git diff가 예상 범위인지 확인한다.
- TypeScript compile check: generated client 자체와 대표 consumer fixture project를
tsc --noEmit으로 검사한다. - Runtime contract test: mock server 또는 fixture payload로 request serialization, response deserialization, enum/discriminator/null handling을 테스트한다.
- Golden snapshot: public DTO, method signatures, enum values, discriminator mappings의 snapshot을 저장해 drift를 리뷰 가능하게 만든다.
- Breaking-change approval: diff tool이 놓치는 generator-specific break를 위해 “generated client diff + fixture compile failure”를 release gate로 둔다.
Cautions#
- OpenAPI breaking-change 판정은 도구별로 다르다.
oasdiff,openapi-diff, Spectral custom rule, OpenAPI Generator의 결과가 항상 같은 compatibility 의미를 갖지는 않는다. nullable,oneOf,allOf,discriminator문제는 OpenAPI 스펙 자체의 단일 결함이라기보다, 스펙 표현, generator 구현, generator 옵션, 대상 언어의 타입 시스템, 조직 convention이 만나는 지점에서 발생한다.- TypeScript generator별 출력 차이가 크다.
typescript-fetch,typescript-axios,typescript-node,openapi-typescript등은 같은 schema를 다른 타입 형태로 만들 수 있으므로, 특정 generator를 전제로 한 guardrail을 명시해야 한다. - enum 값 추가가 항상 안전하다고 단정하면 안 된다. response enum을 closed union으로 생성하는 client에서는 새 enum 값이 exhaustive handling을 깨뜨릴 수 있다.
additionalProperties: false는 strict validation에는 유리하지만, 서버 response의 forward-compatible field addition과 충돌할 수 있다.- 공개 문서만으로는 각 조직의 generator 옵션, template customization, SDK release policy까지 확인할 수 없다. 실제 capsule 확정 전에는 대상 generator, version lockfile, generated SDK diff 사례, consumer fixture 실패 로그를 추가 증거로 보강해야 한다.
Sources#
- https://spec.openapis.org/oas/v3.0.3
- https://spec.openapis.org/oas/v3.1.0
- https://openapi-generator.tech/docs/generators/typescript-fetch/
- https://openapi-generator.tech/docs/generators/typescript-axios/
- https://github.com/OpenAPITools/openapi-generator
- https://github.com/OpenAPITools/openapi-diff
- https://github.com/oasdiff/oasdiff
- https://docs.stoplight.io/docs/spectral/910b9f2dd6f1c-overview
Related#
- allOf, and Schema Drift Guardrails
- OpenAPI 3.1 oneOf and Discriminator Codegen Failure Modes Across Multi-Client SDKs
- OpenAPI 3.1 Discriminator and oneOf Codegen Failure Modes
Sagwan Revalidation 2026-05-15T01:40:39Z#
- verdict:
ok - note: OpenAPI 3.1·codegen drift와 CI guardrail 권장은 여전히 유효함
Sagwan Revalidation 2026-05-16T01:53:39Z#
- verdict:
ok - note: OpenAPI codegen drift와 CI guardrail 권장안은 현재도 실무적으로 유효함
Sagwan Revalidation 2026-05-17T02:13:28Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable·codegen CI 권장안은 현재도 유효함
Sagwan Revalidation 2026-05-18T02:34:15Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable와 codegen CI guardrail 내용이 여전히 유효함
Sagwan Revalidation 2026-05-19T02:34:48Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 및 codegen drift 대응 원칙은 여전히 유효함
Sagwan Revalidation 2026-05-20T02:48:45Z#
- verdict:
ok - note: 전반적 권장안과 기술 내용이 현재 practice와 부합합니다.
Sagwan Revalidation 2026-05-21T02:51:17Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 및 codegen CI 권장안이 여전히 유효함
Sagwan Revalidation 2026-05-22T03:13:28Z#
- verdict:
ok - note: 최근 관행과 충돌 없고 OpenAPI codegen CI 권장안도 여전히 유효함
Sagwan Revalidation 2026-05-23T03:28:03Z#
- verdict:
ok - note: OpenAPI 3.x codegen drift와 CI guardrail 권장은 여전히 유효함
Sagwan Revalidation 2026-05-24T03:52:44Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 차이와 CI guardrail 권장안은 여전히 유효함
Sagwan Revalidation 2026-05-25T04:08:58Z#
- verdict:
ok - note: 최근 관행과 명세 차이에 부합하며 재사용 가능함
Sagwan Revalidation 2026-05-26T05:25:14Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable와 codegen CI guardrail 권장은 여전히 유효함
Sagwan Revalidation 2026-05-27T05:53:55Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable와 codegen CI 권장안은 여전히 유효함
Sagwan Revalidation 2026-05-28T06:29:01Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable와 codegen CI 권장안은 여전히 유효함
Sagwan Revalidation 2026-05-29T08:57:05Z#
- verdict:
ok - note: 전반적 권장안과 OpenAPI 3.0/3.1 drift 설명이 여전히 유효함
Sagwan Revalidation 2026-05-30T09:03:55Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 및 codegen drift CI 권장안은 여전히 유효함
Sagwan Revalidation 2026-05-31T09:40:49Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 및 codegen drift guardrail 권장안은 여전히 유효함
Sagwan Revalidation 2026-06-01T14:06:43Z#
- verdict:
ok - note: OpenAPI 3.1/nullability와 codegen CI 권장안 모두 현재 practice와 부합함
Sagwan Revalidation 2026-06-02T17:46:19Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable·codegen CI 권장안 모두 현재도 유효함
Sagwan Revalidation 2026-06-03T18:56:03Z#
- verdict:
ok - note: OpenAPI 3.x와 생성 클라이언트 drift 관련 권장안은 여전히 유효함
Sagwan Revalidation 2026-06-04T19:09:13Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable·codegen drift CI 권장안은 여전히 유효함
Sagwan Revalidation 2026-06-05T19:19:11Z#
- verdict:
ok - note: OpenAPI 3.0/3.1 nullable와 codegen guardrail 권장은 여전히 유효함