Scarab 진단 필드 테스트 #028 — OpenAPI Generator Rust 서버 클라이언트 기능 경계
요약
OpenAPI Generator의 Rust 서버 생성기에서 발생하는 의존성 누락 버그를 분석합니다. 특정 기능(feature) 조합 사용 시 공유 모델 코드에 필요한 lazy_static 및 regex 의존성이 제대로 연결되지 않아 빌드가 실패하는 문제를 다룹니다.
핵심 포인트
- Rust 서버 생성 시 특정 기능 조합에서 의존성 누락 발생
- 생성된 코드의 기능 경계(boundary) 관리의 중요성
- Cargo feature wiring을 통한 정확한 의존성 연결 필요성
- 단순 의존성 추가가 아닌 제너레이터의 정확성 패치 관점
대상: OpenAPITools/openapi-generator
이슈: OpenAPITools/openapi-generator#23920
PR: OpenAPITools/openapi-generator#24023
필드 랩 (Field Lab): https://github.com/scarab-systems/scarab-field-lab/blob/main/field-tests/openapitools-openapi-generator-23920/README.md
이 필드 테스트는 OpenAPI Generator에서 rust-server 클라이언트 전용 빌드 시 생성된 공유 모델 (shared model) 코드에 필요한 의존성 (dependencies)을 누락할 수 있는 Rust 서버 생성기 버그를 대상으로 했습니다.
눈에 보이는 문제는 Rust 생성기가 클라이언트를 생성할 수 없다는 것이 아니었습니다.
더 구체적인 문제는 다음과 같았습니다:
- rust-server 프로젝트가 생성됨
- 기본 기능 (default features)을 비활성화한 상태로 빌드가 실행됨
- 클라이언트 (client) 기능이 활성화됨
- 생성된 공유 모델 코드가 여전히 패턴 검증 (pattern validation)을 방출할 수 있음
- 해당 패턴 검증이 lazy_static 및 regex를 필요로 할 수 있음
- 하지만 클라이언트 전용 기능 경로가 이러한 의존성들을 안정적으로 연결하지 못함
- 결과적으로 생성된 Rust 샘플이
cargo check --no-default-features --features client명령 하에서 실패할 수 있음
이것은 전형적인 생성된 코드 경계 (generated-code boundary) 버그입니다.
사용자는 누락된 의존성 사용을 직접 작성하지 않습니다.
생성기가 해당 크레이트 (crates)를 필요로 할 수 있는 코드를 방출하는 것입니다.
따라서 생성기는 또한 생성된 코드가 생성기가 지원한다고 주장하는 기능 조합 하에서 빌드될 수 있도록 Cargo 기능 연결 (Cargo feature wiring)을 방출해야 합니다.
진단 질문은 다음과 같지 않았습니다:
모든 Rust 서버 기능이 모든 것에 의존하게 만들려면 어떻게 해야 하는가?
더 나은 질문은 다음과 같았습니다:
어떤 생성된 코드 경로가 lazy_static 및 regex를 방출할 수 있으며, 어떤 Cargo 기능 경계가 해당 의존성들을 소유해야 하는가?
필드 랩 기록
이 필드 테스트에 대한 공개 사례 기록은 Scarab Field Lab에서 확인할 수 있습니다:
Upstream posture (업스트림 태세)
이것은 깔끔한 업스트림 수정(upstream repair) 후보였습니다.
이슈는 열려 있었고, 실패 현상은 좁은 기능 설정(feature configuration) 내에서 재현 가능했으며, 수정 범위는 OpenAPI Generator가 소유한 Rust 서버 템플릿 및 샘플 생성 경로 내에 있었습니다.
이것이 중요한 이유는 이것이 단순한 의존성 추가 요청(dependency wish-list) 패치가 아니었기 때문입니다.
이것은 제너레이터(generator)의 정확성(correctness) 패치였습니다.
제너레이터가 지원되는 기능 조합(feature combination) 이면에 있는 코드를 생성할 때, 생성된 Cargo 매니페스트(manifest)에는 해당 코드 경로에 필요한 의존성(dependencies)들이 포함되어야 합니다.
업스트림 PR은 깔끔하게 오픈되었으며 SDS, Scarab, Codex, 로컬 경로(local-path) 또는 내부 워크플로우 언어를 포함하지 않습니다.
SDS 결과
이 필드 테스트는 일회성(disposable) OpenAPI Generator 아레나를 대상으로 SDS 필드 테스트 태세(field-test posture)로 실행되었습니다.
유용한 결과는 경계가 명확한 소유권(bounded ownership) 판독이었습니다.
실패는 작지만 중요한 체인(chain) 전반에 걸쳐 존재했습니다:
- OpenAPI 스키마 입력 (schema input)
- 생성된 Rust 공유 모델 코드 (generated Rust shared model code)
- 패턴 검증 출력 (pattern-validation output)
- Cargo 의존성 및 기능 와이어링 (dependency and feature wiring)
- 클라이언트 전용 Rust 샘플 컴파일 (client-only Rust sample compilation)
이 체인은 제너레이터가 소유한 경계(generator-owned boundary)를 가리킵니다.
올바른 수정 방법은 검증(validation)을 제거하는 것이 아니었습니다.
사용자에게 크레이트(crates)를 수동으로 추가하라고 말하는 것도 아니었습니다.
모든 생성된 빌드가 항상 모든 의존성을 가져오도록 만드는 것도 아니었습니다.
올바른 방법은 rust-server Cargo 기능 와이어링(feature wiring)을 강화하여, 클라이언트 전용 빌드 시 생성된 공유 모델이 패턴 검증 코드를 생성할 때 해당 모델에 필요한 의존성들이 포함되도록 하는 것이었습니다.
Failure shape (실패 형태)
실패 형태는 기능/의존성 불일치(feature/dependency mismatch)였습니다.
생성된 Rust 코드는 lazy_static 및 regex를 요구할 수 있습니다.
클라이언트 전용 빌드 경로에서도 공유 모델을 컴파일할 수는 있습니다.
하지만 Cargo 기능 와이어링이 해당 생성된 공유 코드에 필요한 크레이트들을 안정적으로 포함하지 못했습니다.
이는 기능 조합(feature combination)이 내부적으로 일관되지 않기 때문에 생성된 프로젝트를 망가뜨립니다.
쉽게 말해:
제너레이터(generator)가 코드를 생성할 때, 선택된 기능 경로(feature path)가 제공하지 않는 크레이트(crate)를 필요로 하는 코드를 내보낸 것입니다.
이것은 Rust 컴파일러의 문제가 아닙니다.
이것은 사용자 애플리케이션의 문제도 아닙니다.
이것은 사용자가 수동으로 의존성(dependency) 추가를 잊어버린 경우도 아닙니다.
이것은 제너레이터 계약(generator contract)의 문제입니다.
만약 생성된 코드가 지원되는 기능 빌드(feature build) 하에서 특정 크레이트를 참조할 수 있다면, 생성된 매니페스트(manifest)는 동일한 기능 빌드 하에서 해당 크레이트를 사용할 수 있도록 만들어야 합니다.
경계 (Boundary)
여기서의 경계는 다음과 같습니다:
생성된 공유 모델 요구사항(generated shared model requirements) 대 생성된 Cargo 기능 와이어링(generated Cargo feature wiring)
OpenAPI Generator는 이 경계의 양쪽 모두를 소유합니다.
OpenAPI Generator는 생성된 Rust 모델 코드를 소유합니다.
OpenAPI Generator는 생성된 Cargo 매니페스트 템플릿을 소유합니다.
따라서 공유 모델이 lazy_static과 regex를 사용하여 패턴 검증(pattern validation)을 생성할 수 있다면, 해당 모델들을 컴파일하는 기능 경로(feature path)는 그에 맞는 의존성 와이어링을 반드시 포함해야 합니다.
이것이 수정 대상(repair surface)입니다.
이 패치는 Rust 서버 생성을 재설계하는 것이 아닙니다.
모델 검증을 제거하는 것도 아닙니다.
모든 Cargo 기능을 하나의 광범위한 의존성 세트로 평탄화(flatten)하는 것도 아닙니다.
실제 생성된 코드의 요구사항에 맞춰 기능 와이어링을 더 엄격하게 조이는 것입니다.
이것이 Scarab의 경계입니다:
생성된 코드와 생성된 빌드 메타데이터가 불일치할 때, 계약이 깨지는 가장 작은 소유 영역(owned seam)을 수리하는 것
변경 사항
이 PR은 rust-server의 Cargo 기능 와이어링을 강화하여, 클라이언트 전용 빌드(client-only builds) 시에도 생성된 공유 모델 검증 코드에 필요한 의존성들이 포함되도록 합니다.
관련된 특정 의존성 경로는 lazy_static과 regex를 포함하며, 이는 생성된 공유 모델이 패턴 검증 로직을 생성할 때 필요할 수 있습니다.
패치는 해당 기능 조합에 대한 집중적인 회귀 고정 장치(regression fixture)와 테스트를 추가합니다.
또한 영향을 받는 Rust 서버 샘플을 재생성하여, 체크인된 생성된 결과물이 수정된 템플릿 동작을 반영하도록 합니다.
이것은 이 프로젝트가 제너레이터 프로젝트이기 때문에 매우 중요합니다.
템플릿만 변경된다면 수정이 완료된 것이 아닙니다.
생성된 샘플(generated samples) 또한 수정된 출력을 보여주어야 하며, 회귀 테스트 (regression test)를 통해 기능 조합이 여전히 빌드 가능한 상태임을 증명해야 합니다.
이것이 광범위한 의존성 패치 (dependency patch)가 아니었던 이유
유혹적인 해결책은 생성된 Rust 서버 매니페스트 (manifest)가 모든 곳에서 더 많은 의존성을 포함하도록 만드는 것이었습니다.
그것이 더 쉬운 방법이겠지만, 정밀도는 떨어질 것입니다.
버그의 원인은 다음과 같지 않았습니다:
Rust 서버 생성 시 모든 모드에서 모든 의존성이 필요하다.
버그의 원인은 다음과 같았습니다:
클라이언트 전용 기능 (client-only feature) 빌드 시에도 lazy_static과 regex를 필요로 하는 공유 생성 모델 코드가 여전히 컴파일될 수 있다.
따라서 수정 작업은 기능 경계 (feature boundary) 근처에서 이루어졌습니다.
이는 Cargo 기능 설계 (feature design)가 생성된 프로젝트의 계약 (contract)의 일부이기 때문에 중요합니다.
의존성을 과도하게 연결 (over-wiring)하면 버그를 숨길 수는 있지만, 생성된 결과물의 규율을 떨어뜨리게 됩니다.
더 나은 수정 방법은 기능 경로 (feature path)를 정직하게 만드는 것입니다:
만약 클라이언트 기능이 공유 모델 검증 코드를 컴파일할 수 있다면, 클라이언트 기능은 해당 검증 코드가 요구하는 의존성을 반드시 포함해야 합니다.
진단 결과가 중요했던 이유
이 사례는 실제 플랫폼에 영향을 미치는 또 다른 작은 패치라는 점에서 유용합니다.
실패가 시각적으로 극적이지는 않습니다.
UI가 깨지는 것도 아닙니다.
대규모 서브시스템을 재작성하는 것도 아닙니다.
하지만 생성된 Rust 출력물에 의존하는 누구에게나, 클라이언트 전용 기능 빌드가 깨지는 것은 실제적인 실패입니다.
생성된 코드는 종종 CI 파이프라인, SDK 빌드, 통합 테스트 (integration tests), 그리고 다운스트림 애플리케이션 작업에 투입됩니다. 만약 제너레이터가 자체적으로 지원하는 기능 조합 중 하나에서 실패하는 프로젝트를 방출한다면, 다운스트림 사용자는 제너레이터에 대한 신뢰를 잃게 됩니다.
진단적 태도 (diagnostic posture)의 가치는 수정을 실제 계약 (contract)을 중심으로 프레임화했다는 점에 있습니다:
- OpenAPI Generator는 모델 코드 (model code)를 생성합니다.
- OpenAPI Generator는 Cargo 기능 연결 (feature wiring)을 생성합니다.
- 선택된 기능 (feature) 조합은 생성기가 생성하는 코드를 충족해야 합니다.
- 수정 사항은 생성된 코드 요구 사항과 생성된 빌드 메타데이터 (build metadata)가 만나는 지점에 위치해야 합니다.
그러한 프레임워크 덕분에 패치 (patch)의 규모를 작게 유지할 수 있었습니다.
이는 광범위한 Rust 서버 재작성 (rewrite)을 피할 수 있게 해주었습니다.
사용자 측의 임시 방편 (workaround)을 피할 수 있게 해주었습니다.
생성기가 소유한 경계면 (seam)을 수리했습니다.
검증 (Validation)
패치는 집중 검사 (focused check)와 전체 프로젝트 검사 (full project check)를 통해 모두 검증되었습니다.
다음 항목들에 대해 검증을 통과했습니다:
- 타겟 회귀 테스트 (targeted regression test)
cargo check --no-default-features --features client를 사용한 Rust 샘플 확인- 전체
./mvnw clean package실행 - 769개 생성기에 대한 전체 샘플 생성
./bin/utils/export_docs_generators.sh실행git diff --check실행
이 보고서 작성 시점에 PR (Pull Request)은 오픈되어 있으며, 리뷰 준비가 완료되었고 머지 (merge) 가능한 상태입니다.
CircleCI 노드는 녹색 (green)입니다.
Cubic AI 리뷰도 녹색 (green)입니다.
전체 검증 기록과 공개 상태를 확인하려면 Field Lab 사례 기록을 참조하십시오:
현장 테스트 결과 (Field test result)
이것은 OpenAPI Generator를 위한 제한된 범위의 Rust 서버 기능 연결 (feature-wiring) 수리 후보였습니다.
문제는 다음과 같이 요약됩니다:
- rust-server 클라이언트 전용 빌드가 생성된 공유 모델 (shared models)을 컴파일할 수 있음
- 생성된 공유 모델이 패턴 검증 (pattern validation) 코드를 생성할 수 있음
- 해당 검증 코드가
lazy_static및regex를 요구할 수 있음 - 클라이언트 기능 경로 (client feature path)가 해당 의존성 (dependencies)을 안정적으로 제공하지 못함
- 생성된 Rust 프로젝트가
cargo check --no-default-features --features client환경에서 실패할 수 있음 - 수리를 통해 소유된 생성 코드 경로에 대한 Cargo 기능 연결 (feature wiring)을 강화함
- 집중 회귀 고정 장치/테스트 (regression fixture/test)를 추가함
- 영향을 받는 Rust 서버 샘플들을 재생성함
- 전체 검증을 통과함
이것이 수리 경로 (repair lane)입니다.
이 패치는 Rust 서버 생성을 재설계한다고 주장하지 않습니다.
이 패치는 OpenAPI 검증 의미론 (semantics)을 변경한다고 주장하지 않습니다.
이 패치는 생성된 모든 기능 (feature)이 모든 의존성 (dependency)을 포함해야 한다고 주장하지 않습니다.
이 패치는 생성된 공유 모델 코드 (shared model code)와 생성된 Cargo 메타데이터 (metadata)가 서로 일치하지 않았던 기능 경계 (feature boundary)를 수정합니다.
공식 주장 (Public claim)
이 필드 테스트에 대한 올바른 주장은 다음과 같습니다:
Scarab/SDS는 OpenAPITools/openapi-generator#23920에 대한 제한된 수리 후보 (bounded repair candidate)를 도출하는 데 기여했습니다. 해당 이슈에서는 Rust 서버의 client-only 빌드가 lazy_static 및 regex를 누락할 수 있는 문제가 있었는데, 이는 생성된 공유 모델 코드가 해당 크레이트 (crates)를 필요로 하는 패턴 검증 (pattern validation)을 방출할 수 있었음에도 불구하고 발생했습니다. 업스트림 PR (upstream PR)은 Rust 서버의 Cargo 기능 와이어링 (feature wiring)을 강화하고, 집중적인 회귀 고정 장치/테스트 (regression fixture/test)를 추가하며, 영향을 받는 Rust 서버 샘플들을 재생성합니다. 검증은 타겟 회귀 테스트, cargo check --no-default-features --features client, 전체 Maven 패키지 빌드, 769개 생성기에 대한 전체 샘플 생성, 생성기 문서 내보내기, 그리고 공백 검사 (whitespace checks)를 통해 통과되었습니다. 이는 Rust 서버 생성 방식이나 Cargo 기능 정책을 광범위하게 재설계한다고 주장하는 것이 아닙니다. 대신, 클라이언트 기능 경로 (client feature path)가 생성된 공유 모델 검증에 필요한 의존성을 전달하지 못했던 생성 코드/빌드 메타데이터 사이의 경계를 수정하는 것입니다.
공개 (Disclosure): 이 필드 보고서는 본인의 필드 테스트 노트, 공개된 이슈 및 PR 기록, 검증 요약, 그리고 수리 기록을 바탕으로 AI 지원 편집을 통해 작성되었습니다. 기술적 주장과 최종 문구는 발행 전 검토를 거쳤습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기