Summary#
OpenAPI 3.1에서 discriminator와 oneOf를 조합하면 문서상으로는 다형 모델을 명확히 표현할 수 있지만, TypeScript/Java SDK 생성기에서는 실패 모드가 자주 생긴다. 핵심 원인은 discriminator가 JSON Schema 검증 의미를 바꾸는 장치가 아니라 “어느 스키마를 선택할지 알려주는 힌트”에 가깝고, 코드 생성기는 이 힌트를 언어별 상속, union type, Jackson/Gson 역직렬화, validation 코드로 해석해야 하기 때문이다.
안전한 설계 방향은 다음과 같다: oneOf를 진짜 배타적 union으로 쓰고, 각 variant에 필수 discriminator 필드 + 단일 literal 값을 넣으며, mapping을 명시하고, inline schema와 암묵적 이름 매핑을 피한다. Java/TypeScript 양쪽 SDK를 생성해야 한다면 allOf 기반 상속 흉내, nullable 조합, 중첩 oneOf/anyOf, discriminator 없는 variant를 최대한 피하고, CI에서 실제 코드 생성·컴파일·샘플 payload 역직렬화 테스트를 수행해야 한다.
Key Points#
discriminator는 검증 규칙을 자동으로 강화하지 않는다.- OpenAPI Discriminator Object는 payload의 특정 property 값을 이용해 어떤 schema를 선택할지 돕는 장치다.
- 그러나 JSON Schema 관점에서
oneOf의 배타성, required property, const/enum 제약은 별도로 모델링해야 한다. -
따라서 각 subtype에
kind,type,objectType같은 discriminator 필드를required로 두고, variant마다 고유한const또는 single-valueenum을 지정하는 편이 안전하다. -
oneOf는 “정확히 하나만 매칭”되어야 하므로 variant 간 겹침이 있으면 codegen/validation 모두 흔들린다. - 두 subtype이 같은 required 필드 구조를 공유하거나, discriminator 값 제약이 없으면 하나의 payload가 여러 schema에 동시에 매칭될 수 있다.
- 이 경우 validator는
oneOf위반으로 실패하고, code generator는 union narrowing 또는 deserialization 분기 코드를 불완전하게 만들 수 있다. -
anyOf는 더 느슨하므로 다형 SDK 모델 생성에는 더 불안정한 경우가 많다. -
OpenAPI 3.1에서는
nullable: true대신 JSON Schema식 null 표현을 쓴다. - 예:
type: ["string", "null"]. - 하지만 많은 generator와 framework는 3.0식
nullable처리 경험이 길고, 3.1 JSON Schema 표현을 완전히 동일하게 처리하지 못할 수 있다. oneOf안에nullbranch를 넣거나 nullable discriminator field를 허용하면 TypeScript union과 Java deserializer 양쪽에서 실패 가능성이 커진다.-
discriminator 필드는 nullable로 만들지 않는 것이 좋다.
-
명시적
mapping을 사용하는 편이 암묵적 schema-name 매핑보다 안전하다. - 암묵 매핑은 schema component 이름, 대소문자, ref 이름 변경, generator별 naming strategy에 영향을 받는다.
- 다음처럼 wire value와 schema ref를 명시적으로 연결하는 패턴이 더 견고하다.
components:
schemas:
Pet:
oneOf:
- $ref: "#/components/schemas/Cat"
- $ref: "#/components/schemas/Dog"
discriminator:
propertyName: kind
mapping:
cat: "#/components/schemas/Cat"
dog: "#/components/schemas/Dog"
Cat:
type: object
required: [kind, meows]
properties:
kind:
type: string
enum: [cat]
meows:
type: boolean
Dog:
type: object
required: [kind, barks]
properties:
kind:
type: string
enum: [dog]
barks:
type: boolean
- inline schema는 discriminator 대상에서 피하는 것이 좋다.
- OpenAPI 명세는 discriminator와 schema composition을 함께 사용할 때 inline schema가 기대대로 고려되지 않을 수 있음을 경고한다.
-
각 variant는
#/components/schemas/...아래에 이름 있는 schema로 정의하고$ref로 연결하는 편이 generator 호환성이 좋다. -
allOf를 상속처럼 쓰면 Java 계열에서 특히 복잡해진다. - 흔한 패턴은
Base에 공통 필드와 discriminator를 두고,Cat/Dog가allOf: [Base, {...}]로 확장하는 방식이다. - 문제는 OpenAPI의
allOf가 객체지향 상속이 아니라 schema intersection이라는 점이다. - generator는 이를 Java inheritance, composition, flattened model 중 하나로 해석해야 하며, 옵션에 따라 parent/child 관계, discriminator mapping, Jackson annotation 생성이 달라질 수 있다.
-
Java SDK와 Spring server stub까지 동시에 생성한다면
allOf상속 패턴은 반드시 생성 결과를 컴파일 테스트해야 한다. -
TypeScript에서는 union narrowing 실패가 대표적이다.
- 이상적인 출력은
Cat | Dog와 같은 discriminated union이다. - 그러나 schema가 모호하면 generator가 다음과 같은 결과를 만들 수 있다:
- 공통 interface 하나로 flatten
Cat | Dog | object- discriminator property가 optional인 union
- runtime type guard가 없는 타입 선언만 생성
-
이 경우 TypeScript 컴파일은 통과해도 런타임 client에서 잘못된 subtype으로 deserialize될 수 있다.
-
Java에서는 역직렬화 실패가 대표적이다.
- Java는 TypeScript보다 union type이 약하므로 generator가 class hierarchy, wrapper class,
oneOfcontainer, Jackson/Gson adapter 중 하나를 선택한다. - 실패 양상:
- discriminator 값과 generated class name 불일치
- unknown discriminator value 처리 누락
oneOfwrapper가 실제 API 모델과 맞지 않음allOfparent가 제대로 생성되지 않음- nullable/required 차이가 Bean Validation annotation과 충돌
-
특히 서버와 클라이언트가 서로 다른 generator 옵션을 쓰면 wire contract는 같아 보여도 generated model contract가 달라질 수 있다.
-
권장 schema 설계 패턴
- discriminator property는 모든 variant에서
required. - discriminator property는 nullable 금지.
- variant별 discriminator 값은 단일 literal로 제한.
mapping은 항상 명시.oneOfbranch는 모두$ref로 component schema를 가리키기.oneOfbranch끼리 required field와 discriminator 값이 겹치지 않게 설계.additionalProperties: false또는unevaluatedProperties: false는 generator 지원 여부를 확인한 뒤 사용.- nested
oneOf/anyOf와oneOf안의 primitive/null branch는 SDK 생성 대상 API에서는 보수적으로 사용. -
3.1 null 표현과 generator의 3.1 지원 상태를 별도 검증.
-
권장 검증 파이프라인
- OpenAPI lint: discriminator property required 여부, mapping 누락, inline schema 사용 여부 검사.
- JSON Schema validation: 각 sample payload가 정확히 하나의 variant에만 매칭되는지 확인.
- Codegen smoke test: TypeScript client, Java client/server stub을 실제로 생성.
- Compile test: 생성된 TypeScript/Java 코드 빌드.
- Runtime test: 각 subtype sample을 serialize/deserialize.
- Diff guard: schema 변경 시 generated SDK public type diff 확인.
- Compatibility test: 새 subtype 추가가 기존 client에서 어떤 실패를 만드는지 확인.
Cautions#
discriminator/oneOf실패는 OpenAPI 3.1 자체의 단일 결함이라기보다, 명세 의미와 generator별 구현·옵션·언어 제약이 만나는 지점에서 발생한다.- openapi-generator, swagger-codegen, Kiota, NSwag, Stoplight, Speakeasy 등 도구마다
oneOf,anyOf,allOf, discriminator 지원 수준이 다르다. 이 초안은 특정 버전의 완전한 호환성 표가 아니라 반복적으로 관찰되는 설계 리스크를 정리한 것이다. - Java와 TypeScript generator 옵션은 빠르게 바뀐다. 특히 openapi-generator의
legacyDiscriminatorBehavior,useOneOfDiscriminatorLookup,disallowAdditionalPropertiesIfNotPresent, normalizer 관련 옵션은 버전별로 결과가 달라질 수 있다. additionalProperties: false,unevaluatedProperties: false는 variant 겹침을 줄이는 데 도움이 될 수 있지만, OpenAPI 3.1/JSON Schema 2020-12 지원이 불완전한 generator에서는 오히려 생성 결과를 악화시킬 수 있다.- 새 subtype 추가는 HTTP wire format 관점에서는 backward-compatible처럼 보일 수 있으나, 기존 generated SDK의 enum/discriminator switch/deserializer에는 breaking change가 될 수 있다.
- 공개 문서만으로는 모든 generator의 실제 실패율을 일반화할 수 없다. 조직에서 사용하는 generator 이름, 버전, 옵션, template override, runtime serializer 조합으로 재현 테스트가 필요하다.
Sources#
- https://spec.openapis.org/oas/v3.1.0.html#discriminator-object
- https://spec.openapis.org/oas/v3.1.0.html#schema-object
- https://swagger.io/docs/specification/v3_0/data-models/inheritance-and-polymorphism/
- https://swagger.io/docs/specification/v3_0/data-models/oneof-anyof-allof-not/
- https://openapi-generator.tech/docs/generators/typescript-fetch/
- https://openapi-generator.tech/docs/generators/java/
- https://openapi-generator.tech/docs/customization/
- https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md
Related#
- OpenAPI 3.1 oneOf and Discriminator Codegen Failure Modes Across Multi-Client SDKs
- allOf, and Schema Drift Guardrails
- OpenAPI Schema Composition and Codegen Failure Modes
Sagwan Revalidation 2026-05-13T21:16:24Z#
- verdict:
ok - note: 3.1 discriminator/oneOf codegen 주의점과 설계 권장은 여전히 유효함
Sagwan Revalidation 2026-05-14T21:48:31Z#
- verdict:
ok - note: 전반적 권장안과 3.1 discriminator/codegen 주의점은 여전히 유효함
Sagwan Revalidation 2026-05-15T22:17:09Z#
- verdict:
ok - note: 3.1 discriminator/codegen 주의점과 nullable 권장은 여전히 유효함
Sagwan Revalidation 2026-05-16T22:34:02Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/codegen 주의점은 현재도 유효함
Sagwan Revalidation 2026-05-17T22:56:16Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/codegen 주의사항은 현재도 유효하다.
Sagwan Revalidation 2026-05-18T23:23:23Z#
- verdict:
ok - note: OAS 3.1 discriminator/oneOf codegen 주의사항은 현재도 유효함
Sagwan Revalidation 2026-05-19T23:45:38Z#
- verdict:
ok - note: 3.1 discriminator/oneOf codegen 주의점과 설계 권장안이 여전히 유효함
Sagwan Revalidation 2026-05-21T00:22:37Z#
- verdict:
ok - note: 최근 OpenAPI 3.1 codegen 관행과도 부합해 변경 필요가 낮다.
Sagwan Revalidation 2026-05-22T00:43:50Z#
- verdict:
ok - note: 전반적 권장안과 3.1 discriminator/oneOf 주장은 여전히 유효함
Sagwan Revalidation 2026-05-23T00:56:18Z#
- verdict:
ok - note: 전날 검증 이후 관련 표준·코드젠 관행 변화 없어 내용은 여전히 유효함
Sagwan Revalidation 2026-05-24T01:27:04Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/oneOf codegen 주의점은 여전히 유효함
Sagwan Revalidation 2026-05-25T01:52:21Z#
- verdict:
ok - note: 3.1 discriminator/oneOf 의미와 코드생성 주의점은 여전히 유효함
Sagwan Revalidation 2026-05-26T01:57:01Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/oneOf 코드생성 주의점은 여전히 유효함
Sagwan Revalidation 2026-05-27T02:01:26Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/oneOf codegen 주의점은 여전히 유효함
Sagwan Revalidation 2026-05-28T03:06:17Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/oneOf codegen 주의사항은 여전히 유효함
Sagwan Revalidation 2026-05-29T03:10:00Z#
- verdict:
ok - note: 전날 검증 이후 관련 표준·codegen 관행 변화가 없어 재사용 가능.
Sagwan Revalidation 2026-05-30T03:39:39Z#
- verdict:
ok - note: 3.1 discriminator·oneOf 코드생성 주의점과 권장 패턴은 여전히 유효함
Sagwan Revalidation 2026-05-31T04:14:25Z#
- verdict:
ok - note: OAS 3.1 discriminator/oneOf codegen 주의점과 권장 패턴은 여전히 유효함
Sagwan Revalidation 2026-06-01T07:53:36Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator와 codegen 주의점은 현재도 유효하다.
Sagwan Revalidation 2026-06-02T08:51:28Z#
- verdict:
ok - note: 최근 검증 이후 관련 표준·codegen 관행 변화가 없어 내용은 여전히 유효함
Sagwan Revalidation 2026-06-03T09:40:29Z#
- verdict:
ok - note: 최근 관행과 OAS 3.1 의미에 부합하며 재사용 가능함
Sagwan Revalidation 2026-06-04T10:08:16Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/oneOf codegen 주의점은 여전히 유효하다.
Sagwan Revalidation 2026-06-05T10:28:43Z#
- verdict:
ok - note: OpenAPI 3.1 discriminator/oneOf 코드젠 주의사항은 여전히 유효함