구조화된 출력
구조화된 출력(Structured Output)은 LLM이 자유 텍스트 대신 JSON Schema 같은 미리 지정한 형식을 정확히 따르도록 출력을 강제하는 기능입니다. 프롬프트로 형식을 부탁하는 것이 아니라 토큰 생성 단계에서 스키마를 강제하므로, 필수 필드 누락이나 잘못된 형식 없이 항상 파싱 가능한 데이터를 얻을 수 있습니다.
- 구조화된 출력은 LLM의 응답을 JSON Schema 등 지정한 스키마에 강제로 맞추는 기능으로, 파싱 가능한 데이터를 안정적으로 받기 위한 핵심 장치입니다.
- 프롬프트로 형식을 요청하는 방식과 달리, 토큰 생성 단계에서 스키마 외의 토큰을 차단(제약 디코딩)하므로 형식 위반이 원천적으로 불가능합니다.
- OpenAI는 Structured Outputs를 적용한 gpt-4o-2024-08-06가 복잡한 JSON Schema 준수 평가에서 100%를 기록했고, 미적용 모델(gpt-4-0613)은 40% 미만이었다고 밝혔습니다.
- OpenAI(Structured Outputs)·Anthropic(Claude)·Google(Gemini)이 모두 동일한 개념을 지원하며, 데이터 추출·분류·에이전트 도구 호출에 필수적으로 쓰입니다.
- SEO의 '구조화 데이터(structured data, 스키마 마크업)'와는 전혀 다른 개념입니다. 이 용어는 검색엔진용 마크업이 아니라 'LLM 출력 형식 제어'를 가리킵니다.
구조화된 출력이란
구조화된 출력(Structured Output)은 LLM이 자유로운 문장 대신 미리 정의한 스키마(주로 JSON Schema)를 정확히 따르도록 출력을 강제하는 기능입니다. "JSON으로 답해 주세요"라고 프롬프트로 부탁하는 수준이 아니라, 모델이 스키마에서 벗어난 응답을 아예 생성하지 못하도록 만드는 것이 핵심입니다. 그 결과 필수 키 누락, 잘못된 자료형, 깨진 JSON 같은 문제 없이 곧바로 프로그램이 파싱할 수 있는 데이터를 얻게 됩니다.
이 개념은 LLM 출력을 후속 시스템(데이터베이스, API, 함수 호출 등)에 안정적으로 연결하기 위해 등장했습니다. LLM 응답을 코드로 처리하려면 형식이 매번 정확히 일치해야 하는데, 기존에는 모델이 가끔 설명 문장을 덧붙이거나 필드를 빠뜨려 파싱 오류가 났습니다. 구조화된 출력은 이 불확실성을 제거하여, AI 검색·에이전트·데이터 추출 파이프라인의 신뢰성을 끌어올리는 토대가 됩니다.
SEO의 '구조화 데이터'와 혼동 금지
이름이 비슷해 자주 혼동되지만, 구조화된 출력은 SEO에서 말하는 구조화 데이터(structured data), 즉 schema.org 기반의 JSON-LD 스키마 마크업과는 전혀 다른 개념입니다. 구조화 데이터는 검색엔진이 페이지 내용을 이해하도록 HTML에 삽입하는 메타데이터입니다. 반면 구조화된 출력은 LLM이 생성하는 응답 자체의 형식을 제어하는 기능으로, 검색 마크업과는 목적·작동 위치·소비 주체가 모두 다릅니다. 아래 표로 차이를 정리합니다.
| 구분 | 구조화된 출력 (Structured Output) | 구조화 데이터 (Structured Data, 스키마 마크업) |
|---|---|---|
| 목적 | LLM 응답을 지정한 형식으로 강제 | 검색엔진에 페이지 내용을 설명 |
| 형식 | JSON Schema (응답 본문) | JSON-LD·Microdata 등 schema.org 어휘 |
| 위치 | LLM API 응답 | HTML 문서 내부 마크업 |
| 소비 주체 | 후속 코드·API·에이전트 | 구글·빙 등 검색엔진 크롤러 |
| 대표 예 | OpenAI Structured Outputs, Gemini responseSchema | 리치 결과(별점·FAQ·상품) 마크업 |
작동 방식: 프롬프트 요청이 아니라 토큰 제약
구조화된 출력의 핵심은 제약 디코딩(constrained decoding)입니다. 정의한 JSON Schema를 문법(grammar)으로 컴파일한 뒤, 모델이 토큰을 하나씩 생성하는 추론 단계에서 스키마가 허용하지 않는 토큰을 차단합니다. 즉 형식 위반이 "드물게 발생"하는 것이 아니라 구조적으로 불가능해집니다.
Anthropic의 Claude 문서는 이 과정을 "JSON 스키마를 문법으로 컴파일하여 추론 중 토큰 생성을 제약한다"고 설명하며, 프롬프트에 의존하는 방식이 아니라고 명시합니다. 이 점에서 단순히 "유효한 JSON"만 보장하는 JSON 모드와, "스키마까지 정확히 준수"하는 구조화된 출력은 구분됩니다.
| 구분 | JSON 모드 | 구조화된 출력 (Structured Outputs) |
|---|---|---|
| 유효한 JSON 보장 | 예 | 예 |
| 스키마 준수 보장 | 아니요 | 예 |
| 필수 필드 누락 방지 | 아니요 | 예 |
| 작동 원리 | 형식 유도(프롬프트) | 제약 디코딩(토큰 차단) |
JSON Schema 예시
구조화된 출력의 토대는 JSON Schema입니다. JSON Schema는 JSON 데이터의 구조와 제약을 정의하는 선언적 언어로, 현재 권장 버전은 draft 2020-12입니다. 예를 들어 사용자 정보를 추출하는 스키마는 다음과 같이 작성합니다.
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" },
"plan_interest": {
"type": "string",
"enum": ["free", "pro", "enterprise"]
},
"demo_requested": { "type": "boolean" }
},
"required": ["name", "email", "plan_interest", "demo_requested"],
"additionalProperties": false
}
OpenAI API에서는 이 스키마를 response_format에 담아 호출하며, strict를 켜야 스키마가 강제됩니다. OpenAI는 모든 필드를 required로 두고 additionalProperties를 false로 설정할 것을 요구합니다.
response_format = {
"type": "json_schema",
"json_schema": {
"name": "user_info",
"strict": true,
"schema": { /* 위 스키마 */ }
}
}
Claude API에서는 동일한 스키마를 output_config.format에 type: "json_schema"로 전달하며, 도구 호출의 인자까지 엄격히 강제하려면 도구 정의에 strict: true를 추가합니다. Google Gemini는 responseSchema(스키마 강제)와 response_mime_type(JSON 모드)을 구분해 제공하며, Python의 Pydantic·JavaScript의 Zod로 스키마를 정의하면 그대로 동작합니다.
근거와 실측 데이터
구조화된 출력의 효과는 제공사 공식 자료로 뒷받침됩니다.
- OpenAI: Structured Outputs를 적용한 gpt-4o-2024-08-06가 복잡한 JSON Schema 준수 평가에서 100%를 기록한 반면, 구버전 gpt-4-0613은 40% 미만에 그쳤다고 발표했습니다. OpenAI는 가능하면 JSON 모드 대신 항상 Structured Outputs를 쓸 것을 권장합니다.
- Anthropic: Claude의 구조화된 출력은 스키마를 문법으로 컴파일해 토큰 생성을 제약하므로
JSON.parse()오류가 발생하지 않으며, 스키마 위반에 대한 재시도가 불필요하다고 설명합니다. 컴파일된 문법은 24시간 캐시됩니다. 다만 재귀 스키마·룩어헤드 등 일부 JSON Schema 기능과 도구 개수·매개변수 수에는 제한이 있습니다. - Google: Gemini 문서는 스키마를 강제하면 "취약한 출력 파싱이 필요 없어진다(eliminating the need for fragile output parsing)"고 설명하며, 데이터 추출·분류·에이전트 워크플로를 대표 용도로 제시합니다.
적용 체크리스트
- 가능하면 단순 JSON 모드 대신 스키마를 강제하는 구조화된 출력을 사용하십시오.
- JSON Schema에서 모든 필드를
required로 두고additionalProperties를false로 설정하십시오(OpenAI 권장). - 열거형이 필요한 필드는
enum으로 값 집합을 좁혀 잘못된 값을 차단하십시오. - 스키마를 직접 손으로 쓰기보다 Pydantic(Python)·Zod(JavaScript)로 정의해 코드와 스키마를 일치시키십시오.
- 응답이 거부(refusal)되거나 길이 제한으로 잘렸는지(
finish_reason)를 확인해 예외를 처리하십시오. - 재귀 스키마·복잡한 수치 제약 등 일부 기능은 미지원일 수 있으니 각 제공사의 스키마 제한을 확인하십시오.