0Pricing
Claude Architect · 강의

구조화된 출력

파싱을 위한 스키마와 함께 --output-format json을 사용합니다

구조화된 출력은(는) CoddyKit의 무료 Claude Architect 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Claude Architect 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Why Structured Output in CI/CD

When you run Claude Code inside a pipeline, a human is not reading the result — a script is. A CI job needs a stable, machine-parseable answer so it can fail the build, post a comment, or gate a merge.

Two flags make this possible:

  • -p (or --print) runs Claude Code non-interactively — required in any pipeline.
  • --output-format json returns a parseable result instead of free-form text.

This lesson is Domain 4 (Prompt Engineering & Structured Output) meeting Scenario 5 (Claude Code for CI/CD).

claude -p "Review the staged diff for security bugs" \
  --output-format json

The Problem With Free-Form Text

If you let the model answer in prose, your pipeline has to scrape that prose with regexes — counting words, hunting for phrases like "looks good" or "found issues". That is brittle and a classic anti-pattern.

The same rule applies to the agentic loop: you terminate on a stop_reason, never by parsing text for words like "done". In CI, you decide pass/fail from structured fields, never from free text.

Structured output replaces fragile text-scraping with a contract your script can trust.

Adding a Schema

--output-format json gives you JSON, but plain JSON can still vary in shape. Pair it with a JSON Schema so the output always has the exact fields your pipeline expects.

A schema delivers two guarantees:

  • It eliminates syntax errors — no half-formed JSON to crash your parser.
  • It enforces required fields — the fields you mark required are always present.

Schema-constrained output is the same mechanism that powers tool use: tool_use + JSON Schema is how Claude returns reliable structured data.

{
  "type": "object",
  "properties": {
    "verdict": { "type": "string", "enum": ["pass", "fail"] },
    "issues": {
      "type": "array",
      "items": { "type": "object" }
    }
  },
  "required": ["verdict", "issues"]
}

Designing the Issue Object

Make each finding a precise object the pipeline can act on. A good review schema gives every issue a location, a severity, and an explanation — so the job can annotate the exact line.

Use an enum for severity to keep values consistent across runs. Free-text severities like "kinda bad" are unparseable.

{
  "type": "object",
  "properties": {
    "file":     { "type": "string" },
    "line":     { "type": "integer" },
    "severity": { "type": "string",
                   "enum": ["blocker", "major", "minor"] },
    "message":  { "type": "string" }
  },
  "required": ["file", "severity", "message"]
}

Required Fields: The Golden Rule

Mark a field required only if it is always present. This is the single most-tested structured-output rule.

If you require a field that may be absent — say line for a project-wide finding that has no specific line — the model will fabricate a value to satisfy the schema. That hallucinated line number then drives a wrong CI annotation.

In the previous scene, line was deliberately left out of required: not every issue maps to one line.

Enums + an "other" Escape Hatch

Enums keep values clean, but a rigid enum can box the model in when reality doesn't fit any category. The extensible pattern: add an "other" enum value plus a free-text detail field.

Now the model can stay in-schema for the common cases and still report the unexpected one without fabricating a wrong category.

{
  "category": {
    "type": "string",
    "enum": ["security", "performance",
             "style", "other"]
  },
  "category_detail": {
    "type": "string",
    "description": "Free text when category is 'other'"
  }
}

Guaranteeing Structure With tool_choice

When you call Claude through the SDK rather than the CLI, you guarantee structured output by combining a tool whose input_schema is your JSON Schema with the right tool_choice:

  • "auto" — the model may answer in text OR call a tool (no guarantee).
  • "any" — the model MUST call some tool, which guarantees structured output.
  • {"type":"tool","name":"X"} — forces one specific tool.

For a CI review where you always need the report object, force the exact tool by name.

resp = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=2048,
    tools=[review_report_tool],   # input_schema = your JSON Schema
    tool_choice={"type": "tool", "name": "emit_review"},
    messages=[{"role": "user", "content": diff_text}],
)

Parsing the CI Result

In the pipeline you read the JSON, then branch on a field — never on the prose. Pull the structured payload and let the verdict decide the exit code.

Because the schema marked verdict and issues as required, this code never has to guess whether the keys exist.

import json, subprocess, sys

out = subprocess.run(
    ["claude", "-p", PROMPT, "--output-format", "json"],
    capture_output=True, text=True,
).stdout

report = json.loads(out)
if report["verdict"] == "fail":
    for i in report["issues"]:
        print(f"{i['file']}:{i.get('line','-')} {i['message']}")
    sys.exit(1)

Validate, Then Retry With Feedback

Even with a schema, a value can be semantically wrong (a bad arithmetic total, a malformed reference). Validate the parsed object with Pydantic-style checks, and on a structural/format error use retry-with-feedback.

Send the model three things: the original input, the wrong output it produced, and the exact validation error. This fixes format, structural, and arithmetic mistakes.

Key limit: retry does NOT help when the information is simply absent from the source — no amount of re-prompting invents data that isn't there.

from pydantic import BaseModel, ValidationError

class Review(BaseModel):
    verdict: str
    issues: list[dict]

try:
    review = Review.model_validate_json(out)
except ValidationError as e:
    retry(original=diff_text, bad_output=out, error=str(e))

Review in an Isolated Session

If the same conversation that generated code also reviews it, the reviewer keeps its own reasoning and won't challenge itself — same-session self-review is an anti-pattern.

Run the structured review in an isolated, fresh session. An independent instance is far better at catching real defects. This pairs naturally with structured output: a clean session in, a clean JSON report out.

Tune the prompt with explicit criteria ("flag a comment only when it contradicts the code") to minimize false positives that would block good merges.

Blocking Checks vs. Overnight Audits

A pre-merge CI gate is blocking and time-sensitive — run it synchronously with -p --output-format json. Using the Message Batches API here is wrong: batches are 50% cheaper but have no latency SLA, up to a 24h window, and don't support multi-turn tool calling.

Reserve the Batch API for non-blocking jobs — an overnight audit of the whole repo, a nightly report — where custom_id correlates each request and you re-submit only failures.

Quick Check

Apply the structured-output rules to a real pipeline decision.

Recap: Structured Output in CI/CD

Key takeaways:

  • In pipelines run Claude Code with -p (non-interactive) and --output-format json plus a schema; branch on fields, never on prose.
  • A JSON Schema eliminates syntax errors and enforces required fields.
  • Mark a field required ONLY if it is always present — requiring a possibly-absent field causes fabrication.
  • Use enums with an "other" value + a detail field for extensibility.
  • Via the SDK, tool_choice "any" or a forced tool guarantees structured output; "auto" does not.
  • Validate (Pydantic-style) and retry-with-feedback (original + wrong output + exact error) for format errors — but retries can't supply absent data.
  • Review in an isolated/fresh session, not the generating one; minimize false positives with explicit criteria.
  • Blocking gate = synchronous; Batch API only for non-blocking overnight jobs.

자주 묻는 질문

“구조화된 출력” 강의는 무료인가요?

네 — “구조화된 출력” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Claude Architect 강의 전체를 잠금 해제할 수 있습니다. Claude Architect 강의에는 총 4개의 강의가 포함되어 있습니다.

“구조화된 출력”에서 뭘 배우나요?

파싱을 위한 스키마와 함께 --output-format json을 사용합니다 브라우저에서 직접 실행하는 실습 코드로 Claude Architect을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Claude Architect을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Claude Architect은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“구조화된 출력” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Claude Architect 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Claude Architect 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 비대화형 모드
  2. 구조화된 출력
  3. 검토를 위한 세션 격리
  4. 테스트 생성 및 표준
← Claude Architect(으)로 돌아가기