당신의 API에는 수천 명의 LLM 소비자가 있지만, 그들 중 누구도 변경 사항(Changelog)을 읽을 수 없습니다.
요약
LLM과 에이전트가 API의 주요 소비자로 등장하면서, 모델의 학습 데이터 동결로 인해 발생하는 '동결된 소비자(frozen consumer)' 문제를 다룹니다. 기존의 계약 테스트 방식으로는 대응할 수 없는 API 스키마 변경과 그로 인한 성능 저하 문제를 경고합니다.
핵심 포인트
- LLM 소비자는 특정 학습 시점에 API 지식이 동결됨
- 기존의 소비자 주도 계약 테스트로는 LLM 대응 불가
- API 스키마 변경 시 에이전트가 구형 방식을 고수하며 조용히 오류 발생
- API 제공자는 모델의 학습 데이터와 런타임 프롬프트를 고려해야 함
지난 18개월 사이, 모든 공개 API의 소비자 기반이 변화했지만 거의 아무도 자신들의 테스트 전략을 업데이트하지 않았습니다.
만약 당신이 오늘 API를 게시한다면 — Confluence 페이지에 OpenAPI 명세가 있는 내부 API라 할지라도 — 당신이 온보딩(Onboarding)하지 않았고, 키(Key)를 발급하지 않았으며, 폐지(Deprecation)를 알리지 않았고, 변경 사항(Changelog)으로 연락할 수 없는 소비자들을 마주하게 됩니다. 그 소비자들은 언어 모델(Language Models)과 사람들이 그 모델들을 기반으로 구축하고 있는 에이전트(Agents)들입니다. 그들 중 일부는 지난주에 당신의 API를 학습했습니다. 일부는 18개월 전에 학습했으며 그 시점에 지식이 동결(Frozen)되었습니다. 당신이 user_id를 userId로 이름을 변경했을 때, 그들 중 누구도 이메일을 받지 못했습니다.
이것은 오늘날 실제 운영 환경에서 대규모로 존재하는 실질적인 소비자 범주입니다. 그리고 파괴적 변경(Breaking changes)을 포착하기로 되어 있었던 테스트 규율 — 소비자 주도 계약 테스트(Consumer-driven contract testing), Pact 중심의 세계 — 은 이를 위해 설계되지 않았습니다. 이 불일치는 대부분의 팀이 깨닫는 것보다 더 크며, 현재 아무도 버그로 추적하지 않는 부류의 버그들을 조용히 만들어내고 있습니다.
동결된 소비자 문제 (The frozen-consumer problem)
전통적인 API 소비자는 알려져 있고, 이름이 있으며, 버전이 지정된 코드베이스입니다. 당신의 모바일 앱 버전 4.3.2, 파트너사의 결제 서비스, 당신이 게시한 Python SDK 등이 이에 해당합니다. 당신은 이들을 열거할 수 있고, 이들을 대상으로 테스트할 수 있습니다. 무언가를 변경할 때, 해당 팀에 전화를 걸어 알릴 수 있습니다. 만약 지난 10년 동안 Pact를 도입했다면, 당신은 제공자(Provider)의 변경 사항이 어떤 소비자에게 문제를 일으킬지 배포 전에 자동으로 알려주는 브로커(Broker)를 보유하고 있을 것입니다.
LLM 소비자는 이 중 그 어느 것도 아닙니다.
그것은 코드베이스가 아니라 하나의 _인구 집단 (population)_입니다. 당신이 대응할 수 있는 어떤 방식으로도 스스로 버전을 관리하지 않습니다. 그것은 학습 중단 시점 (training cutoff)을 가지고 있으며, 그 시점 이후로 당신의 API 문서는 모델의 가중치 (weights) 속에 동결됩니다. 런타임 (runtime) 시점에는 도구 사용 (tool-use) 프롬프트가 존재하며, 그 이후의 당신의 API에 대한 추가적인 지식은 어떤 에이전트 제작자가 우연히 붙여넣은 내용이 전부입니다. 그것은 당신의 변경 사항 (changelog)을 가져오지 않습니다. 새로운 스키마 (schema)에 맞춰 재시도하지도 않습니다. 그것은 무시무시할 정도의 자신감을 가지고, 이전 필드를 사용하여 이전 엔드포인트 (endpoint)를 호출하고, 이전 형태를 파싱하며, 조용히 성능을 저하시킬 것입니다.
이를 **동결된 소비자 (frozen consumer)**라고 부릅시다. 즉, 당신의 스키마에 대한 이해가 당신이 제어할 수 없고, 쿼리할 수 없으며, 지원 중단 헤더 (deprecation header)로 업데이트할 수도 없는 특정 시점에 고정되어 있는, 실제 행동상 유의미한 당신 API의 사용자입니다.
만약 당신의 팀이 OpenAPI 명세 (spec)를 배포한다면, 그것은 학습 코퍼스 (training corpus)에 포함됩니다. 당신의 엔드포인트가 문서화된 요청에 응답한다면, 에이전트들이 그것을 호출하고 있는 것입니다. KushoAI의 State of Agentic API Testing 2026 보고서에 따르면, 공개 API의 41%가 특정 스냅샷 이후 30일 이내에 스키마 드리프트 (schema drift)를 경험하며, 63%는 90일 이내에 경험하는 것으로 나타났습니다. 이는 운영 환경에 있는 대다수의 LLM 소비자들이 자신들의 학습 데이터가 수집된 이후 이미 변경된 API를 호출하고 있음을 의미합니다. 드리프트는 기본 상태입니다. "동기화된 소비자 (synchronized consumer)"는 언제나 근사한 허구였지만, 인간 소비자라면 적어도 그들에게 알릴 수는 있었습니다. 하지만 인구 집단에게는 알릴 수 없습니다.
새로운 파괴적 변경 (breaking-change) 분류 체계
엔드포인트 삭제, 필수 필드 삭제, 타입 변경, 열거형 (enum) 범위 축소와 같은 기존의 파괴적 변경 분류 체계는 여전히 옳습니다. 다만 불완전할 뿐입니다.
이제 인간이 작성한 소비자에게는 파괴적이지 않지만, 동결된 소비자에게는 재앙적인 변화 범주들이 존재합니다. 다음 세 가지가 중요합니다.
1. 어휘적 파괴 (Lexical breaks)
당신이 user_id를 userId로 변경합니다. 당신의 변경 사항 (changelog)은 이를 기록합니다. 당신의 SDK는 자동으로 재생성됩니다. 타입이 지정된 클라이언트 (typed clients)는 업데이트될 때까지 컴파일을 거부합니다. 모든 인간 소비자는 문제가 없거나, 문제가 있다고 크게 항의할 것입니다.
LLM 소비자(LLM consumer)는 침묵합니다. 해당 토큰 클러스터가 다음 토큰 예측(next-token prediction)에서 승리하는 한, 즉 많은 모델에서 다음 재학습 주기(retraining cycle)가 올 때까지 무기한으로 user_id를 포함한 요청을 계속 생성할 것입니다. 모델은 고장 난 것이 아닙니다. 모델은 모델이 하는 일을 정확히 수행하고 있는 것입니다. 즉, 학습된 학습 분포(training distribution)를 기반으로 가장 가능성 높은 토큰 시퀀스(token sequence)를 예측하고 있는 것입니다.
다음과 같은 경우에도 마찬가지입니다:
snake_case↔camelCase마이그레이션- 복수형 ↔ 단수형 컬렉션 명명 (
/users/{id}/ordervs/users/{id}/orders) - 헤더 접두사(Header prefix) 변경 (
X-Request-Id→Request-Id,X-제거) - 경로 버전 관리(Path versioning) 전환 (
/v1/items→ 콘텐츠 협상(content negotiation)을 포함한/api/items) - 약어 대소문자 표기 (
userID↔userId)
인간 소비자나 전통적인 계약 테스트(contract tests)의 경우, 이러한 변경 사항은 타입 체크(typecheck)가 몇 초 만에 잡아낼 수 있는 "찾아 바꾸기(find-replace)" 수준의 변경입니다. 하지만 고정된 소비자(frozen consumers)에게 이는 보이지 않는 절벽과 같습니다. 또한 필드 추가(field additions) 역시 안전하지 않습니다. KushoAI의 데이터에 따르면, 관찰된 모든 드리프트(drift) 이벤트의 86%가 추가 사항에 의해 발생하며, LLM은 인지된 공백을 채우기 위해 관련 도메인 API의 필드 이름을 신뢰성 있게 환각(hallucinate)합니다. 따라서 "추가적인" 변경이라 할지라도, 모델이 귀하가 허용한다고 믿는 유령 필드(phantom fields)를 포함한 호출을 생성할 수 있습니다.
2. 안정적인 형태 내부의 의미론적 드리프트 (Semantic drift inside stable shapes)
응답의 형태(shape)는 변하지 않았습니다. 하지만 의미는 변했습니다.
기존 열거형(enum)에 두 개의 새로운 값을 추가했습니다: status: "active" | "inactive"가 status: "active" | "inactive" | "trialing" | "paused"로 변경되었습니다. 엄격하게는 추가적인 변경입니다. 타입이 지정된 소비자(typed consumers)는 스위치 문(switch statements)을 확장할 것입니다 (또는 TypeScript 사용자가 런타임에 이를 발견하게 될 것입니다. 결과는 같고 타이밍만 다를 뿐입니다).
LLM 소비자에게 있어 새로운 값들은 이제 _분포 외 데이터 (out of distribution)_입니다. 모델은 이진(binary) 값을 학습했습니다. 모델은 이진 값에 따라 분기할 것입니다. "trialing"이라는 값은 에이전트의 프롬프트(prompt), 모델 온도(temperature), 그리고 주변 문맥(context)에 따라 일정 확률로 "inactive" 분기를 통과하거나 나머지 확률로 "active" 분기를 통과하게 될 것입니다. 에러를 발생시키지도 않을 것이고, 로그를 남기지도 않을 것입니다. 그저 무시할 수 없는 비율의 고객들을 잘못된 코드 경로(code path)로 안내할 뿐입니다.
이러한 현상의 가장 위험한 형태는 심지어 열거형(enums)조차 아닙니다. 그것은 의미론(semantics)이 변질되는 응답 코드(response codes)입니다. 예전에는 "유효성 검사 실패, 재시도 금지"를 의미했던 422 코드가, 이제는 새로운 ML 기반 검증기에 의해 "유효성 검사 실패, 재시도 가능"이라는 의미로도 발행되는 경우입니다. 코드는 동일하지만 의미는 다릅니다. 고정된 소비자(Frozen consumers)들은 이를 계속해서 치명적인 오류로 취급할 것입니다. 반면 인간 소비자는 당신이 공지했을 때 재시도 정책(retry policy)을 업데이트합니다.
3. 환각된 엔드포인트(Hallucinated endpoints)와 부활한 필드
이것은 반대의 문제입니다. 당신의 API는 작아지는데, LLM은 계속해서 삭제된 부분을 호출하는 것입니다.
고정된 소비자들은 당신이 2년 전에 폐기(sunset)한 엔드포인트를 자신 있게 호출할 것입니다. 그들은 당신의 서버가 이제 거부하거나(운이 좋은 경우), 조용히 무시하는(더 나쁜 경우) 지원 중단된(deprecated) 필드들로 요청 본문(request bodies)을 채울 것입니다. 그들은 당신이 더 이상 발행하지 않는 페이지네이션 토큰(pagination tokens)을 믿을 것입니다.
이에 대해 문서화된 실패 모드(failure mode)가 존재합니다. 연구자들은 이를 _기능적 환각 (functional hallucination)_이라고 부릅니다. 에이전트가 존재하지 않는 엔드포인트를 호출하거나
표준적인 소비자 주도 계약 테스트 (Consumer-driven contract testing) 도구인 Pact는 다음과 같이 작동합니다: 소비자가 제공자 (Provider)로부터 기대하는 바를 선언합니다. 이 기대 사항은 브로커 (Broker)에 게시됩니다. 제공자는 CI (지속적 통합) 과정에서 현재의 코드가 게시된 모든 기대 사항을 충족하는지 검증합니다. 만약 제공자의 변경 사항이 등록된 소비자 중 누구라도 깨뜨릴 가능성이 있다면, 검증은 실패하며 머지 (Merge)는 이루어지지 않습니다.
이것은 아름다운 시스템이며, 저 또한 팬입니다 — 지난주에 저는 Pact가 진정으로 훌륭함에도 불구하고 왜 대부분의 팀이 계약 테스트를 수행하지 않는지에 대한 글을 썼습니다. 하지만 계약을 다시 읽어보십시오: 소비자가 무엇을 기대하는지 선언 (declares) 합니다. 소비자는 참여자입니다. 계약은 상호적입니다.
고정된 소비자 (Frozen consumer)는 참여자가 아닙니다. 그것은 아무것도 서명하지 않습니다. 브로커에 게시하지도 않습니다. 심지어 자신이 학습한 API의 버전이 무엇인지조차 알지 못합니다. 대신, 그것은 _유령 계약 (phantom contract)_을 가지고 있습니다: 즉, 누군가가 어느 시점에 스크래핑한 텍스트로부터 유도된 당신의 스키마 (Schema)에 대한 통계적 모델이며, 버전도 없고, 만료일도 없으며
이것은 Pact의 잘못이 아닙니다. Pact는 그 가정이 전제하는 범위 내에서는 올바릅니다. 반면, _당신의 소비자가 누구인가_에 대한 가정은 공개 스펙 (Spec)을 가진 그 어떤 API에도 더 이상 적용되지 않습니다.
실제로 해야 할 일
저에게도 명확한 정답은 없으며, 아직 다른 누구도 정답을 가지고 있지 않습니다. 하지만 세 가지 관행이 수렴하기 시작하고 있으며, 이들은 모두 충분히 비용이 적게 들어서 해당 규율이 성숙하기를 기다리지 않고도 이번 분기 내에 바로 도입할 수 있습니다.
OpenAPI 스펙을 AI 호환성 계약으로 취급하십시오
당신의 OpenAPI 문서는 더 이상 단순한 문서가 아닙니다. 그것은 고정된 소비자 집단이 단 한 번 읽고 영원히 기억할 정전적 산물 (Canonical artifact)입니다. 설명 (Description)은 예전보다 더 중요해졌습니다. 예시 (Example)도 예전보다 더 중요해졌습니다. 필드 (Field)의 이름은 예전보다 훨씬 더 중요해졌습니다.
이는 당신이 변경 사항을 만드는 방식에 영향을 미칩니다. 가독성을 위해 필드 이름을 변경하는 것은 더 이상 비용이 들지 않는 개선 사항이 아닙니다. 그 비용은
이것은 바보같이 들릴 수 있습니다. 하지만 이것은 계약 테스트 (contract tests)가 잡아낼 수 없는 것들을 포착합니다. 왜냐하면 이것이 바로 실제 소비자 (consumers)들이 보여줄 실제 동작이기 때문입니다. 만약 모델이 지속적으로 필드 이름을 잘못 지정하거나, 열거형 (enum)을 잘못 읽거나, 필수 파라미터 (required parameter)를 환각 (hallucinate)하거나, 더 이상 사용되지 않는 엔드포인트 (deprecated endpoint)를 호출한다면, 당신은 Pact 테스트로는 절대 드러나지 않을 프로덕션 버그 (production bug)를 방금 발견한 것입니다.
의사 코드(pseudo-Python)로 작성한 최소 버전은 다음과 같습니다:
def ai_compat_check(spec, endpoint, models=("claude", "gpt", "gemini")):
findings = []
for model in models:
...
이를 계약 테스트와 함께 CI (지속적 통합) 환경에서 실행할 수 있습니다. 차이점(divergence)을 실패(failure)가 아닌 '발견 사항(finding)'으로 취급하세요. 때로는 모델이 당신이 수정할 필요가 없는 바보 같은 행동을 하는 것일 수도 있습니다. 하지만 때로는 그것이 당신의 명세서 (spec)에 있는 실제 모호함이나, 고착된 소비자 (frozen-consumer)의 함정을 가리키고 있을 수도 있습니다.
에이전트가 실제로 읽을 수 있는 구조화된 폐기 채널 (deprecation channel)을 게시하세요
현재 당신의 폐기 (deprecation) 전략은 다음과 같습니다: 블로그 포스트, 변경 사항 (changelog), 알려진 소비자에게 보내는 이메일. 앞의 두 가지는 고착된 소비자들에게 전혀 도달하지 못합니다. 세 번째 방식은 사람이 에이전트의 지침 (instructions)을 업데이트하기 위해 개입해야만 소비자에게 도달합니다.
새롭게 떠오르는 해결책은 모델 컨텍스트 프로토콜 (Model Context Protocol, MCP) 및 이와 유사한 기계 판독 가능한 인터페이스 (machine-readable surfaces)입니다. MCP 서버는 에이전트가 '런타임 (runtime)'에 가져올 수 있는 구조화되고 쿼리 가능한 계약 (contract)입니다. 이는 모델의 학습 데이터 (training data)에 의존하지 않습니다. 그것은 에이전트에게 지금 현재의 형태가 무엇인지 즉시 알려줍니다.
REST API와 함께 MCP 인터페이스를 게시하는 것은 LLM 인구 집단과 맺을 수 있는 '등록된 소비자 (registered-consumer)' 관계에 가장 가까운 방식입니다. MCP를 사용하지 않는 에이전트에게는 도달하지 못하겠지만, 이를 사용하는 인구 집단은 빠르게 성장하고 있으며, 초기에 그곳에 자리 잡는 비용은 저렴합니다.
카테고리 수준의 관점
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기