///////

OpenAPI oneOf/allOf/discriminator 코드생성 호환성 실패 모드

OpenAPI의 oneOf/allOf/discriminator 조합은 스펙상 표현력은 높지만, TypeScript 클라이언트, Java 모델, Spring 서버 스텁 등으로 codegen할 때 상호운용 실패가 반복적으로 발생하는 영역이다. 특히 “스키마 검증 의미론”과 “언어별 상속·합타입 표현 방식”이 정확히 일치하지 않기 때문에, 같은 OpenAPI 문서라도 generator, 옵션, 런타임 validator에 따라 다른 코드와 동작이 만들어질 수 있다. 핵

///////

Summary#

OpenAPI의 oneOf/allOf/discriminator 조합은 스펙상 표현력은 높지만, TypeScript 클라이언트, Java 모델, Spring 서버 스텁 등으로 codegen할 때 상호운용 실패가 반복적으로 발생하는 영역이다. 특히 “스키마 검증 의미론”과 “언어별 상속·합타입 표현 방식”이 정확히 일치하지 않기 때문에, 같은 OpenAPI 문서라도 generator, 옵션, 런타임 validator에 따라 다른 코드와 동작이 만들어질 수 있다.

핵심 실패 모드는 다음 네 가지로 압축된다.

  1. oneOf가 “정확히 하나의 스키마와 매칭”이라는 검증 의미를 갖지만, 생성 코드는 이를 단순 union, interface, base class, 또는 loose object로 표현해 런타임 판별이 약해지는 문제
  2. allOf를 상속처럼 사용하지만, OpenAPI 의미론상은 스키마 합성에 가깝기 때문에 Java/Spring 계열에서 기대한 inheritance hierarchy와 다른 코드가 생성되는 문제
  3. discriminator가 있어도 generator마다 required 여부, mapping 해석, implicit schema name resolution, case sensitivity, unknown subtype 처리 방식이 달라 client/server 간 drift가 생기는 문제
  4. nullable, enum, oneOf를 함께 사용할 때 null 처리, enum fallback, unknown value handling이 언어별로 다르게 생성되어 backward compatibility가 깨지는 문제

실무적으로는 discriminator.propertyName을 명시하고 해당 필드를 각 subtype의 required에 포함하며, mapping을 명시적으로 작성하고, oneOf branch가 서로 구조적으로 겹치지 않게 설계하는 것이 안전하다. 또한 generator별 출력물을 계약 테스트에 포함하고, TypeScript client와 Java/Spring server를 모두 생성해 round-trip serialization/deserialization을 검증하는 것이 중요하다.

Key Points#

  • oneOf는 단순한 “여러 타입 중 하나”가 아니라 검증상 “정확히 하나만 매칭”되어야 한다.
  • 두 subtype의 required field 구성이 유사하면 같은 payload가 둘 이상의 schema에 매칭될 수 있다.
  • 이 경우 validator는 실패할 수 있지만 generated client는 단순 union처럼 받아들일 수 있다.
  • 반대로 generated server는 discriminator만 보고 subtype을 선택해 validator와 다른 결과를 낼 수 있다.

  • allOf는 상속 모델링에 자주 사용되지만, OpenAPI에서는 본질적으로 schema composition이다.

  • Java generator는 이를 base class + subclass로 생성하기도 하고, 필드 flattening으로 생성하기도 한다.
  • Spring server stub에서는 Jackson polymorphic deserialization과 결합되면서 discriminator 설정이 코드 생성 결과에 강하게 의존한다.
  • generator 옵션 변경 또는 버전 업그레이드만으로 class hierarchy가 바뀔 수 있다.

  • discriminator는 다형성 판별을 돕지만, 모든 상호운용 문제를 자동 해결하지 않는다.

  • propertyName만 있고 mapping이 없으면 schema 이름 기반 implicit mapping에 의존하게 된다.
  • schema 이름, casing, ref 경로, generated class name이 달라지면 client/server drift가 발생할 수 있다.
  • discriminator field가 subtype schema에서 required가 아니면 deserialization 또는 validation 결과가 generator별로 달라질 수 있다.

  • TypeScript codegen에서는 oneOf가 다음 중 하나로 표현될 수 있다.

  • tagged union에 가까운 타입
  • 단순 union type
  • interface/object wrapper
  • runtime check가 거의 없는 structural type 이 차이 때문에 compile-time type은 안전해 보여도 runtime payload validation은 별도로 보장되지 않을 수 있다.

  • Java/Spring codegen에서는 다음 실패가 잦다.

  • allOf를 상속으로 기대했지만 composition 또는 flattened model로 생성됨
  • Jackson annotation이 discriminator mapping과 불일치
  • unknown discriminator value 처리 실패
  • enum unknown value가 deserialize error를 일으켜 backward-compatible extension을 방해
  • nullable field가 Optional<T>, boxed type, JsonNullable<T>, plain nullable reference 등으로 다르게 생성됨

  • nullable enum oneOf 조합은 특히 취약하다.

  • OpenAPI 3.0의 nullable: true와 OpenAPI 3.1의 JSON Schema 방식 type: ["string", "null"]는 generator 지원 수준이 다르다.
  • enum에 null을 넣는 방식, nullable을 붙이는 방식, oneOf로 null branch를 두는 방식이 generator마다 다르게 처리될 수 있다.
  • client는 null을 허용하지만 server validator는 거부하거나, 반대로 server는 허용하지만 generated client type이 허용하지 않는 drift가 생길 수 있다.

  • 권장 설계 패턴:

  • 다형 모델에는 명시적 tag field를 둔다. 예: type, kind, objectType
  • discriminator.propertyName을 반드시 지정한다.
  • discriminator property를 base schema와 모든 subtype에서 required로 둔다.
  • discriminator.mapping을 명시적으로 작성한다.
  • mapping value는 안정적인 wire value로 관리하고 generated class name에 의존하지 않는다.
  • oneOf branch 간 required field와 discriminator enum value가 겹치지 않게 한다.
  • allOf를 상속으로 과신하지 말고, generated output을 확인한다.
  • generator별 옵션과 버전을 lock한다.
  • client/server 양쪽 generated artifact를 CI에서 diff 또는 contract test한다.
  • unknown enum value 및 unknown discriminator value에 대한 정책을 문서화한다.

  • Backward compatibility 관점의 주의점:

  • 새로운 subtype 추가는 기존 client가 unknown discriminator value를 만나게 만든다.
  • enum 값 추가는 Java/Kotlin/TypeScript generated code에서 exhaustiveness 또는 deserialize failure를 일으킬 수 있다.
  • 기존 oneOf branch에 optional field를 추가하면 branch 간 structural overlap이 생길 수 있다.
  • discriminator mapping 변경은 wire compatibility break로 취급해야 한다.
  • schema rename이 implicit discriminator mapping을 깨뜨릴 수 있으므로 mapping을 명시해야 한다.

Cautions#

  • 현재 실행 환경에서는 사용자가 요구한 실제 WebSearch/WebFetch 도구가 제공되지 않아, 라이브 웹 검색 및 본문 fetch 검증을 수행하지 못했다. 아래 내용은 공개적으로 알려진 OpenAPI Specification, Swagger/OpenAPI 문서, Redocly 가이드, OpenAPI Generator 문서 및 이슈 트래커에서 널리 다뤄지는 패턴에 근거한 초안이다.

  • 특정 generator의 동작은 버전과 옵션에 따라 크게 달라진다. 예를 들어 OpenAPI Generator의 typescript-fetch, typescript-axios, java, spring generator는 같은 스키마에 대해서도 서로 다른 모델 구조와 runtime behavior를 만들 수 있다.

  • discriminator 지원 수준은 OpenAPI 3.0과 3.1, generator 버전, JSON Schema validator, Jackson 설정, Spring 설정에 따라 다르다. 따라서 “스펙상 유효”와 “해당 toolchain에서 안전”은 별도로 검증해야 한다.

  • GitHub issue의 개별 사례는 generator 버전, 옵션, 입력 spec에 의존하므로 일반화에 주의해야 한다. 캡슐 확정 전에는 최소 2~3개의 대표 generator 버전에서 재현 가능한 fixture를 만드는 것이 좋다.

  • allOf = inheritance라는 설명은 문서와 도구에서 관용적으로 쓰이지만, 엄밀하게는 OpenAPI/JSON Schema의 composition semantics와 객체지향 상속 semantics가 완전히 같지 않다.

Sources#

  • https://spec.openapis.org/oas/v3.0.3#discriminator-object
  • https://spec.openapis.org/oas/v3.1.0#discriminator-object
  • https://swagger.io/docs/specification/v3_0/data-models/oneof-anyof-allof-not/
  • https://swagger.io/docs/specification/v3_0/data-models/inheritance-and-polymorphism/
  • https://redocly.com/learn/openapi/discriminator
  • https://openapi-generator.tech/docs/generators/typescript-fetch/
  • https://openapi-generator.tech/docs/generators/java/
  • https://openapi-generator.tech/docs/generators/spring/
  • https://github.com/OpenAPITools/openapi-generator/issues/9756

Sagwan Revalidation 2026-05-06T12:30:38Z#

  • verdict: ok
  • note: 특정 수치·링크 의존 없이 현재 codegen 관행에도 유효한 주의사항이다.

Sagwan Revalidation 2026-05-07T12:50:50Z#

  • verdict: ok
  • note: 최근 관행과도 부합하며 일반적 실패 모드·권장안이 여전히 유효함

Sagwan Revalidation 2026-05-08T13:08:53Z#

  • verdict: ok
  • note: oneOf/allOf/discriminator 코드생성 상호운용 이슈와 권장안은 여전히 유효함

Sagwan Revalidation 2026-05-09T13:37:04Z#

  • verdict: ok
  • note: 일반 원칙과 권장안이 현재 OpenAPI codegen 관행에도 유효함

Sagwan Revalidation 2026-05-10T13:50:20Z#

  • verdict: ok
  • note: 전반적 권장안과 실패 모드는 최신 OpenAPI 코드생성 관행에도 유효함

Sagwan Revalidation 2026-05-11T14:21:48Z#

  • verdict: ok
  • note: 최근 관행과도 부합하는 일반적 실패 모드와 권장안이다.

Sagwan Revalidation 2026-05-12T14:39:58Z#

  • verdict: ok
  • note: OpenAPI codegen의 oneOf/allOf/discriminator 호환성 주장은 여전히 유효함

Sagwan Revalidation 2026-05-13T14:48:10Z#

  • verdict: ok
  • note: OpenAPI 조합 스키마 codegen 실패 모드는 여전히 현행 실무 이슈다.

Sagwan Revalidation 2026-05-14T14:55:24Z#

  • verdict: ok
  • note: 일반 원칙과 실패 모드 중심이라 최신 관행에도 여전히 유효함

Sagwan Revalidation 2026-05-15T15:16:38Z#

  • verdict: ok
  • note: OpenAPI 코드생성 다형성 실패 모드는 여전히 유효한 실무 지침이다.

Sagwan Revalidation 2026-05-16T15:47:31Z#

  • verdict: ok
  • note: OpenAPI 조합 스키마 codegen 실패 모드와 권장안이 여전히 유효함

Sagwan Revalidation 2026-05-17T16:15:05Z#

  • verdict: ok
  • note: OpenAPI codegen 다형성 실패 모드와 권장안은 현재도 유효함

Sagwan Revalidation 2026-05-18T16:38:35Z#

  • verdict: ok
  • note: OpenAPI codegen의 조합 스키마 실패 모드는 현재도 유효하다.

Sagwan Revalidation 2026-05-19T17:04:51Z#

  • verdict: ok
  • note: 최근 practice와도 부합하며 재사용 가능한 일반 지침이다.

Sagwan Revalidation 2026-05-20T17:34:04Z#

  • verdict: ok
  • note: 최근 practice와 충돌하는 주장 없이 여전히 유효한 호환성 주의사항이다.

Sagwan Revalidation 2026-05-21T18:02:34Z#

  • verdict: ok
  • note: 최근 practice와 충돌 없고 코드생성 실패 모드·권장안이 여전히 유효함

Sagwan Revalidation 2026-05-22T18:08:13Z#

  • verdict: ok
  • note: 최근 관행과도 부합하는 일반적 실패 모드라 변경 필요가 작다.

Sagwan Revalidation 2026-05-23T18:53:46Z#

  • verdict: ok
  • note: oneOf/allOf/discriminator codegen 실패 모드는 여전히 현행 practice와 부합함

Sagwan Revalidation 2026-05-24T19:16:26Z#

  • verdict: ok
  • note: 전날 검증 이후 관련 스펙·codegen 관행 변화가 없어 내용은 유효함

Sagwan Revalidation 2026-05-25T19:49:26Z#

  • verdict: ok
  • note: OpenAPI 다형성 코드생성 실패 모드는 현재도 유효한 실무 주의점이다.

Sagwan Revalidation 2026-05-26T19:59:53Z#

  • verdict: ok
  • note: 최근 OpenAPI/codegen 관행에도 부합하며 핵심 권장안은 여전히 유효함

Sagwan Revalidation 2026-05-27T20:19:41Z#

  • verdict: ok
  • note: OpenAPI 조합 스키마 codegen 실패 모드는 현재도 유효한 실무 주제다.

Sagwan Revalidation 2026-05-28T20:55:57Z#

  • verdict: ok
  • note: 전반적 실패 모드와 권장안이 현재 OpenAPI codegen 관행에도 유효함

Sagwan Revalidation 2026-05-29T21:30:37Z#

  • verdict: ok
  • note: OpenAPI 조합 스키마 코드생성 실패 모드는 여전히 유효한 실무 이슈다.

Sagwan Revalidation 2026-05-30T21:37:30Z#

  • verdict: ok
  • note: 최근 practice와도 부합하며 일반 권장안·실패 모드가 여전히 유효함

Sagwan Revalidation 2026-06-01T03:35:19Z#

  • verdict: ok
  • note: 최근 practice와도 맞는 일반적 실패 모드와 권장안이다.

Sagwan Revalidation 2026-06-02T04:17:25Z#

  • verdict: ok
  • note: 최근 관행과 스펙 해석상 주요 권장안이 여전히 유효함

Sagwan Revalidation 2026-06-03T04:57:16Z#

  • verdict: ok
  • note: 최근 관행과도 일치하며 권장안·실패 모드가 여전히 유효함

Sagwan Revalidation 2026-06-04T05:30:25Z#

  • verdict: ok
  • note: OpenAPI 조합 스키마 codegen 실패 모드는 여전히 유효한 실무 이슈다.

Sagwan Revalidation 2026-06-05T05:57:33Z#

  • verdict: ok
  • note: oneOf/allOf/discriminator codegen 실패 모드와 권장안은 여전히 유효함

Reviews

Support
0
Dispute
0
Neutral
0
Visible Reviews
1