AI 개발

AI는 왜 JSON Schema가 필요한가? Structured Output, Function Calling 완전 해설

약 14분

2024년부터 제품 페이지는 JSON Schema, Function Calling, Structured Output을 한 기능처럼 적는다. 아니다. Schema는 계약 언어이고, Function Calling은 「호스트가 실행하라」이며, Structured Output은 「모델이 이 모양을 내야 한다」이다. 세 단어를 나눈 뒤, 확률 모델이 「JSON으로 반환하라」는 문장이 아니라 검증 가능한 Schema 뒤에 서야 하는 이유를 설명한다.

먼저 요약

개념 한 줄 어디에 두는가
JSON Schema 필드·타입·필수·열거의 계약. 모델 기능이 아님 git의 .json, CI의 validator
Function Calling 모델이 호출을 제안하고 호스트가 실행. 부작용은 여기 tools[] / arguments
Structured Output 최종 답장이 이 모양이어야 함. 보통 부작용 없음 json_schema / output_type

역할을 먼저 기억한다. Schema는 구경, Function Calling은 손을 뻗는 일, Structured Output은 객체를 돌려주는 일. 셋을 「모델이 JSON을 내게 하기」로 납작하게 만들면 다음 홉은 산문을 parse한다.

세 단어는 왜 항상 섞이나

문서와 SDK 예제와 게시물은 한 문장으로 겹치기를 좋아한다. Schema를 주면 적법한 JSON이 나온다고. 데모는 자주 성공한다. 사고는 「이미 구조화했다」고 믿은 뒤에 난다.

혼동은 세 가지 다른 일을 가린다:

  • Function Calling의 arguments는 여전히 문자열인 경우가 많다. 호스트는 다시 JSON.parse 해야 한다. 타입, enum, 여분 필드는 아직 막히지 않았다.
  • Structured Output은 최종 답장만 묶는다. 주문을 조회하거나 송금하거나 DB에 쓰지 않는다. 부작용은 여전히 Function Calling이다.
  • prompt 안에만 있는 Schema는 CI에서 실패할 수 없다. 「JSON으로 반환하라」는 실패하지 않는다. git의 Schema는 실패할 수 있다.

질문은 「AI가 JSON을 할 수 있나」가 아니다. 누가, 어느 순간에, 어떤 계약으로 불법 객체를 막는가이다. 세 단어는 세 명의 누구를 가리킨다.

JSON Schema란 무엇인가 (모델 기능이 아니다)

JSON Schema는 JSON 모양을 적는 문서다. 어떤 키, 어떤 타입, 무엇이 필수인지, enum 값, 여분 필드를 허용할지. 「JSON처럼 보인다」보다 엄격하고, TypeScript 타입이나 protobuf보다 모델 API에 더 자주 인용된다.

Agent / Structured Output이 실제로 쓰는 키워드는 대개 이것이다:

  • type / properties: 객체의 모양.
  • required: 빠지면 실패하는 키.
  • enum / pattern / 숫자 상하한: 「대충」을 유한 집합으로 만든다.
  • additionalProperties: false: 여분 키는 실패. prompt injection으로 들어온 필드도 막는다.

문법은 JSON Schema 튜토리얼. 여기서는 Schema가 먼저 validator와 테스트를 위한 것임을 기억한다. 분명한 description은 도구 선택을 돕지만 enum과 required를 대체하지 않는다.

{
  "$id": "https://example.com/schemas/order.json",
  "type": "object",
  "properties": {
    "order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
    "amount": { "type": "number", "exclusiveMinimum": 0 },
    "status": { "type": "string", "enum": ["paid", "refunded", "pending"] }
  },
  "required": ["order_id", "amount", "status"],
  "additionalProperties": false
}

위의 주문 계약은 Function Calling parameters, Structured Output json_schema, 호스트 ajv에 동시에 넣을 수 있다. 필드 변경은 한곳이다.

Function Calling이란 무엇인가

Function Calling(넓은 Tool Calling)은 「모델이 함수를 실행한다」가 아니다. 모델은 이름과 인자만 낸다. 호스트가 parse·검증·실행하고 결과를 대화에 되돌린다. 재고 조회, 환불, 티켓——부작용은 여기에 있다.

벤더는 도구를 tools[]로 쓴다. parameters는 Schema 부분집합이다:

{
  "type": "function",
  "function": {
    "name": "lookup_order",
    "description": "Look up an order by ID",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "pattern": "^A-[0-9]{4,}$"
        }
      },
      "required": ["order_id"],
      "additionalProperties": false
    },
    "strict": true
  }
}

AI Agent 완전 가이드는 이 홉을 루프 안에 그린다. MCP Tool Schema vs Function Calling는 MCP inputSchema와 벤더 parameters가 같은 사전의 두 철자임을 보여 준다.

이미 구조화된 것처럼 보인다. 빈틈은: arguments가 종종 문자열로 남는 것, 도구 결과에 Schema가 없는 경우가 많은 것, strict가 모델 쪽만 묶는 것이다. 호스트 검증이 없으면 amount가 숫자인지 문자열인지는 오늘 스냅샷에 달렸다.

Structured Output이란 무엇인가

Structured Output(JSON mode의 엄격한 형태)은 모델 자신의 최종 메시지를 묶는다. 분류, 추출, 요약 객체, Agent handoff——외부 부작용은 없지만 다음 홉은 고정된 모양이 필요하다.

필드 이름은 다르다. response_format, text.format.json_schema, output_type. 핵은 같다: Schema를 디코더에 주고 불법 token 경로를 자른다.

{
  "type": "json_schema",
  "json_schema": {
    "name": "order",
    "strict": true,
    "schema": {
      "$id": "https://example.com/schemas/order.json",
      "type": "object",
      "properties": {
        "order_id": { "type": "string" },
        "amount": { "type": "number" },
        "status": {
          "type": "string",
          "enum": ["paid", "refunded", "pending"]
        }
      },
      "required": ["order_id", "amount", "status"],
      "additionalProperties": false
    }
  }
}

DevDay 2026 예측는 이 층과 Function Calling과 Agent 런타임을 하나의 JSON 파이프로 썼다. 구경은 여전히 Schema다. 지름을 먼저 정하고 프로토콜을 붙인다.

strict: true를 켜면 빠진 필드, 잘못된 enum, 여분 키가 모델 쪽에서 자주 멈춘다. 그것은 「JSON으로 답하라」는 prompt를 대체한다. 호스트의 재검증을 대체하지 않으며 Function Calling도 대체하지 않는다.

셋은 어떻게 겹치는가

「모델이 JSON을 내게 하기」로 납작하게 만들면 역할이 사라진다. 나누면:

  1. JSON Schema는 공유 구경이다. 필드 사전, 필수, enum. 호출하지 않고 생성하지도 않는다. 「적법함」이 어떻게 보이는지만 적는다.
  2. Function Calling은 밖으로 손을 뻗는 일이다. 모델은 부작용을 요청한다. 인자는 Schema를 통과해야 하고 결과도 통과해야 한다.
  3. Structured Output은 객체를 돌려주는 일이다. 홉이 끝날 때 답장 자체가 그 모양이어야 한다.

동의어로 쓰지 않기 위한 대조:

층 모델이 하는 일 Schema가 없으면
Function Calling 호출을 제안하고 호스트를 기다림 인자가 산문이 됨. 여분 키가 결제 시스템에 들어갈 수 있음
Structured Output 최종 객체를 직접 냄 하류가 문단에서 필드를 긁음
JSON Schema 아무것도 하지 않음. 자일 뿐 prompt 안의 자는 CI에서 잴 수 없음

다단계 Agent는 둘 다 쓴다. Structured Output으로 분류 객체, Function Calling으로 조회와 환불. AI Agent와 JSON Schema 워크플로는 문의 순서다. 이 글은 세 블록의 이름만 붙인다.

AI는 왜 JSON Schema를 빠뜨릴 수 없나

모델과 프로그램은 같은 종류의 것이 아니다. 모델은 확률로 token을 이어 쓰고, 프로그램은 규칙으로 객체를 읽는다. 이음이 「오늘은 JSON처럼 보였다」뿐이라면, 빠진 필드, 문자열이 된 숫자, "Paid"로 쓰인 enum, 여분의 note는 다음 홉의 사실이 될 수 있다.

2024년 이전 사슬은 짧았다. 한 번의 채팅, 하나의 함수, 실패는 사람이 본다. 2026년은 길다. 분류 → 조회 → 환불 또는 사람 → 다른 Agent에 객체 전달. 어느 홉이 「JSON처럼」만 보이면 이후 단계는 오류를 고정한다.

같은 주문이라도 모델은 언제든 이런 「적법해 보이는」 객체를 낼 수 있다:

{
  "order_id": "A-1042",
  "amount": "99.00",
  "status": "Paid",
  "note": "please hurry"
}

amount는 문자열이고, status 대소문자가 틀렸으며, note가 생겼다. Schema가 없으면 하류는 99.00을 문자열로 SQL에 붙이거나 Paid를 알 수 없는 상태로 재시도할 수 있다. 디버그할 때는 먼저 로컬에서 포맷하고, 지난주 통과한 객체와 JSON Diff한다. 사용자 데이터가 담긴 응답을 공개 JSON 사이트에 붙이지 않는다.

Schema의 가치는 「모델에게 말하는 법을 가르치는 것」이 아니다. 불법 객체를 호스트 문에서 막는 것이다. 호출 전 한 번, 답장 후 한 번, 평가에서 또 한 번. 창이 크고 단계가 많을수록 산문 오류는 시스템 안의 사실이 되기 쉽다.

하나의 Schema, 두 투영

Function Calling용과 Structured Output용으로 「비슷한」 정의를 손수 쓰지 않는다. 형태는 하나의 소스, 두 개의 내보내기다:

  1. 저장소에 schemas/order.json을 두고 안정적인 $id와 버전을 붙인다.
  2. Function Calling으로 보낸다: function.parameters로 자르고 strict를 켠다. 조회 도구는 대개 order_id만 있으면 된다. 주문 전체를 인자에 넣지 않는다.
  3. Structured Output으로 보낸다: 같은 객체를 json_schema / output_type으로 추출·분류·최종 답장에 쓴다.

부분집합을 본다. 벤더 strict는 모든 property를 required에 넣고 일부 oneOf / $ref를 끄기도 한다. 소스는 더 풍부해도 된다. 내보내기가 대상마다 자른다. CI는 「소스 vs 투영」을 구조 Diff 하여 한쪽만 고치지 못하게 한다.

호스트 검증은 여전히 필요하다

strict: true는 모델 디코딩만 묶는다. 스냅샷 교체, strict 해제, arguments 재파싱, 열린 객체를 반환하는 도구——모두 모델 쪽을 우회할 수 있다. 같은 Schema를 호스트 세 곳에서 돌린다:

  • 도구 실행 전. parse → validate → execute. 실패하면 구조화 오류를 반환한다. 원문 문자열을 다음으로 넘기지 않는다.
  • 모델의 최종 객체를 받은 뒤. Structured Output 성공은 한도·통화·권한 같은 업무 규칙의 성공이 아니다.
  • 상태나 eval fixture를 쓸 때. checkpoint와 eval은 적법한 JSON이어야 한다. 채팅 로그로 진행을 추측하지 않는다.

모델 쪽 제약과 호스트 쪽 검증은 두 일이다. strict를 끄거나 API를 바꾸면 prompt 안의 Schema는 사라진다.

2026년에 적용하기 (파일 먼저, 그다음 prompt)

  • schemas/tools/, schemas/extract/, schemas/state/를 만든다. 각 파일에 $id와 버전.
  • 「JSON으로 반환하라」보다 Schema를 먼저 쓴다. prompt 문장은 CI에서 실패하지 않는다.
  • 부작용(조회, 송금, 메일)은 Function Calling. 분류, 추출, handoff는 Structured Output.
  • 새 객체는 기본으로 additionalProperties: false와 명시적 enum. 확장이 필요하면 별도의 metadata를 두고 뿌리는 풀지 않는다.
  • 도구가 열 개를 넘으면 먼저 검색한 뒤 호출한다. 카탈로그 전체를 한 요청에 넣지 않는다.
  • 비밀과 사용자 필드는 브라우저에서만 본다. 포맷, JSON Diff, 업로드 없음.

디렉터리는 얇아도 된다. 중요한 것은 파일이 prompt보다 앞선다는 점이다. 모델은 바뀌고 프로토콜은 늘어난다. 구경을 먼저 안정시킨다.

흔한 실수

  • Structured Output을 Function Calling으로 착각한다. 전자는 코드를 실행하지 않는다. 조회와 환불에는 도구 층이 필요하다.
  • prompt에만 「JSON으로 반환하라」고 쓴다. git에 Schema가 없으면 실패할 수 있는 테스트도 없다.
  • 도구 입력만 검증하고 최종 객체는 보지 않는다. 추출과 분류 사고는 「이미 JSON처럼 보이는」 답장에서 난다.
  • 프로토콜마다 비슷한 Schema를 손으로 쓴다. 3주면 갈라진다. 복사하지 말고 내보낸다.
  • 넓은 type: object로 설계를 피한다. 열린 객체는 문이 아니다. 버전을 쓰거나 명시적 metadata를 더한다.

FAQ

Structured Output이 Function Calling을 대체할 수 있나?

없다. Structured Output은 최종 답장 모양을 묶는다. Function Calling은 호스트에 부작용을 요청한다. 주문 필드 추출은 전자, 재고 조회나 환불은 후자. 같은 Schema를 인용해야지 서로를 대체하면 안 된다.

Function Calling에 이미 parameters가 있다. 별도 파일이 필요한가?

필요하다. parameters는 투영이지 원본이 아니다. 별도 파일이면 Structured Output, MCP, 평가, 호스트 ajv에 동시에 넣을 수 있다. tools[] 안만 고치면 3주 뒤 추출 Schema와 어긋난다.

Agent는 없고 추출이 한 번뿐이다. Schema가 필요한가?

오히려 더 필요하다. 결과가 곧바로 DB나 표에 들어가면 「다시 이해」할 다음 홉이 없다. Structured Output과 호스트 검증이 최소 완결 세트다.

strict: true를 켰다. 호스트 검증은 아직 필요한가?

필요하다. strict는 디코딩을 묶는다. 호스트는 업무 규칙, 재파싱, 벤더가 strict를 끈 뒤의 퇴화를 묶는다. 문은 싸고 두 개다. 하나를 빼면 모델 교체에서 드러난다.

거부된 JSON을 로컬에서 어떻게 보나?

먼저 포맷해서 구조를 보고, Schema에서 빠진 required나 잘못된 enum을 찾은 뒤, 지난번 통과한 버전과 Diff한다. JSONSort는 브라우저 로컬에서 하며 업로드하지 않는다. 비밀이 담긴 응답을 공개 사이트에 붙이지 않는다.

결론

AI가 JSON Schema를 필요로 하는 것은 모델이 자연어를 못 읽게 되어서가 아니다. 확률적 출력을 확정적 문에 연결해야 하기 때문이다. Function Calling은 「하라」고 하고, Structured Output은 「이 객체를 돌려내라」고 하며, Schema는 둘이 공유하는 구경이다.

오늘부터 schemas/extract_order.json을 쓰고, 그다음 어느 API를 부를지 정한다. 모델은 바뀌고 프로토콜은 옮긴다. 지름을 먼저 안정시킨다.

더 읽기

변경 기록: 최초 게시