Gemini API로 구조화된 JSON 생성하기: 개발자 완전 가이드

프롬프트만의 JSON, responseMimeType부터 responseSchema / JSON Schema까지. Gemini Structured Outputs, Python·REST 예제, Function Calling과의 역할, 배포 전 검증.

이전 기사AI Agent가 JSON 없이는 살 수 없는 이유Tool Calling 및 MCP는 홉 단위로 추적됩니다. 이것은 다음을 본다.최종 답변모델은 사용자 또는 다운스트림 프로그램을 제공합니다. Gemini가 JSON을 내보내도록 만드는 방법을 분석하고 검증하고 저장할 수 있습니다. 단지 JSON처럼 보이는 문장이 아닙니다.

이것이 Gemini 문서의 구조화된 출력(제어된 생성)입니다. Function Calling과 스키마 아이디어를 공유하지만 대상은 다릅니다. 전자는최종 페이로드; 후자는 제약한다도구 인수. 에이전트가 '인보이스 추출'과 '결제 API 호출'을 동일한 종류의 호출로 처리하지 않도록 이 둘을 분리하세요.

세 가지 접근 방식, 각각 더 엄격함

팀은 일반적으로 "Gemini 출력 JSON 만들기"를 위해 세 가지 방법을 시도합니다. 신뢰성은 규모에 따라 다릅니다.

접근하다당신이 통제하는 것충분할 때
프롬프트 전용: “JSON을 출력해 주세요.”소프트 제약; 마크다운 울타리와 후행 해설이 계속 나타납니다.탐색, 일회성 스크립트
responseMimeType: application/json출력은 유효한 JSON 텍스트여야 합니다.모양은 다양합니다. 성공하려면 단지 구문 분석()만 있으면 됩니다.
MIME + responseSchema / responseJsonSchema필드, 유형, 열거형 및 필수 키가 제한됩니다.프로덕션 추출, 양식, 에이전트 간 페이로드

Everyone has seen the first failure mode: a ```json fence, an extra paragraph, single quotes, a trailing comma. The second layer parses, but price may be a string and items may be missing. The third layer is this tutorial: hand JSON Schema to the API so the decoder avoids illegal paths at each token.

제한된 디코딩: 스키마가 프롬프트보다 나은 이유

A prompt only raises the odds that the model wants to comply. Structured Output compiles the Schema into generation: if the next token would break JSON syntax or leave the Schema (for example starting an undeclared field), its probability is suppressed. So response.text is usually a parseable object — no regex to strip fences.

Since 2025 the Gemini API complements the OpenAPI 3.0-style responseSchema with standard JSON Schema (often responseJsonSchema on the wire). Pydantic model_json_schema() and Zod exports can be sent almost as-is. Gemini 2.5 and later also tend to preserve property order from the Schema, which helps CSV columns and tables downstream.

Classification has a side path: responseMimeType: text/x.enum emits only the enum string (for example Keyboard), with no braces. Use application/json when you need an object; use the enum MIME when you need a single label.

Python: 전체 google-genai 예

Prefer the current SDK google-genai (from google import genai). Do not mix it with the legacy google-generativeai package. With GEMINI_API_KEY set:

from google import genai
from pydantic import BaseModel, Field


class LineItem(BaseModel):
    name: str = Field(description="Product name")
    qty: int = Field(description="Quantity, positive integer")
    unit_price_cents: int = Field(description="Unit price in cents")


class Invoice(BaseModel):
    vendor: str
    currency: str = Field(description="ISO 4217, e.g. CNY")
    items: list[LineItem]
    total_cents: int


client = genai.Client()
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Extract an invoice from: Acme sold 2 keyboards at 199 CNY each.",
    config={
        "response_mime_type": "application/json",
        "response_schema": Invoice,
    },
)

print(response.text)      # JSON string
invoice = response.parsed  # Invoice instance (Pydantic path)
print(invoice.total_cents)

response.parsed is meaningful when response_schema is a Pydantic or SDK type. If you pass a raw JSON Schema dict (next section), json.loads(response.text) and validate yourself.

For many records use list[Invoice] or wrap invoices: list[Invoice] in an object. An array at the root is less stable on some models than always returning an object; production code usually does the latter.

response_schema 대 JSON 스키마

어떤 구성 키를 사용할지 추측하지 마세요.

  • response_schema: Pydantic 모델, Python Enum 또는 SDK 스키마 객체. SDK는 이를 온-와이어 OpenAPI 하위 집합에 매핑합니다.
  • response_json_schema: a JSON Schema object (dict). Use it for Invoice.model_json_schema(), Zod toJSONSchema(), and richer keywords such as additionalProperties, minimum / maximum, and prefixItems.
schema = {
  "type": "object",
  "properties": {
    "vendor": { "type": "string" },
    "currency": { "type": "string", "enum": ["CNY", "USD", "EUR"] },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "qty": { "type": "integer", "minimum": 1 },
          "unit_price_cents": { "type": "integer", "minimum": 0 }
        },
        "required": ["name", "qty", "unit_price_cents"],
        "additionalProperties": False
      }
    },
    "total_cents": { "type": "integer" }
  },
  "required": ["vendor", "currency", "items", "total_cents"],
  "additionalProperties": False
}

response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Extract the invoice: …",
    config={
        "response_mime_type": "application/json",
        "response_json_schema": schema,
    },
)

Older REST docs use uppercase types in responseSchema (OBJECT, STRING, ARRAY, INTEGER). The JSON Schema path uses lowercase object / string. Do not mix the two keyword sets. Put field meaning in description: it enters the model context and decides whether qty is pieces or cases. Types alone cannot.

REST 요청의 모습

On the Gemini Developer API, generateContent puts structured output under generationConfig. The key goes in x-goog-api-key or a query param — never in a frontend repo.

POST https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent

{
  "contents": [
    {
      "role": "user",
      "parts": [{ "text": "Extract an invoice from the text: …" }]
    }
  ],
  "generationConfig": {
    "responseMimeType": "application/json",
    "responseJsonSchema": {
      "type": "object",
      "properties": {
        "vendor": { "type": "string" },
        "total_cents": { "type": "integer" }
      },
      "required": ["vendor", "total_cents"]
    }
  }
}

On success the candidate text is candidates[0].content.parts[0].text — a JSON string. Vertex AI uses the same field names; only the endpoint and GCP auth change. Images and PDFs can be inputs: the Schema constrains output, not multimodal input.

Function Calling로 작업을 분할하는 방법

둘 다 스키마를 사용하여 JSON을 관리하지만 서로 다른 홉에 위치합니다.

구조화된 출력기능 호출 / 도구 호출
제약되는 것최종 답변 JSON도구 인수 JSON
누가 부작용을 일으키나아무도; 그것은 단지 데이터일 뿐이다호스트 / MCP 서버
일반적인 구성responseMimeType + 스키마tools[].parameters / inputSchema
실패 시다시 시도하거나 사람에게 다시 문의하세요.오류를 도구 메시지로 작성하고 다시 질문하세요.

송장 추출, 조정 라벨, 메모를 작업 목록으로 전환: 구조화된 출력. 재고 조회, 티켓 생성, repo 파일 읽기: 도구 — 참조데이터 흐름 기사그리고JSON 현존 및 MCP 진화. 결제 API가 이미 실행된 척하기 위해 구조화된 출력을 사용하지 마세요. 모델이 이를 호출하지 않았습니다.

런타임 유효성 검사 및 일반적인 함정

제한된 디코딩은 비즈니스 정확성이 아닙니다. 두 개의 게이트를 유지하십시오.

  1. Syntax and Schema: after json.loads, validate again with the same JSON Schema (required, enum, minimum).
  2. Business invariants: for example sum(item.qty * item.unit_price_cents) == total_cents. Schema cannot express that; you write it.

일반적인 함정:

  • 지원되지 않는 키워드: 전체 초안 2020-12 스키마를 덤프하면 일부 키워드가 자동으로 무시될 수 있습니다. 유형/속성/필수/열거/항목으로 시작한 다음 additionalProperties 및 최소/최대를 추가하세요.
  • Array at the root: { "items": [ ... ] } as an object root is often more reliable.
  • Markdown 해설과 JSON 혼합: JSON MIME이 켜져 있으면 “설명을 먼저 하고 JSON”을 요청하지 마세요.
  • 잘림: maxOutputTokens를 높이거나 "목록을 먼저 분할한 다음 각 행을 채웁니다".
  • 프런트엔드의 키: 브라우저의 구조화된 출력 데모에서 API 키가 누출되었습니다. 스키마는 공개될 수 있습니다. 키는 서버에 남아 있습니다.

개발 중에는 스키마와 2~3개의 긍정적/부정적 샘플을 git에 보관하세요. JSON Toolbox에서 로컬로 구조와 Diff를 검사합니다. REST와 동일한 계약 테스트 습관이며 Gemini를 소비자로 사용합니다.

FAQ

JSON MIME 유형만 보내는 것과 스키마도 보내는 것의 차이점은 무엇입니까?

responseMimeType application/json만 사용하면 모델은 유효한 JSON을 내보내려고 시도하지만 필드 이름, 유형 및 필수 키에는 제한이 없습니다. responseSchema 또는 responseJsonSchema를 추가하면 디코딩 중에 토큰이 제한되므로 모양이 유지되거나 다음 에이전트로 전달될 만큼 충분히 안정적입니다.

response_schema와 response_json_schema를 어떻게 선택하나요?

Pydantic 모델 또는 SDK 스키마와 함께 response_schema를 사용하세요. SDK는 response.parsed를 노출할 수 있습니다. 전체 JSON Schema 객체(additionalProperties, min/max, prefixItems)에 대해 response_json_schema를 사용하거나 Pydantic/Zod model_json_schema()를 있는 그대로 보낼 때 사용하세요. 둘 다 response_mime_type=application/json이 필요합니다.

구조화된 출력이 Function Calling을 대체할 수 있나요?

아니요. 구조화된 출력은 사용자 또는 다운스트림 코드가 보는 최종 JSON을 제한합니다. 함수 호출 / 도구 호출은 도구 인수 JSON을 제한하고 여전히 호스트가 도구를 실행해야 합니다. 추출, 분류, 양식 작성을 위해 구조화된 출력을 사용합니다. 날씨, 파일 및 MCP 도구를 사용하세요. Agent 파이프라인은 종종 두 가지를 모두 사용합니다.

모델이 100% 스키마 준수를 보장합니까?

제한된 디코딩은 구문 오류와 유형 드리프트를 줄이지만 의미론적 환각, 잘림 및 지원되지 않는 키워드 무시는 여전히 발생합니다. 프로덕션에서는 유효성 검사기를 통해 동일한 스키마를 실행하고 실패 시 재시도하거나 성능을 저하시킵니다.

중첩된 개체, 배열 및 열거형이 지원됩니까?

예. 객체, 배열, 문자열 열거형이 일반적인 조합입니다. 분류를 위해 MIME을 text/x.enum로 설정하여 모델이 JSON 객체가 아닌 열거형 값만 내보내도록 할 수 있습니다. 매우 깊은 중첩 또는 순환 참조가 거부될 수 있습니다. 스키마를 평면화합니다.

스키마와 샘플 출력을 로컬에서 어떻게 검증합니까?

responseJsonSchema 및 몇 가지 모델 출력 샘플을 JSON 파일로 저장하세요. 브라우저의 JSON 도구 상자에서 로컬로 구문과 구조를 확인하세요. 아무 것도 업로드되지 않습니다. 출시 후 런타임에 동일한 스키마를 다시 사용하십시오.

요약

To get structured JSON from Gemini, the order is: Schema first, JSON MIME second, prompt last. The prompt owns meaning (what to extract); the Schema owns shape (what fields look like). Pydantic / Zod are author-friendly fronts; on the wire you send response_schema or response_json_schema.

Run one real invoice or a support transcript: write the Schema → call generateContent once → paste response.text into a validator. Only then wire a database or the next agent. Tool arguments still go through Function Calling / MCP — do not collapse them into one API.