OpenAPI 역공학(reverse-engineering)을 위한 AST와 AI: 정적 분석이 멈추고 모델이 역할을 하는 지점
요약
OpenAPI 문서 생성 과정에서 LLM의 비결정성 및 정적 분석의 한계를 지적합니다. 성공적인 접근 방식은 정적 분석이 담당하는 결정론적 핵심(라우트, 타입)과 모델이 보조하는 선택적 인텔리전스 영역을 엄격히 분리하여 신뢰성을 확보해야 합니다.
핵심 포인트
- LLM만으로 OpenAPI를 생성하면 가짜 정보가 생기고 재현성이 떨어집니다.
- 순수 정적 분석은 런타임 계산(SerializerMethodField 등)으로 인해 놓치는 부분이 많습니다.
- 신뢰할 수 있는 파이프라인은 결정론적 핵심과 선택적 인텔리전스를 분리해야 합니다.
‘코드에서 API 문서를 생성’하는 분야에는 두 가지 접근 방식이 지배적입니다. 하나는 ‘LLM에 리포지토리를 보여주면 모든 것을 이해할 것’이라고 주장합니다. 다른 하나는 ‘정적 분석은 결정론적이니 파일을 파싱하기만 하면 된다’고 말합니다. 하지만 이 둘 다 실제 백엔드에서는 각기 다른 이유로 실패합니다. 실제로 프로덕션에서 작동하는 디자인들은 엄격한 역할 분담을 사용합니다. 정적 분석이 라우트(routes)와 타입(types)이라는 폐쇄적인 영역을 소유하고, 모델은 정적 분석이 알 수 없다고 증명할 수 있는 좁은 부분에만 접근하도록 허용합니다.
모든 것을 AI로 처리하는 방식이 실패하는 이유
전체 리포지토리를 채팅 모델에 넣어 OpenAPI를 요청하는 것은 세 개의 라우트 데모에서는 인상적이지만 실제 서비스에서는 위험합니다.
- 가짜 정보를 생성합니다(It invents). 모델은 존재하지 않는 그럴듯한 엔드포인트와 필드를 표면화하고, 사소하지만 중요한 것들은 누락시킵니다. 방출된 작업(operation)과 코드 내의 증거를 연결하는 메커니즘이 없습니다.
- 재현성이 떨어집니다(It is not reproducible). 같은 코드를 두 번 스캔해도 다른 필수 필드와 열거형(enums)을 산출할 수 있어, 출력을 CI에서 게이트(gate)하기가 불가능합니다.
- 정보를 과도하게 노출합니다(It over-exposes). 응답 형태를 요청받았을 때, 모델은 엔드포인트가 절대 반환하지 않는 내부 플래그나 자격 증명 관련 필드를 포함하여 전체 데이터베이스 엔티티를 직렬화하는 경향이 있습니다.
- 소스 코드가 유출됩니다(It leaks source). 관련된 계약(contract)이 아니라 전체 코드베이스가 제3자에게 전송되므로, 독점적인 백엔드에는 적합하지 않습니다.
- 불확실성을 숨깁니다(It hides uncertainty). 자신감 있어 보이는 스키마는 어떤 필드가 증명되었고 어떤 필드는 추측된 것인지에 대한 신호를 제공하지 않습니다.
OpenAPI 문서는 계약입니다. 계약에는 증거와 반복 가능성이 필요한데, 이는 자유 형식의 모델이 강점을 가진 부분이 아닙니다.
순수 정적 분석이 눈이 멀어버리는 이유
반대 극단인, 의미론적 계층(semantic layer) 없이 정규 표현식(regexes)과 AST 워크를 사용하는 방식은 다른 방식으로 실패합니다. 동적 프레임워크는 구문만으로는 따라갈 수 없는 헬퍼(helpers), 직렬화기(serializers), 매퍼(mappers)에서 응답을 구성하기 때문입니다.:
- 여러 서비스로부터 응답을 구성하는 계산된 직렬화기(computed serializer)는 파서가 완전히 해결할 수 없는 반환 형태를 가집니다.
- Django의
SerializerMethodField나 런타임에 구축되는 선택지 목록(choices list)은 정적 열거형(static enum)이 없습니다. - 오류 및 성공 분기를 거쳐 여러 패키지에 걸친 헬퍼로 구성된 응답은 성공 스키마가 부분적으로 알려지지 않은 상태를 남깁니다.
- 수동으로 구현된 파싱을 통해 강제 변환되는 쿼리 매개변수는 단순한 타입 추측이 시사하는 것보다 더 넓은 세트를 허용합니다.
완전히 증명할 수 없는 것은 아무것도 내보내지 않는 도구는 unknown으로 가득 찬 문서를 생성하고, 팀들은 그 도구를 사용하지 않게 됩니다. 가정(assumption)을 통해 이러한 구멍들을 채우는 도구는 거짓말하는 문서를 생성합니다. 정직한 대답은 세 번째 상태와 이를 해결하기 위한 제약된 방식입니다.
작동하는 파이프라인
신뢰할 수 있는 스캐너는 결정론적 핵심(deterministic core)과 선택적 인텔리전스(optional intelligence)를 분리합니다:
indexer
-> language packs (AST와 존재할 경우 타입 체커)
-> framework packs (라우트 그래프, 핸들러, 인스턴스 추적)
...
경로(Routes)는 실제 프레임워크 인스턴스를 추적하여 확립되는 폐쇄된 세계이므로, cache.get(...)와 같은 유사한 호출은 결코 엔드포인트로 오인되지 않으며, 마운트되지 않은 라우터는 도달할 수 없는 것으로 보고됩니다. TypeScript 프로젝트는 컴파일러 체커를 사용하여 제네릭(generics), 명명된 인터페이스(named interfaces), 열거형(enums), 유틸리티 타입(utility types) 및 검증 스키마를 해결합니다. 다른 언어들은 언어 툴체인이 설치되지 않은 상태에서 tree-sitter WASM을 통해 파싱됩니다. 이 패스 이후, 모든 매개변수, 요청 본문, 응답은 정확히 세 가지 상태 중 하나에 있습니다: 증거로 증명됨(proven with evidence), 증명이 없음(proven absent), 또는 query-unknown, body-schema-unknown, response-unknown, auth-unknown, 또는 sse-events-unknown과 같은 코드를 지닌 명시적 간극(explicit gap)입니다.
AI가 적합한 것은 오직 세 번째 상태뿐입니다.
모델을 정직하게 유지하는 세 가지 규칙
간극 해결사(gap resolver)가 활성화되면, 세 가지 제약 조건이 그것이 위장된 올-AI 생성기가 되는 것을 방지합니다.
1. 경로(route), 메서드(method) 또는 경로는 절대 임의로 생성할 수 없습니다. 작업 집합은 모델 호출 이전에 프레임워크 추적(framework tracing)에 의해 고정됩니다. 리졸버는 스캐너가 이미 존재함을 증명한 작업의 속성들을 채웁니다. 아무리 그럴듯해 보여도 엔드포인트(endpoint)를 추가할 수 없습니다.
2. 전체 파일이 아닌, 간극을 포함하는 가장 작은 조각만 봅니다. 응답 스키마가 아직 증명되지 않은 핸들러의 경우, 모델은 주변 모듈, 다른 경로 또는 관련 없는 비밀 정보가 아니라 해당 핸들러의 조각과 특정 간극 코드만을 받습니다. 스캐닝 패키지 자체는 모델 공급업체(model vendor)를 호출하지 않습니다. 호스트 애플리케이션이 사용자의 자체 모델 구성을 사용하여 명시적인 옵트인(opt-in)을 통해 호출합니다. 소스 코드는 기본적으로 어딘가로 전송되지 않습니다.
3. 출력은 제한되고 검토 가능합니다. 응답은 엄격한 JSON Schema 서브셋에 대해 검증됩니다: $ref 없음, 바운드된 깊이 및 속성 개수. 리졸버는 쿼리 매개변수(query parameters), 헤더(headers), 요청 본문(request bodies), 상태 기반 응답 스키마(status-keyed response schemas) 및 SSE 이벤트 페이로드(SSE event payloads)를 채울 수 있습니다. 데스크톱 워크플로우에서는 각 제안이 사람이 승인, 편집 또는 거부하는 검토 항목으로 표시됩니다. 실패하거나 거부된 채움은 결코 치명적이지 않으며 숨겨지지 않습니다. 간극은 보고서에 계속 보이게 됩니다.
null을 반환하는 실패한 채움(fill)은 좋은 결과입니다. 이는 알려지지 않은 계약(contract)이 자신감 있는 추측으로 승격되는 대신 '알려지지 않음' 상태로 유지되었음을 의미합니다.
재현성(Reproducibility)은 우연이 아니라 기능입니다
AI 기반 스캐닝이 CI(Continuous Integration)에 포함되려면 동일한 입력값이 항상 동일한 결과를 생성해야 합니다. 결정론적 추출(Deterministic extraction)은 이미 이 기능을 수행합니다. 모델 단계는 프롬프트 계약 버전(prompt-contract version)과 핸들러 슬라이스 및 프롬프트 버전을 키로 하는 캐시(cache)에 고정되며, 온도 0(temperature zero)에서 호출되므로 변경되지 않은 핸들러는 이전 해결 결과를 재사용합니다. 출처 정보(Provenance)는 생성된 문서가 아닌 .powerduck/discovery.json 사이드카(sidecar)에 저장되어 OpenAPI 자체를 깨끗하고 편집 가능하게 유지합니다. 재스캔 시, 세 방향 병합(three-way merge)을 적용하여 새로운 증거를 반영하는 동시에 수동 편집 내용을 권위 있는 것으로 처리합니다: 변경된 경로는 구조적 계약을 새로 고치지만 설명(descriptions), 예시(examples), 태그(tags), 확장(extensions)은 유지하고, 제거된 경로는 조용히 삭제되기보다 플래그가 지정됩니다. 최종적으로 문서는 항상 인간이 소유합니다.
모델이 진정으로 가치를 발휘하는 영역
모델은 정적 증명만으로는 코드를 실행하지 않고는 불가능한 지점에서 가치가 있습니다: 계산된 매퍼(computed mapper)의 출력을 추론하거나, SerializerMethodField에 대한 스키마를 제안하거나, 크로스 패키지 헬퍼 뒤에 숨겨진 형태를 제안하는 경우입니다. 이러한 제안들을 가설(hypotheses)로 취급하십시오. 각 가설은 테스트용 더미 데이터(fixture)를 사용한 실제 요청으로 입증되거나, 명시적인 알 수 없음(unknown) 상태로 하향 조정되거나, 인간에 의해 수정됩니다. 가장 강력한 파이프라인은 이 세 가지 계층을 순서대로 실행합니다: 먼저 정적 증명, 그 다음 잔여 격차(residual gaps)를 위한 제약된 AI, 그리고 계약이 소스에서 증명될 수 없을 때 최종 심판관 역할을 하는 라이브 요청입니다.
기본값을 선택하기
- CI 게이팅 및 소스를 기기 외부로 내보낼 수 없는 모든 저장소에는 정적 전용(static-only) 출력을 사용하십시오. 격차 보고서(gap report)는 여전히 정확한 작업 목록입니다.
- 동적 부분의 초안을 원하고 검토할 준비가 되어 있을 때 **AI 격차 채우기(AI gap fills)**를 활성화하십시오. 이 경우 검토 단계를 필수적으로 유지해야 합니다.
- 보안 또는 결제와 관련된 모든 영역에서는 **런타임 검증(runtime verification)**을 활용하십시오. 여기서는 잘못된 필드가 알 수 없는 상태보다 더 큰 비용을 초래할 수 있습니다.
사고방식의 전환은 스캐너를 출력 결과가 얼마나 완벽해 보이는지로 측정하는 것을 멈추고, 모든 주장이 증거로 뒷받침되거나 해결되지 않은 것으로 명확하게 표시되는지를 기준으로 측정하는 것입니다. 눈에 보이는 공백이 열 군데 있는 문서가 조용히 만들어낸 필드가 열 군데 있는 문서보다 더 신뢰할 수 있습니다.
Powerduck은 이 정확한 분리를 구현합니다: 기본적으로 결정론적 프레임워크 추적과 타입 분석을 사용하며, 경로를 절대 생성하지 않는 선택적 검토 가능한 AI 공백 채우기 기능을 제공하고, 편집 내용을 보존하는 증분 스캔도 가능합니다. 온라인 데모에서 처음부터 끝까지 확인해보고, 코드-to-OpenAPI 개요에서 언어별 정적 추출을 비교해보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기