채팅 UI의 모든 응답 아래에 작은 “AI 생성” 배지를 표시하는 것만으로는 충분하지 않습니다. 파트너 팀이 배치 작업에서 /summarize 엔드포인트를 호출하고, 결과를 데이터베이스에 저장한 뒤 고객용 보고서에 렌더링한다면 UI 배지는 사라집니다. API 소비자는 응답이 모델에서 생성되었는지 알 수 없고, 바로 그 지점에서 AI 공개(disclosure)가 필요합니다.
AI 공개를 UI 기능이 아니라 API 계약(contract) 으로 설계해야 하는 이유입니다. 2026년 8월 2일부터 EU AI 법의 제50조는 이 문제를 더 구체적으로 만듭니다. 모델 제공업체는 모델 수준에서 출력을 표시할 수 있지만, 사용자가 AI와 상호작용하고 있음을 알리는 의무는 시스템을 배포하는 주체에게 적용될 수 있습니다.
이 글에서는 AI 공개 정보를 API 응답에 포함하고, OpenAPI로 문서화하고, 캐시·오류·배치·대체 모델 경로까지 테스트하는 방법을 다룹니다. Apidog를 사용하면 API 설계, 문서화, 테스트를 한곳에서 관리할 수 있습니다.
응답에 포함할 공개 정보
AI 공개 필드는 최소한 다음 세 질문에 답해야 합니다.
- 콘텐츠가 생성되었는가?
- 어떤 공급업체와 모델이 생성했는가?
- 출처(provenance)를 실제로 검증했는가?
단순한 불리언인 ai_generated: true보다 열거형이 더 실용적입니다.
-
synthetic: 인간 저작 없이 모델이 생성 -
assisted: 사람이 작성한 콘텐츠를 모델이 편집, 번역 또는 요약 -
human: 모델이 관여하지 않음
예를 들어 “모델이 초안을 모두 작성했다”와 “사람이 작성한 초안을 모델이 다듬었다”는 다른 상태입니다. 특히 제50조의 예외 조항을 적용할 때도 이 차이는 중요합니다.
{
"id": "sum_4f81a2",
"content": "The incident affected two regions for 41 minutes...",
"ai": {
"generation": "synthetic",
"vendor": "anthropic",
"model": "claude-opus-5",
"human_review": false,
"generated_at": "2026-08-11T09:14:22Z"
},
"provenance": {
"status": "unchecked",
"standard": null
}
}
human_review를 별도 필드로 유지하기
human_review는 단순한 메타데이터가 아닙니다. 제50조(4)항에서는 공공의 이익과 관련된 사안에 대해 대중에게 알리기 위해 게시되는 AI 생성 텍스트라도, 사람이 검토했고 식별 가능한 주체가 편집 책임을 지는 경우를 구분합니다.
따라서 플랫폼이 사람이 초안을 검토하거나 승인했다는 사실을 기록한다면, 호출자도 그 정보를 사용할 수 있도록 응답에 포함해야 합니다.
출처 상태를 불리언으로 축소하지 않기
provenance.status는 true 또는 false만으로 표현하면 안 됩니다. 최소한 다음 상태를 구분해야 합니다.
-
verified: 출처 정보를 검증함 -
absent: 출처 정보가 없음 -
invalid: 출처 정보가 있지만 유효하지 않음 -
unchecked: 아직 확인하지 않음
검증 서비스가 중단되어 검사하지 못한 상태와 출처 정보가 없는 상태는 다릅니다. 이 차이를 보존해야 다운스트림 시스템이 올바르게 처리할 수 있습니다.
헤더와 본문 중 어디에 넣어야 하나?
실무에서는 둘 다 제공하는 것이 좋습니다.
- 본문(body): 저장, 재처리, 전달되는 JSON 데이터에 공개 정보를 포함합니다.
- 헤더(header): 프록시, API 게이트웨이, 로깅 계층처럼 본문을 파싱하지 않는 시스템도 정보를 사용할 수 있게 합니다.
HTTP/1.1 200 OK
Content-Type: application/json
X-AI-Generated: synthetic
X-AI-Model: anthropic/claude-opus-5
헤더를 사용할 때는 다음 규칙을 적용하세요.
- 모든 AI 기반 엔드포인트에서 같은 헤더 이름과 값을 사용합니다.
- 본문과 헤더 중 어느 쪽이 정식(canonical) 값인지 문서화합니다.
- 둘의 값이 일치하는지 테스트로 강제합니다.
일부 경로에만 존재하는 헤더는 없는 것보다 더 위험합니다. 호출자는 일관된 계약을 기대하기 때문입니다.
스트리밍 응답에서는 공개 정보를 응답 헤더 또는 첫 번째 이벤트에 포함하세요. 호출자가 첫 토큰부터 렌더링한다면, 응답 종료 시점의 트레일러까지 기다리게 해서는 안 됩니다. 헤더 설계의 기본은 HTTP 헤더란 무엇인가에서 확인할 수 있습니다.
OpenAPI 사양에 AI 공개 스키마 추가하기
OpenAPI 정의에 없는 필드는 결국 관례가 됩니다. 관례는 리팩터링 과정에서 쉽게 사라집니다.
먼저 재사용 가능한 스키마를 정의하세요.
components:
schemas:
AiDisclosure:
type: object
required: [generation]
properties:
generation:
type: string
enum: [synthetic, assisted, human]
description: >
synthetic = produced by a model with no human authoring.
assisted = a human authored the content and a model edited,
translated, or summarised it.
human = no model involvement.
vendor:
type: string
example: anthropic
model:
type: string
example: claude-opus-5
human_review:
type: boolean
description: >
True when a person reviewed the output before it was returned
and an identifiable party holds editorial responsibility.
generated_at:
type: string
format: date-time
그다음 모델 출력을 포함할 수 있는 모든 응답에서 이 스키마를 참조합니다.
components:
schemas:
SummaryResponse:
type: object
required: [id, content, ai]
properties:
id:
type: string
content:
type: string
ai:
$ref: "#/components/schemas/AiDisclosure"
여기서 핵심은 ai를 선택 사항이 아니라 필수 필드로 만드는 것입니다. 선택 필드는 호출자에게 방어 코드를 요구하고, 많은 호출자는 이를 구현하지 않습니다.
이렇게 하면 두 가지 이점이 있습니다.
- 생성된 API 문서가 모든 소비자에게 필드 의미를 자동으로 설명합니다.
- 사양 유효성 검사와 변경 감지가 필드 삭제 또는 필수성 변경을 잡아냅니다.
OpenAPI 사양 유효성 검사 방법으로 사양을 검증하고, CI에서 중단 변경 사항을 차단하는 OpenAPI diff로 ai 필드가 선택 사항으로 바뀌는 변경을 차단하세요.
자주 누락되는 구현 경로 점검하기
AI 공개 필드는 정상 응답 경로만 구현하면 빠르게 누락됩니다. 다음 네 경로를 명시적으로 확인하세요.
1. 캐시된 응답
공개 정보를 붙이기 전에 본문만 캐시하면, TTL 동안 공개 정보가 없는 응답이 반환될 수 있습니다.
모델 출력만 캐시하고 응답 래퍼를 별도로 재구성하기보다, 공개 정보를 포함한 전체 응답 객체를 캐시하는 방식이 안전합니다.
2. 오류 및 부분 응답
타임아웃으로 부분 요약을 반환하더라도 그것은 모델 출력입니다. 오류 엔벨로프가 별도 스키마를 사용한다면 해당 스키마에도 AI 공개 필드를 포함해야 합니다.
3. 배치 작업과 웹훅 페이로드
비동기 전달 경로는 종종 동기 API와 다른 코드, 더 단순한 DTO, 별도 직렬화 로직을 사용합니다. 따라서 공개 필드가 가장 자주 빠지는 지점입니다.
4. 대체 모델 경로
주 모델이 실패해 대체 모델로 전환되면 model 값도 실제 호출 모델로 변경되어야 합니다. 공개 블록에 모델 이름을 하드코딩하면 대체 실행 시 잘못된 정보를 반환할 수 있습니다.
이 네 경우를 모두 해결하는 핵심 원칙은 하나입니다.
성공 응답을 직렬화할 때가 아니라, 모델 출력이 응답 객체에 들어오는 순간 공개 정보를 첨부하세요.
계약처럼 테스트하기
공개 필드는 호출자에게 하는 약속입니다. 테스트하지 않는 계약은 문서에 불과합니다.
다음 다섯 가지 단언으로 대부분의 누락을 잡을 수 있습니다.
1. 모든 AI 기반 응답에 ai 필드가 존재하는지 확인
const body = pm.response.json();
pm.test("response carries AI disclosure", function () {
pm.expect(body).to.have.property("ai");
pm.expect(body.ai.generation).to.be.oneOf([
"synthetic",
"assisted",
"human"
]);
});
2. 헤더와 본문의 생성 상태가 일치하는지 확인
pm.test("header and body agree", function () {
pm.expect(pm.response.headers.get("X-AI-Generated"))
.to.eql(body.ai.generation);
});
3. 응답의 모델 ID가 실제 호출 모델과 일치하는지 확인
이 테스트는 대체 모델 경로에서 특히 중요합니다. 업스트림 출력의 워터마크 처리 여부가 모델 ID에 따라 달라질 수 있기 때문입니다. Claude의 API 워터마킹처럼 모델 고정이 성능 설정이 아니라 규정 준수 세부 사항이 될 수 있는 경우도 있습니다.
4. 캐시 응답에도 공개 정보가 유지되는지 확인
같은 요청을 두 번 호출하고, 두 번째 캐시 응답의 ai 블록이 첫 번째 응답과 동일한지 단언하세요.
5. 오류 경로에도 공개 정보가 있는지 확인
타임아웃 또는 다운스트림 실패를 강제한 뒤, 오류 또는 부분 응답 엔벨로프에도 필요한 공개 필드가 포함되는지 확인하세요.
이 테스트를 하나의 시나리오로 구성하고 CI에서 실행할 수 있습니다.
apidog run --access-token "$APIDOG_ACCESS_TOKEN" \
-t "$DISCLOSURE_SCENARIO_ID" \
-e "$APIDOG_ENV_ID" \
-r cli,html
단언이 실패하면 명령은 0이 아닌 종료 코드로 끝납니다. 따라서 ai 필드를 제거한 변경은 배포 전에 빌드 실패로 차단됩니다.
전체 CI 구성은 GitHub Actions에서 API 테스트 자동화에서, 일반적인 테스트 작성 방식은 API 단언에서 확인할 수 있습니다. 직접 시나리오를 구성하려면 Apidog 다운로드를 사용하세요.
호출자가 찾을 수 있는 위치에 문서화하기
문서화 대상은 두 가지이며, 각각 다른 위치가 필요합니다.
API 참조 문서
OpenAPI 스키마의 description은 단순한 설명이 아니라 계약의 일부입니다. 특히 assisted가 제품에서 정확히 무엇을 의미하는지 명확히 적으세요.
호출자는 이 설명을 바탕으로 라벨 표시, 저장 정책, 고객 노출 여부 같은 구현 또는 법적 판단을 내릴 수 있습니다.
짧은 정책 페이지
별도의 한 페이지 정책 문서에는 다음 내용을 정리하세요.
- 어떤 엔드포인트가 모델 출력을 반환할 수 있는가
- 어떤 모델을 사용하는가
- 인간 검토가 발생하는가
-
human_review가true일 때 무엇을 의미하는가 - 무엇을 보증하며, 무엇을 보증하지 않는가
이 문서를 API 참조 문서에서 링크하고 버전 관리하세요.
제한 사항도 구체적으로 기록해야 합니다. 예를 들어 Claude 출력을 전달하는 경우, 텍스트에 포함된 워터마크를 애플리케이션이 자체적으로 검증할 수 없고 Anthropic이 탐지 기능을 공개하지 않았다면, 검증 가능하다고 암시하지 마세요. 관련 배경은 Claude의 워터마크를 감지하는 방법에서 확인할 수 있습니다.
대화형 문서는 이때 특히 유용합니다. 호출자가 표만 읽는 대신 실제 응답에서 ai와 provenance 필드를 직접 확인할 수 있기 때문입니다. 콘솔 체험 기능이 있는 대화형 API 문서 호스팅을 참고하세요.
자주 묻는 질문
X-AI-Generated 헤더는 표준인가요?
아닙니다. AI 공개용으로 승인된 표준 HTTP 헤더는 없습니다. 팀에서 이름을 정하고, 문서화하고, 모든 관련 엔드포인트에서 일관되게 사용하세요.
공개 정보는 헤더와 본문 중 어디에 넣어야 하나요?
둘 다 넣는 것이 좋습니다. 본문은 저장·전달·재처리에 사용되고, 헤더는 프록시·게이트웨이·로그·비JSON 응답에 유용합니다. 둘이 불일치할 경우 어떤 값이 정식인지 계약에 명시하세요.
법적으로 반드시 구현해야 하나요?
역할과 콘텐츠에 따라 다릅니다. 제50조의 의무는 제공업체와 배포업체에 다르게 적용될 수 있으며, 제50조(4)항은 인간 편집 통제가 있는 콘텐츠를 구분합니다. 자세한 내용은 API 개발자를 위한 EU AI 법 제50조를 참고하세요. 법률 판단은 전문가와 검토하고, 구현 가능한 계약을 만드는 일은 개발팀이 담당해야 합니다.
제공업체가 워터마크를 넣으면 충분하지 않나요?
아닙니다. 워터마크가 호출자가 읽거나 검증할 수 없는 신호라면, API 소비자가 즉시 사용할 수 있는 공개 정보가 아닙니다. 워터마크는 보완책일 수 있지만 응답 계약의 대체재는 아닙니다.
스트리밍 응답은 어떻게 처리해야 하나요?
응답 헤더 또는 첫 번째 스트림 이벤트에 공개 정보를 넣으세요. 호출자는 토큰이 도착하는 즉시 렌더링할 수 있으므로, 스트림 종료까지 기다리게 하면 안 됩니다.
생성 후 사람이 편집한 콘텐츠는 어떻게 표현하나요?
generation: "assisted"와 human_review를 함께 사용하세요. 모델 지원 여부와 실제 인간 검토 여부는 서로 다른 정보입니다.
이 필드도 버전 관리해야 하나요?
예. AI 공개 필드는 응답 스키마의 일부입니다. 새 enum 값을 추가하거나 필수성을 바꾸는 변경도 호출자가 알아야 할 계약 변경입니다. OpenAPI diff를 CI에 연결해 이런 변경을 감지하세요.
핵심 요약
AI 공개는 UI 배지로만 구현하면 실패하기 쉽고, API 계약으로 구현하면 유지됩니다.
- 응답 본문에 필수
ai필드를 추가합니다. - 헤더에도 핵심 상태를 미러링합니다.
- OpenAPI에서 재사용 가능한 스키마로 정의합니다.
- 캐시, 오류, 배치, 웹훅, 대체 모델 경로를 테스트합니다.
- CI에서 사양 검증과 API 단언을 실행합니다.
이 작업은 대개 오래 걸리지 않습니다. 하지만 마케팅 문구 수준의 “AI 생성” 표시를, 호출자가 저장하고 처리하며 테스트로 신뢰할 수 있는 구현 계약으로 바꿉니다.
Top comments (0)