프롬프트에 JSON으로 답하라고 했는데 후속 자동화 단계에서 키가 빠지거나 자료형이 달라진다면, 먼저 응답이 유효한 JSON인지와 약속한 스키마까지 맞는지를 나눠 확인해야 합니다. JSON 모드는 유효한 JSON을 받는 데 초점을 두고, Structured Outputs는 지정한 스키마에 맞춘 응답을 받도록 설계된 방식입니다.
이 차이를 놓치면 JSON 파싱 성공을 업무 데이터 검증 성공으로 오해하게 됩니다. 운영에서는 응답 형식, 스키마 검증, 거부·불완전 상태를 차례로 분리해 보는 편이 안전합니다.
JSON 모드와 Structured Outputs는 무엇이 다른가
JSON 모드는 결과를 JSON 형식으로 받기 위한 선택입니다. JSON 문법으로 읽히더라도 필수 키와 값의 타입까지 자동으로 보장하는 것은 아닙니다.
반면 Structured Outputs는 지정한 스키마에 맞춘 결과를 받도록 설계된 방식입니다. 후속 코드가 정해진 필드를 바로 사용해야 한다면 단순 문자열 파싱보다 스키마를 함께 관리하는 접근이 적합합니다. 세부 지원 범위는 사용 중인 모델과 API 버전에 따라 공식 문서에서 다시 확인해야 합니다.
공식 근거: OpenAI Structured Outputs 문서
Pydantic 검증은 응답의 마지막 안전망입니다
Structured Outputs를 사용하더라도 애플리케이션 경계에서 최종적으로 어떤 값을 받을지 확인해야 합니다. OpenAI Python SDK의 client.chat.completions.parse()에는 Pydantic 모델을 전달할 수 있습니다.
from pydantic import BaseModel
from openai import OpenAI
class Classification(BaseModel):
label: str
confidence: float
client = OpenAI()
response = client.chat.completions.parse(
model="사용 중인 모델",
messages=[{"role": "user", "content": "문의 내용을 분류하세요."}],
response_format=Classification,
)
message = response.choices[0].message
if message.refusal:
print("모델 거부:", message.refusal)
elif message.parsed:
print(message.parsed.label, message.parsed.confidence)
파싱된 값은 message.parsed에서 확인하며, 거부가 있으면 message.refusal을 먼저 살펴야 합니다. 이 예제의 confidence가 품질 보증 수치라는 뜻은 아니며, 그 의미는 애플리케이션의 업무 정의가 정해야 합니다.
공식 근거: OpenAI Python SDK Structured Outputs Parsing Helpers
필드 누락과 타입 오류를 진단하는 순서
응답이 끝까지 도착했는지 확인합니다
Responses API를 사용한다면 status가 incomplete인지 확인해야 합니다. 완성되지 않은 응답이라면 JSON이나 Pydantic 오류로만 단정하지 말고, 불완전 상태로 분기해 원문과 상태를 보존합니다.
거부 상태를 일반 데이터와 분리합니다
Structured Outputs 응답에서 모델 거부(refusal)가 발생할 수 있습니다. 거부 문장을 업무 데이터로 파싱하려 하면 필드 누락처럼 보일 수 있으므로, 거부 여부를 별도 상태로 기록합니다.
JSON 문법과 스키마를 각각 확인합니다
문법상 유효한 JSON인데 Pydantic 검증이 실패한다면 키 이름·필수 여부·중첩 구조·자료형을 모델 정의와 비교합니다. 숫자를 기대한 필드에 문자열이 들어오거나 필수 필드가 빠진 경우는 JSON 파싱 성공과 별개의 문제입니다.
실패 원문과 검증 오류를 함께 남깁니다
검증 실패 때 원문을 버리면 같은 문제가 반복될 때 원인을 찾기 어렵습니다. 민감한 내용은 정책에 맞게 가린 뒤 요청 식별자·응답 상태·검증 오류·재처리 결과를 함께 남깁니다. 재요청 여부는 일시성, 입력 오류, 비용과 중복 처리 위험을 보고 결정합니다.
JSON 모드로 충분한 경우와 Structured Outputs가 필요한 경우
최종 결과를 사람이 읽고 후속 시스템이 필드를 엄격하게 소비하지 않는다면 JSON 모드와 별도 검증만으로 시작할 수 있습니다. 반대로 결과를 데이터베이스에 넣거나 자동화 단계의 분기 조건으로 사용할 때처럼 필드와 타입이 계약의 일부라면 Structured Outputs와 Pydantic 검증을 함께 검토하는 편이 낫습니다.
다만 Structured Outputs가 업무 의미까지 판단해 주는 것은 아닙니다. 스키마에 맞는 label이 들어와도 분류 기준과 값의 범위가 업무상 타당한지는 애플리케이션 검사가 담당해야 합니다.
다음 자동화 단계로 넘기기 전 체크할 것
- 응답이
incomplete인지, 또는 모델 거부인지 먼저 분기합니다. - JSON 문법이 아니라 필수 필드·중첩 구조·자료형까지 확인합니다.
- Pydantic 검증 실패 시 원문, 상태, 오류 내용을 함께 보존합니다.
- 검증을 통과한 값에도 업무 규칙과 민감정보 처리 기준을 적용합니다.
- 모델과 SDK를 바꿀 때는 현재 공식 문서와 사용 중인 버전을 다시 확인합니다.
핵심은 JSON 파싱을 최종 검증으로 착각하지 않는 것입니다. 단순한 형식 변환이면 JSON 모드와 애플리케이션 검증으로 충분할 수 있지만, 후속 코드가 고정된 구조를 전제로 한다면 Structured Outputs와 Pydantic을 함께 두고 parsed, refusal, incomplete를 별도 흐름으로 다루는 것이 판단하기 쉽습니다.