---
title: 구조화된 출력
url: https://platform.claude.com/docs/ko/build-with-claude/structured-outputs
description: 에이전트 워크플로에서 검증된 JSON 결과를 얻습니다
featureMetadata:
  status: ga
  zdr:
    eligibility: eligible
    note: Excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements).
  supportedModels:
    - claude-fable-5-1
    - claude-mythos-5-1
    - claude-fable-5
    - claude-mythos-5
    - claude-mythos-preview
    - claude-opus-5-5
    - claude-opus-5
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-5-5
    - claude-sonnet-5
    - claude-sonnet-4-6
    - claude-sonnet-4-5-20250929
    - claude-opus-4-5-20251101
    - claude-haiku-5-5
    - claude-haiku-4-5-20251001
  supportedPlatforms:
    Claude API: ga
    Claude Platform on AWS: ga
    Amazon Bedrock:
      availability: ga
      note: Amazon Bedrock에서 구조화된 출력은 Claude Opus 4.6, Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Opus 4.5, Claude Haiku 4.5에 대해 레거시 [Amazon Bedrock (Opus 4.6 이하)](https://platform.claude.com/docs/ko/build-with-claude/claude-on-amazon-bedrock-legacy) 통합에서 사용할 수 있으며, [Claude in Amazon Bedrock](https://platform.claude.com/docs/ko/build-with-claude/claude-in-amazon-bedrock)에서는 사용할 수 없습니다.
    Google Cloud: ga
    Microsoft Foundry: ga
---

"Structured outputs"(구조화된 출력)는 Claude의 응답이 특정 스키마를 따르도록 제약하여, 다운스트림 처리를 위한 유효하고 파싱 가능한 출력을 보장합니다. 구조화된 출력은 두 가지 상호 보완적인 기능을 제공합니다:

* **JSON 출력** (`output_config.format`): Claude의 응답을 특정 JSON 형식으로 받습니다. 예를 들어 이미지나 텍스트에서 데이터를 추출하거나, 구조화된 보고서를 생성하거나, API 응답의 형식을 지정할 때 사용합니다. 이 페이지에서는 JSON 출력을 다룹니다.
* **엄격한 도구 사용** (`strict: true`): 도구 이름과 입력에 대한 스키마 검증을 보장합니다. [엄격한 도구 사용](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/strict-tool-use)을 참조하세요.

이 기능들은 독립적으로 사용하거나 [같은 요청에서 함께](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#using-both-features-together) 사용할 수 있습니다.

## 구조화된 출력을 사용하는 이유

구조화된 출력이 없으면 Claude는 애플리케이션을 중단시키는 잘못된 형식의 JSON 응답이나 유효하지 않은 도구 입력을 생성할 수 있습니다. 신중하게 프롬프트를 작성하더라도 다음과 같은 문제가 발생할 수 있습니다:

* 유효하지 않은 JSON 구문으로 인한 파싱 오류
* 필수 필드 누락
* 일관되지 않은 데이터 타입
* 오류 처리 및 재시도가 필요한 스키마 위반

구조화된 출력은 제약된 디코딩(constrained decoding)을 통해 스키마를 준수하는 응답을 보장합니다:

* **항상 유효함:** 더 이상 `JSON.parse()` 오류가 없습니다
* **타입 안전:** 필드 타입과 필수 필드가 보장됩니다
* **신뢰성:** 스키마 위반으로 인한 재시도가 필요 없습니다

## 작동 방식

<Steps>
  <Step title="스키마 정의하기">
    원하는 구조를 JSON 스키마 또는 사용하는 언어의 타입으로 기술합니다. 스키마는 JSON Schema를 따르며, 몇 가지 [제한 사항](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#json-schema-limitations)이 있습니다.
  </Step>

  <Step title="output_config.format으로 전송하기">
    요청은 `type: "json_schema"`와 함께 `output_config.format`에 스키마를 담아 전달합니다. SDK 헬퍼가 이를 자동으로 설정해 줍니다.
  </Step>

  <Step title="응답 읽기">
    Claude는 응답의 텍스트 콘텐츠 블록에 스키마와 일치하는 유효한 JSON을 반환합니다. SDK 헬퍼는 이를 사용자의 타입으로 파싱합니다.
  </Step>
</Steps>

## 사용법

<Tabs>
  <Tab title="cURL">
    ```bash
    curl https://api.anthropic.com/v1/messages \
      -H "content-type: application/json" \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -d '{
        "model": "claude-opus-5-5",
        "max_tokens": 1024,
        "messages": [
          {
            "role": "user",
            "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
          }
        ],
        "output_config": {
          "format": {
            "type": "json_schema",
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"},
                "plan_interest": {"type": "string"},
                "demo_requested": {"type": "boolean"}
              },
              "required": ["name", "email", "plan_interest", "demo_requested"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```

    Claude는 응답의 텍스트 콘텐츠 블록에 다음과 같은 JSON을 반환합니다:

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create \
      --transform 'content.#(type=="text").text|@fromstr' \
      --format jsonl <<'YAML'
    model: claude-opus-5-5
    max_tokens: 1024
    messages:
      - role: user
        content: >-
          Extract the key information from this email: John Smith
          (john@example.com) is interested in our Enterprise plan and wants
          to schedule a demo for next Tuesday at 2pm.
    output_config:
      format:
        type: json_schema
        schema:
          type: object
          properties:
            name: {type: string}
            email: {type: string}
            plan_interest: {type: string}
            demo_requested: {type: boolean}
          required: [name, email, plan_interest, demo_requested]
          additionalProperties: false
    YAML
    ```

    JSON 출력의 일부 속성만 출력하려면 `--transform`에 [GJSON 경로](https://platform.claude.com/docs/ko/cli-sdks-libraries/cli/using#transform-output-with-gjson)를 전달하세요.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Python">
    ```python
    from pydantic import BaseModel
    # ...


    class ContactInfo(BaseModel):
        name: str
        email: str
        plan_interest: str
        demo_requested: bool


    client = Anthropic()

    response = client.messages.parse(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": (
                    "Extract the key information from this email: "
                    "John Smith (john@example.com) is interested in our Enterprise plan "
                    "and wants to schedule a demo for next Tuesday at 2pm."
                ),
            }
        ],
        output_format=ContactInfo,
    )

    print(response.parsed_output)
    ```

    권장 메서드인 `client.messages.parse()`에 [Pydantic](https://docs.pydantic.dev/) 모델을 `output_format`으로 전달하세요. SDK는 모델의 스키마를 변환하여 `output_config.format`으로 전송하고, 응답을 검증한 뒤, 파싱된 모델을 `parsed_output`에 반환합니다.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    name='John Smith' email='john@example.com' plan_interest='Enterprise' demo_requested=True
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { z } from "zod";
    import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

    const ContactInfoSchema = z.object({
      name: z.string(),
      email: z.string(),
      plan_interest: z.string(),
      demo_requested: z.boolean()
    });

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content:
            "Extract the key information from this email: " +
            "John Smith (john@example.com) is interested in our Enterprise plan " +
            "and wants to schedule a demo for next Tuesday at 2pm."
        }
      ],
      output_config: { format: zodOutputFormat(ContactInfoSchema) }
    });

    // 자동으로 파싱 및 검증됩니다
    console.log(response.parsed_output);
    ```

    [Zod](https://zod.dev/) 스키마를 `zodOutputFormat()`으로 감싸서 `client.messages.parse()`에 `output_config.format`으로 전달하세요. `zodOutputFormat()`은 스키마를 변환하고, `parse()`는 응답을 검증한 뒤 스키마에 맞게 타입이 지정된 파싱 결과를 `parsed_output`에 반환합니다.

    위 예제의 출력은 다음과 같습니다:

    ```javascript Output
    {
      name: 'John Smith',
      email: 'john@example.com',
      plan_interest: 'Enterprise',
      demo_requested: true
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp
    using Anthropic;
    using Anthropic.Models.Messages;
    using Anthropic.Services;

    var client = new AnthropicClient();

    var response = await client.Messages.Create<ContactInfo>(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = [new() {
            Role = Role.User,
            Content = "Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm."
        }],
    });

    if (response.Content.Select(block => block.Parsed()).OfType<ContactInfo>().FirstOrDefault() is { } contact)
    {
        Console.WriteLine(contact);
    }

    public record ContactInfo
    {
        public string Name { get; set; } = "";
        public string Email { get; set; } = "";
        public string PlanInterest { get; set; } = "";
        public bool DemoRequested { get; set; }
    }
    ```

    일반 C# 클래스나 레코드를 제네릭 `Create<T>()` 오버로드에 전달하세요. SDK는 해당 타입에서 JSON 스키마를 도출하여 변환하고, 각 텍스트 블록을 다시 해당 타입으로 파싱합니다.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    ContactInfo { Name = John Smith, Email = john@example.com, PlanInterest = Enterprise, DemoRequested = True }
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

    func main() {
    	client := anthropic.NewClient()

    	var contact ContactInfo
    	_, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.BetaMessageParam{
    			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(
    				"Extract the key information from this email: " +
    					"John Smith (john@example.com) is interested in our Enterprise plan " +
    					"and wants to schedule a demo for next Tuesday at 2pm.",
    			)),
    		},
    		OutputConfig: anthropic.BetaOutputConfigParam{
    			Format: anthropic.BetaJSONOutputFormatParam{Schema: &contact},
    		},
    	})
    	if err != nil {
    		panic(err)
    	}

    	fmt.Printf("%#v\n", contact)
    }
    ```

    <Note>
      Go에서 구조체를 스키마로 전달하는 기능은 베타 단계이므로, 이 예제에서는 `client.Beta.Messages.New`를 사용합니다. GA 버전인 `client.Messages.New`를 사용하려면 [원시 JSON 스키마 사용하기](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#using-a-raw-json-schema)에 나온 것처럼 JSON 스키마를 전달하세요.
    </Note>

    SDK는 구조체에서 JSON 스키마를 생성 및 변환하고, 응답을 해당 구조체로 언마샬링합니다.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```
  </Tab>

  <Tab title="Java">
    ```java
    static class ContactInfo {
        public String name;
        public String email;
        public String plan_interest;
        public boolean demo_requested;
    }

    void main() throws Exception {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        StructuredMessageCreateParams<ContactInfo> params = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .addUserMessage("Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm.")
            .outputConfig(ContactInfo.class)
            .build();

        StructuredMessage<ContactInfo> response = client.messages().create(params);
        ContactInfo contact = response.content().stream()
            .flatMap(block -> block.text().stream())
            .findFirst().orElseThrow().text();
        IO.println(new ObjectMapper().writeValueAsString(contact));
    }
    ```

    Java 클래스를 `outputConfig()`에 전달하면 SDK는 해당 클래스에서 JSON 스키마를 도출하고 스키마를 검증합니다. 그런 다음 빌더가 `StructuredMessageCreateParams<T>`를 생성합니다. SDK는 스키마를 변환하는 대신, 로컬 검증을 통해 [API에서 지원하지 않는 제약 조건](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#json-schema-limitations)을 거부합니다. 파싱된 결과는 `response.content().stream().flatMap(block -> block.text().stream()).findFirst().orElseThrow().text()`로 읽으세요.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```

    <Note>
      스키마 클래스를 최상위 클래스 또는 `static` 중첩 클래스로 선언하세요. 이 요구 사항은 SDK가 JSON 응답을 클래스 인스턴스로 역직렬화하는 데 사용하는 Jackson Databind 라이브러리(`com.fasterxml.jackson.databind`)에서 비롯되며, 이 라이브러리는 비정적 내부 클래스를 인스턴스화할 수 없습니다.
    </Note>

    <Accordion title="제네릭 타입 소거">
      Java는 클래스의 메타데이터에서 필드에 대한 제네릭 타입 정보를 유지하지만, 다른 범위에서는 제네릭 타입 소거가 적용됩니다. `List<Book>` 타입의 `BookList.books` 필드에서는 JSON 스키마를 도출할 수 있지만, 동일한 타입의 로컬 변수에서는 유효한 JSON 스키마를 도출할 수 없습니다.

      JSON 응답을 Java 클래스 인스턴스로 변환하는 중 오류가 발생하면, 진단을 돕기 위해 오류 메시지에 JSON 응답이 포함됩니다. JSON 응답에 민감한 정보가 포함될 수 있는 경우, 직접 로깅하지 않거나 오류 메시지에서 민감한 세부 정보를 삭제하도록 하세요.
    </Accordion>

    <Accordion title="로컬 스키마 검증">
      구조화된 출력은 [JSON Schema 언어의 하위 집합](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#json-schema-limitations)을 지원합니다. SDK는 이 하위 집합에 맞추기 위해 클래스에서 스키마를 자동으로 생성합니다. `outputConfig(Class<T>)` 메서드는 지정된 클래스에서 도출된 스키마에 대해 검증 검사를 수행합니다.

      핵심 사항:

      * **로컬 검증**은 원격 AI 모델에 요청을 보내지 않고 수행됩니다.
      * **원격 검증**도 JSON 스키마를 수신할 때 AI 모델에 의해 수행됩니다.
      * **버전 호환성:** SDK 버전이 오래된 경우 로컬 검증은 실패하지만 원격 검증은 성공할 수 있습니다.
      * **로컬 검증 비활성화:** 호환성 문제가 발생하면 `JsonSchemaLocalValidation.NO`를 전달하세요:

      ```java
      import com.anthropic.core.JsonSchemaLocalValidation;
      // ...

      static class BookList {
          public List<String> books;
      }

      void main() {
          StructuredMessageCreateParams<BookList> createParams = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(2048)
              .outputConfig(BookList.class, JsonSchemaLocalValidation.NO)
              .addUserMessage("List some famous late twentieth century novels.")
              .build();
      }
      ```
    </Accordion>

    <Accordion title="스트리밍">
      구조화된 출력은 스트리밍에서도 작동합니다. 응답이 스트림 이벤트로 도착하므로, JSON을 역직렬화하기 전에 전체 응답을 누적해야 합니다.

      `MessageAccumulator`를 사용하여 스트림에서 JSON 문자열을 수집하세요. 누적이 완료되면 `MessageAccumulator.message(Class<T>)`를 호출하여 누적된 `Message`를 `StructuredMessage`로 변환하세요. 이는 JSON을 Java 클래스로 자동 역직렬화합니다.
    </Accordion>

    <Accordion title="JSON 스키마 속성">
      SDK가 Java 클래스에서 JSON 스키마를 도출할 때, 기본적으로 `public` 필드 또는 `public` getter 메서드로 표현되는 모든 속성을 포함하고 `public`이 아닌 필드와 getter 메서드는 제외합니다.

      어노테이션으로 가시성을 제어할 수 있습니다:

      * `@JsonIgnore`는 `public` 필드 또는 getter 메서드를 제외합니다
      * `@JsonProperty`는 `public`이 아닌 필드 또는 getter 메서드를 포함합니다

      `public` getter 메서드와 함께 `private` 필드를 정의하면, SDK는 getter에서 속성 이름을 도출합니다(예: `public` 메서드 `getMyValue()`가 있는 `private` 필드 `myValue`는 `"myValue"` 속성을 생성합니다). 관례적이지 않은 getter 이름을 사용하려면 메서드에 `@JsonProperty` 어노테이션을 추가하세요.

      각 클래스는 JSON 스키마에 대해 최소 하나의 속성을 정의해야 합니다. 다음과 같이 스키마 속성을 생성할 수 있는 필드나 getter 메서드가 없으면 검증 오류가 발생합니다:

      * 클래스에 필드나 getter 메서드가 없는 경우
      * 모든 `public` 멤버에 `@JsonIgnore` 어노테이션이 있는 경우
      * 모든 `public`이 아닌 멤버에 `@JsonProperty` 어노테이션이 없는 경우
      * 필드가 빈 `"properties"` 필드를 생성하는 `Map` 타입을 사용하는 경우
    </Accordion>

    <Accordion title="컴포지션과 상속">
      Java 클래스는 JSON 스키마를 정의할 때 구조를 공유하기 위해 컴포지션과 상속을 사용할 수 있습니다. 각 패턴은 출력 구조에 다르게 영향을 미칩니다.

      **컴포지션**은 중첩된 JSON 출력을 생성합니다. `A`와 `B`를 컴포지션하는 클래스 `Composed`에서 스키마를 도출하는 경우:

      ```java
      static class A {
          public String a;
      }

      static class B {
          public String b;
      }

      static class Composed {
          public A composedA;
          public B composedB;
      }
      ```

      JSON 출력은 다음과 같은 중첩 구조를 가집니다:

      ```json
      {
        "composedA": { "a": "hello" },
        "composedB": { "b": "world" }
      }
      ```

      **상속**은 평탄한 JSON 출력을 생성합니다. `Base`를 확장하는 클래스 `Derived`에서 스키마를 도출하는 경우:

      ```java
      static class Base {
          public String a;
      }

      static class Derived extends Base {
          public String b;
      }
      ```

      JSON 출력은 다음과 같은 평탄한 구조를 가집니다:

      ```json
      {
        "a": "hello",
        "b": "world"
      }
      ```
    </Accordion>

    <Accordion title="어노테이션 (Jackson 및 Swagger)">
      Jackson Databind 어노테이션을 사용하여 Java 클래스에서 도출된 JSON 스키마를 보강할 수 있습니다:

      ```java
      import com.fasterxml.jackson.annotation.JsonClassDescription;
      import com.fasterxml.jackson.annotation.JsonIgnore;
      import com.fasterxml.jackson.annotation.JsonPropertyDescription;

      static class Person {

        @JsonPropertyDescription("The first name and surname of the person")
        public String name;

        public int birthYear;

        @JsonPropertyDescription("The year the person died, or 'present' if the person is living.")
        public String deathYear;
      }

      @JsonClassDescription("The details of one published book")
      static class Book {

        public String title;
        public Person author;

        @JsonPropertyDescription("The year in which the book was first published.")
        public int publicationYear;

        @JsonIgnore
        public String genre;
      }

      static class BookList {
        public List<Book> books;
      }
      ```

      어노테이션 요약:

      * `@JsonClassDescription`: 클래스에 설명 추가
      * `@JsonPropertyDescription`: 필드 또는 getter 메서드에 설명 추가
      * `@JsonIgnore`: 스키마에서 `public` 필드 또는 getter 제외
      * `@JsonProperty`: 스키마에 `public`이 아닌 필드 또는 getter 포함

      `@JsonProperty(required = false)`를 사용하면 SDK는 `false` 값을 무시합니다. 클래스에서 도출된 스키마는 항상 모든 속성을 필수로 표시합니다.

      타입별 제약 조건을 위해 Swagger Core (OpenAPI 3) `@Schema` 및 `@ArraySchema` 어노테이션도 사용할 수 있습니다:

      ```java
      import io.swagger.v3.oas.annotations.media.ArraySchema;
      import io.swagger.v3.oas.annotations.media.Schema;

      static class Article {

        @ArraySchema(minItems = 1)
        public List<String> authors;

        public String title;

        @Schema(format = "date")
        public String publicationDate;

        public int pageCount;
      }
      ```

      로컬 검증은 지원되지 않는 제약 키워드를 사용하지 않았는지 확인하지만, 제약 값은 로컬에서 검증되지 않습니다. 예를 들어, 지원되지 않는 `"format"` 값은 로컬 검증을 통과할 수 있지만 원격 오류를 일으킬 수 있습니다.

      동일한 스키마 필드를 설정하기 위해 Jackson과 Swagger 어노테이션을 모두 사용하면 Jackson 어노테이션이 우선합니다.
    </Accordion>
  </Tab>

  <Tab title="PHP">
    ```php
    use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
    use Anthropic\Lib\Contracts\StructuredOutputModel;

    $client = new Client();

    class ContactInfo implements StructuredOutputModel
    {
        use StructuredOutputModelTrait;

        public string $name;
        public string $email;
        public string $plan_interest;
        public bool $demo_requested;
    }

    $message = $client->messages->create(
        maxTokens: 1024,
        messages: [
            [
                'role' => 'user',
                'content' => 'Extract the key information from this email: '
                    . 'John Smith (john@example.com) is interested in our Enterprise plan '
                    . 'and wants to schedule a demo for next Tuesday at 2pm.',
            ],
        ],
        model: 'claude-opus-5-5',
        outputConfig: ['format' => ContactInfo::class],
    );

    $contact = $message->parsedOutput();
    if ($contact instanceof ContactInfo) {
        var_dump($contact);
    }
    ```

    `StructuredOutputModel`을 구현하고 `StructuredOutputModelTrait`를 사용하는 클래스를 정의한 다음, `outputConfig: ['format' => MyClass::class]`에 해당 클래스 이름을 전달하세요. SDK는 클래스의 PHP 8 속성 타입에서 JSON 스키마를 도출하여 변환하고, `$message->parsedOutput()`에서 타입이 지정된 인스턴스를 반환합니다.

    `parsedOutput()`은 성공 시 모델 인스턴스를 반환하고, 파싱이 실패하면 `null`(또는 오류 배열)을 반환합니다. 필드에 접근하기 전에 `instanceof`를 사용하여 타입을 좁히세요.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    object(ContactInfo)#42 (4) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
      ["demo_requested"]=>
      bool(true)
    }
    ```

    <Accordion title="타입 추론">
      SDK는 네이티브 PHP 8 속성 타입을 JSON Schema에 매핑합니다:

      | PHP 타입                            | JSON Schema             |
      | --------------------------------- | ----------------------- |
      | `string`                          | `"string"`              |
      | `int`                             | `"integer"`             |
      | `float`                           | `"number"`              |
      | `bool`                            | `"boolean"`             |
      | `array`                           | `"array"` (다음 참고 사항 참조) |
      | `?type` (nullable)                | 선택적 필드                  |
      | `StructuredOutputModel`을 구현하는 클래스 | 중첩 객체                   |

      `array` 속성의 경우, SDK는 요소 타입이 `#[Constrained(itemClass: MyModel::class)]` 또는 `/** @var MyModel[] */` docblock으로 선언된 중첩 `StructuredOutputModel`인 경우에만 `items` 스키마를 추가합니다. 스칼라 배열(`string[]`, `int[]`)은 제약 없는 `{"type":"array"}`를 생성합니다.

      nullable이 아닌 모든 속성은 필수 필드가 됩니다.
    </Accordion>

    <Accordion title="#[Constrained] 속성을 사용한 제약 조건">
      `#[Constrained]` 속성으로 제약 조건을 추가하세요:

      ```php
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      class Address implements StructuredOutputModel { use StructuredOutputModelTrait; public string $street; }

      class Profile implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          #[Constrained(description: 'Age in years', minimum: 0, maximum: 150)]
          public int $age;

          #[Constrained(format: 'email')]
          public string $email;

          #[Constrained(itemClass: Address::class, minItems: 1)]
          public array $addresses;
      }
      ```

      **API 적용 제약 조건** (스키마에 전송됨): `description`, `format`, `const`, `itemClass`, `minItems` (0 또는 1만).

      **SDK 검증 제약 조건** (전송 스키마에서 제거되고, 설명에 추가되며, 응답에 대해 검증됨): `minimum`, `maximum`, `multipleOf`, `minLength`, `maxLength`.
    </Accordion>
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

    class ContactInfo < Anthropic::BaseModel
      required :name, String
      required :email, String
      required :plan_interest, String
      required :demo_requested, Anthropic::Boolean
    end

    message = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [{
        role: "user",
        content: "Extract the key information from this email: " \
          "John Smith (john@example.com) is interested in our Enterprise plan " \
          "and wants to schedule a demo for next Tuesday at 2pm."
      }],
      output_config: {format: ContactInfo}
    )

    contact = message.parsed_output
    puts contact
    ```

    `Anthropic::BaseModel`을 확장하는 클래스를 정의하고 `messages.create()`에 `output_config: {format: Model}`로 전달하세요. SDK는 클래스에서 JSON 스키마를 도출하여 변환하고, 파싱된 결과를 타입이 지정된 Ruby 객체로 응답의 `parsed_output` 속성에 할당합니다.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {name: "John Smith", email: "john@example.com", plan_interest: "Enterprise", demo_requested: true}
    ```

    <Accordion title="고급 모델 기능">
      Ruby SDK는 더 풍부한 스키마를 위한 추가 모델 정의 기능을 지원합니다:

      * **`doc:` 키워드:** 더 유익한 스키마 출력을 위해 필드에 설명 추가
      * **`Anthropic::ArrayOf[T]`:** 타입이 지정된 배열. 배열 수준 제약 조건(`min_items:`, `max_items:`)은 `ArrayOf` 자체가 아닌 `required`/`optional`의 키워드로 전달하세요
      * **`Anthropic::EnumOf[:a, :b]`:** 제약된 값을 가진 열거형 필드
      * **`Anthropic::UnionOf[T1, T2]`:** `anyOf`에 매핑되는 유니온 타입

      ```ruby
      class FamousNumber < Anthropic::BaseModel
        required :value, Float
        optional :reason, String, doc: "why is this number mathematically significant?"
      end

      class Output < Anthropic::BaseModel
        required :numbers, Anthropic::ArrayOf[FamousNumber], min_items: 3, max_items: 5
      end

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages: [{role: "user", content: "give me some famous numbers"}],
        output_config: {format: Output}
      )

      message.parsed_output
      # => #<Output numbers=[#<FamousNumber value=3.14159... reason="Pi is...">...]>
      ```
    </Accordion>
  </Tab>
</Tabs>

## SDK 변환 작동 방식

대부분의 SDK 헬퍼는 지원되지 않는 기능을 사용하는 스키마를 변환합니다. 변환 단계는 다음과 같습니다:

1. **지원되지 않는 제약 조건 제거** (예: `minimum`, `maximum`, `minLength`, `maxLength`)
2. **설명 업데이트**: 지원되지 않는 각 제약 조건을 필드 설명에 추가합니다 (예: `{minimum: 100}`)
3. 모든 객체에 **`additionalProperties: false` 추가**
4. **문자열 형식 필터링**: 지원되는 목록만 남깁니다
5. 헬퍼가 응답을 검증하는 경우, 원래 스키마와 모든 제약 조건에 대해 **응답 검증**

Claude는 단순화된 스키마를 받지만, 응답을 검증하는 헬퍼는 코드에서 여전히 모든 제약 조건을 적용합니다.

**예시:** `minimum: 100`이 있는 필드는 전송되는 스키마에서 일반 정수가 되며, SDK는 필드 설명에 `{minimum: 100}`을 추가합니다. 응답을 검증하는 헬퍼는 여전히 `minimum: 100`에 대해 응답을 확인합니다.

## 원시 JSON 스키마 사용하기

파일, OpenAPI 명세 또는 런타임에 스키마를 생성하는 코드에서 가져온 JSON 스키마를 사용하려면 `output_config.format`에 전달하세요.

<Tabs>
  <Tab title="cURL">
    ```bash
    curl https://api.anthropic.com/v1/messages \
      -H "content-type: application/json" \
      -H "x-api-key: $ANTHROPIC_API_KEY" \
      -H "anthropic-version: 2023-06-01" \
      -d '{
        "model": "claude-opus-5-5",
        "max_tokens": 1024,
        "messages": [
          {
            "role": "user",
            "content": "Extract the key information from this email: John Smith (john@example.com) is interested in our Enterprise plan and wants to schedule a demo for next Tuesday at 2pm."
          }
        ],
        "output_config": {
          "format": {
            "type": "json_schema",
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"},
                "plan_interest": {"type": "string"},
                "demo_requested": {"type": "boolean"}
              },
              "required": ["name", "email", "plan_interest", "demo_requested"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```

    Claude는 응답의 텍스트 콘텐츠 블록에 다음과 같은 JSON을 반환합니다:

    ```json Output
    {
      "name": "John Smith",
      "email": "john@example.com",
      "plan_interest": "Enterprise",
      "demo_requested": true
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash
    ant messages create \
      --transform 'content.#(type=="text").text|@fromstr' \
      --format jsonl <<'YAML'
    model: claude-opus-5-5
    max_tokens: 1024
    messages:
      - role: user
        content: >-
          Extract the key information from this email: John Smith
          (john@example.com) is interested in our Enterprise plan and wants
          to schedule a demo for next Tuesday at 2pm.
    output_config:
      format:
        type: json_schema
        schema:
          type: object
          properties:
            name: {type: string}
            email: {type: string}
            plan_interest: {type: string}
            demo_requested: {type: boolean}
          required: [name, email, plan_interest, demo_requested]
          additionalProperties: false
    YAML
    ```

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Python">
    ```python
    client = anthropic.Anthropic()

    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[
            {
                "role": "user",
                "content": (
                    "Extract the key information from this email: "
                    "John Smith (john@example.com) is interested in our Enterprise plan "
                    "and wants to schedule a demo for next Tuesday at 2pm."
                ),
            }
        ],
        output_config={
            "format": {
                "type": "json_schema",
                "schema": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "email": {"type": "string"},
                        "plan_interest": {"type": "string"},
                        "demo_requested": {"type": "boolean"},
                    },
                    "required": ["name", "email", "plan_interest", "demo_requested"],
                    "additionalProperties": False,
                },
            }
        },
    )
    text = next(block.text for block in response.content if block.type == "text")
    contact = json.loads(text)
    print(contact)
    ```

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {'name': 'John Smith', 'email': 'john@example.com', 'plan_interest': 'Enterprise', 'demo_requested': True}
    ```

    [API에서 지원하지 않는 제약 조건](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#json-schema-limitations)을 필드 설명으로 옮기려면, 전송하기 전에 `anthropic` 패키지의 `transform_schema()`에 스키마를 통과시키고 필요하면 결과를 편집하세요. `transform_schema()`는 Pydantic 모델도 받습니다. `client.messages.parse()`와 달리, 이 함수는 스키마를 전송하지 않고 변환된 스키마를 반환합니다.
  </Tab>

  <Tab title="TypeScript">
    ```typescript
    import { jsonSchemaOutputFormat } from "@anthropic-ai/sdk/helpers/json-schema";

    const client = new Anthropic();

    const response = await client.messages.parse({
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: "Extract contact info: John Smith, john@example.com, interested in the Pro plan"
        }
      ],
      output_config: {
        format: jsonSchemaOutputFormat({
          type: "object",
          properties: {
            name: { type: "string" },
            email: { type: "string" },
            planInterest: { type: "string" }
          },
          required: ["name", "email", "planInterest"],
          additionalProperties: false
        } as const)
      }
    });

    // response.parsed_output의 타입은 { name: string; email: string; planInterest: string } | null입니다
    console.log(response.parsed_output);
    ```

    Zod를 설치하지 않고 일반 JSON 스키마를 `parse()`에 전달하려면 `jsonSchemaOutputFormat()`을 사용하세요. API는 스키마를 준수하는 응답을 반환하며, 스키마를 `as const`로 선언하면 TypeScript가 스키마에 따라 `parsed_output`의 타입을 자동으로 추론합니다. 가져오거나 생성된 스키마의 경우 `parsed_output`의 타입은 `unknown`이 됩니다.

    기본적으로 이 헬퍼는 `zodOutputFormat()`과 같은 방식으로 [스키마를 변환](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#how-sdk-transformation-works)합니다. 변경 없이 전송하려면 두 번째 인수로 `{ transform: false }`를 전달하세요. `zodOutputFormat()`과 달리 응답을 검증하지 않으므로, `minimum`과 같은 [API에서 지원하지 않는 제약 조건](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#json-schema-limitations)의 위반을 잡아내지 못합니다.

    위 예제의 출력은 다음과 같습니다:

    ```javascript Output
    { name: 'John Smith', email: 'john@example.com', planInterest: 'Pro' }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp
    using System.Text.Json;
    using Anthropic;
    using Anthropic.Models.Messages;

    var client = new AnthropicClient();

    var response = await client.Messages.Create(new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = [new() {
            Role = Role.User,
            Content = "Extract the key information from this email: "
                + "John Smith (john@example.com) is interested in our Enterprise plan "
                + "and wants to schedule a demo for next Tuesday at 2pm."
        }],
        OutputConfig = new OutputConfig
        {
            Format = new JsonOutputFormat
            {
                Schema = new Dictionary<string, JsonElement>
                {
                    ["type"] = JsonSerializer.SerializeToElement("object"),
                    ["properties"] = JsonSerializer.SerializeToElement(new
                    {
                        name = new { type = "string" },
                        email = new { type = "string" },
                        plan_interest = new { type = "string" },
                        demo_requested = new { type = "boolean" },
                    }),
                    ["required"] = JsonSerializer.SerializeToElement(
                        new[] { "name", "email", "plan_interest", "demo_requested" }),
                    ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                },
            },
        },
    });

    if (response.Content.Select(b => b.Value).OfType<TextBlock>().FirstOrDefault() is { } textBlock)
    {
        // JSON이 스키마와 일치함이 보장됩니다
        var contact = JsonSerializer.Deserialize<JsonElement>(textBlock.Text);
        Console.WriteLine(contact);
    }
    ```

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise","demo_requested":true}
    ```
  </Tab>

  <Tab title="Go">
    ```go
    type ContactInfo struct {
    	Name          string `json:"name"`
    	Email         string `json:"email"`
    	PlanInterest  string `json:"plan_interest"`
    	DemoRequested bool   `json:"demo_requested"`
    }

    func main() {
    	client := anthropic.NewClient()

    	message, _ := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages: []anthropic.MessageParam{
    			anthropic.NewUserMessage(anthropic.NewTextBlock(
    				"Extract the key information from this email: " +
    					"John Smith (john@example.com) is interested in our Enterprise plan " +
    					"and wants to schedule a demo for next Tuesday at 2pm.",
    			)),
    		},
    		OutputConfig: anthropic.OutputConfigParam{
    			Format: anthropic.JSONOutputFormatParam{
    				Schema: map[string]any{
    					"type": "object",
    					"properties": map[string]any{
    						"name":           map[string]string{"type": "string"},
    						"email":          map[string]string{"type": "string"},
    						"plan_interest":  map[string]string{"type": "string"},
    						"demo_requested": map[string]string{"type": "boolean"},
    					},
    					"required":             []string{"name", "email", "plan_interest", "demo_requested"},
    					"additionalProperties": false,
    				},
    			},
    		},
    	})

    	for _, block := range message.Content {
    		switch variant := block.AsAny().(type) {
    		case anthropic.TextBlock:
    			var contact ContactInfo
    			json.Unmarshal([]byte(variant.Text), &contact)
    			fmt.Printf("%#v\n", contact)
    		}
    	}
    }
    ```

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    main.ContactInfo{Name:"John Smith", Email:"john@example.com", PlanInterest:"Enterprise", DemoRequested:true}
    ```

    SDK가 맵 스키마를 변환하도록 하려면 베타 API에서 `BetaJSONSchemaOutputFormat()`에 통과시키세요.
  </Tab>

  <Tab title="Java">
    ```java
    import com.anthropic.core.JsonValue;
    import com.anthropic.models.messages.JsonOutputFormat;
    // ...
    import com.anthropic.models.messages.OutputConfig;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;

    void main() throws Exception {
        AnthropicClient client = AnthropicOkHttpClient.fromEnv();

        JsonOutputFormat.Schema schema = JsonOutputFormat.Schema.builder()
            .putAdditionalProperty("type", JsonValue.from("object"))
            .putAdditionalProperty("properties", JsonValue.from(Map.of(
                "name", Map.of("type", "string"),
                "email", Map.of("type", "string"),
                "plan_interest", Map.of("type", "string"))))
            .putAdditionalProperty("required", JsonValue.from(
                List.of("name", "email", "plan_interest")))
            .putAdditionalProperty("additionalProperties", JsonValue.from(false))
            .build();

        OutputConfig outputConfig = OutputConfig.builder()
            .format(JsonOutputFormat.builder().schema(schema).build())
            .build();

        MessageCreateParams createParams = MessageCreateParams.builder()
            .model(Model.CLAUDE_OPUS_5_5)
            .maxTokens(1024)
            .outputConfig(outputConfig)
            .addUserMessage(
                "John Smith (john@example.com) is interested in our Enterprise plan.")
            .build();

        String json = client.messages().create(createParams).content().stream()
            .flatMap(contentBlock -> contentBlock.text().stream())
            .findFirst()
            .orElseThrow()
            .text();

        JsonNode contact = new ObjectMapper().readTree(json);
        IO.println(contact);
    }
    ```

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {"name":"John Smith","email":"john@example.com","plan_interest":"Enterprise"}
    ```

    배열과 설명이 포함된 중첩 스키마를 생성하는 더 광범위한 예제는 SDK 저장소의 [`StructuredOutputsRawExample.java`](https://github.com/anthropics/anthropic-sdk-java/blob/main/anthropic-java-example/src/main/java/com/anthropic/example/StructuredOutputsRawExample.java)를 참조하세요.
  </Tab>

  <Tab title="PHP">
    ```php
    use Anthropic\Messages\OutputConfig;
    use Anthropic\Messages\JSONOutputFormat;

    $client = new Client();

    $message = $client->messages->create(
        maxTokens: 1024,
        messages: [
            [
                'role' => 'user',
                'content' => 'Extract the key information from this email: '
                    . 'John Smith (john@example.com) is interested in our Enterprise plan.',
            ],
        ],
        model: 'claude-opus-5-5',
        outputConfig: OutputConfig::with(format: JSONOutputFormat::with(schema: [
            'type' => 'object',
            'properties' => [
                'name' => ['type' => 'string'],
                'email' => ['type' => 'string'],
                'plan_interest' => ['type' => 'string'],
            ],
            'required' => ['name', 'email', 'plan_interest'],
            'additionalProperties' => false,
        ])),
    );

    $textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
    $contact = json_decode($textBlock->text, associative: true);
    var_dump($contact);
    ```

    스키마를 연관 배열로 `OutputConfig::with()`에 전달하고, `json_decode()`로 응답을 디코딩하세요.

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    array(3) {
      ["name"]=>
      string(10) "John Smith"
      ["email"]=>
      string(16) "john@example.com"
      ["plan_interest"]=>
      string(10) "Enterprise"
    }
    ```
  </Tab>

  <Tab title="Ruby">
    ```ruby
    client = Anthropic::Client.new

    response = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: "Extract the key information from this email: " \
            "John Smith (john@example.com) is interested in our Enterprise plan " \
            "and wants to schedule a demo for next Tuesday at 2pm."
        }
      ],
      output_config: {
        format: {
          type: "json_schema",
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              email: { type: "string" },
              plan_interest: { type: "string" },
              demo_requested: { type: "boolean" }
            },
            required: ["name", "email", "plan_interest", "demo_requested"],
            additionalProperties: false
          }
        }
      }
    )

    text = response.content.find { it.type == :text }.text
    contact = JSON.parse(text)
    p contact
    ```

    위 예제의 출력은 다음과 같습니다:

    ```text Output wrap
    {"name" => "John Smith", "email" => "john@example.com", "plan_interest" => "Enterprise", "demo_requested" => true}
    ```
  </Tab>
</Tabs>

## 일반적인 사용 사례

<AccordionGroup>
  <Accordion title="데이터 추출">
    비구조화된 텍스트에서 구조화된 데이터 추출:

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 4096,
          "messages": [
            {
              "role": "user",
              "content": "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Total: $500.00"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "invoice_number": {"type": "string"},
                  "date": {"type": "string"},
                  "total_amount": {"type": "number"},
                  "line_items": {
                    "type": "array",
                    "items": {"type": "object", "additionalProperties": false}
                  },
                  "customer_name": {"type": "string"}
                },
                "required": ["invoice_number", "date", "total_amount", "line_items", "customer_name"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text|@fromstr' \
        --format jsonl <<'YAML'
      model: claude-opus-5-5
      max_tokens: 4096
      messages:
        - role: user
          content: "Extract invoice data from: Invoice #12345, Date: 2024-01-15, Total: $500.00"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              invoice_number: {type: string}
              date: {type: string}
              total_amount: {type: number}
              line_items:
                type: array
                items: {type: object, additionalProperties: false}
              customer_name: {type: string}
            required: [invoice_number, date, total_amount, line_items, customer_name]
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel


      class Invoice(BaseModel):
          invoice_number: str
          date: str
          total_amount: float
          line_items: list[dict]
          customer_name: str


      client = anthropic.Anthropic()
      invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=4096,
          output_format=Invoice,
          messages=[
              {"role": "user", "content": f"Extract invoice data from: {invoice_text}"}
          ],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const InvoiceSchema = z.object({
        invoice_number: z.string(),
        date: z.string(),
        total_amount: z.number(),
        line_items: z.array(z.record(z.string(), z.any())),
        customer_name: z.string()
      });

      const invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: { format: zodOutputFormat(InvoiceSchema) },
        messages: [{ role: "user", content: `Extract invoice data from: ${invoiceText}` }]
      });
      console.log(response.parsed_output);
      ```

      ```csharp C#
      AnthropicClient client = new();

      string invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 4096,
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          invoice_number = new { type = "string" },
                          date = new { type = "string" },
                          total_amount = new { type = "number" },
                          line_items = new
                          {
                              type = "array",
                              items = new
                              {
                                  type = "object",
                                  additionalProperties = false,
                              },
                          },
                          customer_name = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "invoice_number", "date", "total_amount", "line_items", "customer_name" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
          Messages = [new() { Role = Role.User, Content = $"Extract invoice data from: {invoiceText}" }]
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      client := anthropic.NewClient()

      invoiceText := "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      schema := map[string]any{
      	"type":                 "object",
      	"additionalProperties": false,
      	"properties": map[string]any{
      		"invoice_number": map[string]any{"type": "string"},
      		"date":           map[string]any{"type": "string"},
      		"total_amount":   map[string]any{"type": "number"},
      		"line_items": map[string]any{
      			"type": "array",
      			"items": map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"description": map[string]any{"type": "string"},
      					"quantity":    map[string]any{"type": "number"},
      					"unit_price":  map[string]any{"type": "number"},
      				},
      				"required": []string{"description", "quantity", "unit_price"},
      			},
      		},
      		"customer_name": map[string]any{"type": "string"},
      	},
      	"required": []string{"invoice_number", "date", "total_amount", "line_items", "customer_name"},
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 4096,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Extract invoice data from: %s", invoiceText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class LineItem {
          @JsonProperty("description")
          public String description;

          @JsonProperty("quantity")
          public int quantity;

          @JsonProperty("unit_price")
          public double unitPrice;
      }

      static class Invoice {
          @JsonProperty("invoice_number")
          public String invoiceNumber;

          @JsonProperty("date")
          public String date;

          @JsonProperty("total_amount")
          public double totalAmount;

          @JsonProperty("line_items")
          public List<LineItem> lineItems;

          @JsonProperty("customer_name")
          public String customerName;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          String invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

          StructuredMessageCreateParams<Invoice> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(4096L)
              .outputConfig(Invoice.class)
              .addUserMessage("Extract invoice data from: " + invoiceText)
              .build();

          StructuredMessage<Invoice> response = client.messages().create(params);
          Invoice invoice = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(invoice.invoiceNumber + ": $" + invoice.totalAmount);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Invoice implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $invoice_number;
          public string $date;
          public float $total_amount;
          public array $line_items;
          public string $customer_name;
      }

      $invoiceText = "Invoice #12345, Date: 2024-01-15, Total: $500.00";

      $message = $client->messages->create(
          maxTokens: 4096,
          messages: [
              ['role' => 'user', 'content' => "Extract invoice data from: $invoiceText"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Invoice::class],
      );

      $invoice = $message->parsedOutput();
      if ($invoice instanceof Invoice) {
          echo "Invoice {$invoice->invoice_number}: \${$invoice->total_amount}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class LineItem < Anthropic::BaseModel
        required :description, String
        required :amount, Float
      end

      class Invoice < Anthropic::BaseModel
        required :invoice_number, String
        required :date, String
        required :total_amount, Float
        required :line_items, Anthropic::ArrayOf[LineItem]
        required :customer_name, String
      end

      invoice_text = "Invoice #12345, Date: 2024-01-15, Total: $500.00"

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 4096,
        output_config: {format: Invoice},
        messages: [
          {role: "user", content: "Extract invoice data from: #{invoice_text}"}
        ]
      )

      invoice = message.parsed_output
      puts "Invoice #{invoice.invoice_number}: $#{invoice.total_amount}"
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="분류">
    구조화된 카테고리로 콘텐츠 분류:

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Classify this feedback: Great product, fast shipping!"
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "category": {"type": "string"},
                  "confidence": {"type": "number"},
                  "tags": {"type": "array", "items": {"type": "string"}},
                  "sentiment": {"type": "string"}
                },
                "required": ["category", "confidence", "tags", "sentiment"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text|@fromstr' \
        --format jsonl <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      messages:
        - role: user
          content: "Classify this feedback: Great product, fast shipping!"
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              category:
                type: string
              confidence:
                type: number
              tags:
                type: array
                items:
                  type: string
              sentiment:
                type: string
            required:
              - category
              - confidence
              - tags
              - sentiment
            additionalProperties: false
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class Classification(BaseModel):
          category: str
          confidence: float
          tags: list[str]
          sentiment: str


      feedback_text = "Great product, but the delivery was slow."
      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=Classification,
          messages=[{"role": "user", "content": f"Classify this feedback: {feedback_text}"}],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const ClassificationSchema = z.object({
        category: z.string(),
        confidence: z.number(),
        tags: z.array(z.string()),
        sentiment: z.string()
      });

      const feedbackText = "Great product, but the delivery was slow.";
      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(ClassificationSchema) },
        messages: [{ role: "user", content: `Classify this feedback: ${feedbackText}` }]
      });

      console.log(response.parsed_output);
      ```

      ```csharp C#
      string feedbackText = "Great product, fast shipping!";

      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = $"Classify this feedback: {feedbackText}" }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          category = new { type = "string" },
                          confidence = new { type = "number" },
                          tags = new { type = "array", items = new { type = "string" } },
                          sentiment = new { type = "string" },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "category", "confidence", "tags", "sentiment" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      feedbackText := "Great product, fast shipping!"

      schema := map[string]any{
      	"type": "object",
      	"properties": map[string]any{
      		"category":   map[string]any{"type": "string"},
      		"confidence": map[string]any{"type": "number"},
      		"tags":       map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
      		"sentiment":  map[string]any{"type": "string"},
      	},
      	"required":             []string{"category", "confidence", "tags", "sentiment"},
      	"additionalProperties": false,
      }

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: schema,
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock(fmt.Sprintf("Classify this feedback: %s", feedbackText))),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		var result map[string]any
      		json.Unmarshal([]byte(variant.Text), &result)
      		fmt.Println(result)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class Classification {
          @JsonProperty("category")
          public String category;

          @JsonProperty("confidence")
          public double confidence;

          @JsonProperty("tags")
          public List<String> tags;

          @JsonProperty("sentiment")
          public String sentiment;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          String feedbackText = "Great product, fast shipping!";

          StructuredMessageCreateParams<Classification> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(Classification.class)
              .addUserMessage("Classify this feedback: " + feedbackText)
              .build();

          StructuredMessage<Classification> response = client.messages().create(params);
          Classification result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.category + " (" + result.confidence + ")");
      }
      ```

      ```php PHP
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Classification implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $category;
          public float $confidence;
          public array $tags;
          public string $sentiment;
      }

      $feedbackText = "Great product, fast shipping!";

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => "Classify this feedback: {$feedbackText}"]
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => Classification::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof Classification) {
          echo "{$result->category} ({$result->confidence}): {$result->sentiment}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class Classification < Anthropic::BaseModel
        required :category, String
        required :confidence, Float
        required :tags, Anthropic::ArrayOf[String]
        required :sentiment, String
      end

      feedback_text = "Great product, fast shipping!"

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: {format: Classification},
        messages: [
          {role: "user", content: "Classify this feedback: #{feedback_text}"}
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="API 응답 형식 지정">
    API에 바로 사용할 수 있는 응답 생성:

    <CodeGroup>
      ```bash cURL
      curl https://api.anthropic.com/v1/messages \
        -H "content-type: application/json" \
        -H "x-api-key: $ANTHROPIC_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -d '{
          "model": "claude-opus-5-5",
          "max_tokens": 1024,
          "messages": [
            {
              "role": "user",
              "content": "Process this request: ..."
            }
          ],
          "output_config": {
            "format": {
              "type": "json_schema",
              "schema": {
                "type": "object",
                "properties": {
                  "status": {"type": "string"},
                  "data": {"type": "object", "additionalProperties": false},
                  "errors": {
                    "type": "array",
                    "items": {"type": "object", "additionalProperties": false}
                  },
                  "metadata": {"type": "object", "additionalProperties": false}
                },
                "required": ["status", "data", "metadata"],
                "additionalProperties": false
              }
            }
          }
        }'
      ```

      ```bash CLI
      ant messages create \
        --transform 'content.#(type=="text").text' \
        --raw-output <<'YAML'
      model: claude-opus-5-5
      max_tokens: 1024
      output_config:
        format:
          type: json_schema
          schema:
            type: object
            properties:
              status:
                type: string
              data:
                type: object
                additionalProperties: false
              errors:
                type: array
                items:
                  type: object
                  additionalProperties: false
              metadata:
                type: object
                additionalProperties: false
            required:
              - status
              - data
              - metadata
            additionalProperties: false
      messages:
        - role: user
          content: "Process this request: ..."
      YAML
      ```

      ```python Python
      from pydantic import BaseModel

      client = Anthropic()


      class APIResponse(BaseModel):
          status: str
          data: dict
          errors: list[dict] | None
          metadata: dict


      response = client.messages.parse(
          model="claude-opus-5-5",
          max_tokens=1024,
          output_format=APIResponse,
          messages=[{"role": "user", "content": "Process this request: ..."}],
      )

      print(response.parsed_output)
      ```

      ```typescript TypeScript
      import { z } from "zod";
      import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";

      const client = new Anthropic();

      const APIResponseSchema = z.object({
        status: z.string(),
        data: z.record(z.string(), z.any()),
        errors: z.array(z.record(z.string(), z.any())).optional(),
        metadata: z.record(z.string(), z.any())
      });

      const response = await client.messages.parse({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: { format: zodOutputFormat(APIResponseSchema) },
        messages: [{ role: "user", content: "Process this request..." }]
      });

      console.log(response.parsed_output);
      ```

      ```csharp C#
      var parameters = new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new() { Role = Role.User, Content = "Process this request: ..." }],
          OutputConfig = new OutputConfig
          {
              Format = new JsonOutputFormat
              {
                  Schema = new Dictionary<string, JsonElement>
                  {
                      ["type"] = JsonSerializer.SerializeToElement("object"),
                      ["properties"] = JsonSerializer.SerializeToElement(new
                      {
                          status = new { type = "string" },
                          data = new { type = "object", additionalProperties = false },
                          errors = new
                          {
                              type = "array",
                              items = new { type = "object", additionalProperties = false },
                          },
                          metadata = new { type = "object", additionalProperties = false },
                      }),
                      ["required"] = JsonSerializer.SerializeToElement(new[] { "status", "data", "metadata" }),
                      ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
                  },
              },
          },
      };

      var message = await client.Messages.Create(parameters);
      Console.WriteLine(message);
      ```

      ```go Go
      client := anthropic.NewClient()

      response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
      	Model:     anthropic.ModelClaudeOpus5_5,
      	MaxTokens: 1024,
      	OutputConfig: anthropic.OutputConfigParam{
      		Format: anthropic.JSONOutputFormatParam{
      			Schema: map[string]any{
      				"type":                 "object",
      				"additionalProperties": false,
      				"properties": map[string]any{
      					"status": map[string]any{
      						"type": "string",
      					},
      					"data": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      					},
      					"errors": map[string]any{
      						"type": "array",
      						"items": map[string]any{
      							"type":                 "object",
      							"additionalProperties": false,
      						},
      					},
      					"metadata": map[string]any{
      						"type":                 "object",
      						"additionalProperties": false,
      					},
      				},
      				"required": []string{"status", "data", "metadata"},
      			},
      		},
      	},
      	Messages: []anthropic.MessageParam{
      		anthropic.NewUserMessage(anthropic.NewTextBlock("Process this request: ...")),
      	},
      })
      if err != nil {
      	log.Fatal(err)
      }
      for _, block := range response.Content {
      	switch variant := block.AsAny().(type) {
      	case anthropic.TextBlock:
      		fmt.Println(variant.Text)
      	}
      }
      ```

      ```java Java
      import com.fasterxml.jackson.annotation.JsonProperty;

      static class APIData {
          @JsonProperty("message")
          public String message;

          @JsonProperty("resource_id")
          public String resourceId;
      }

      static class APIError {
          @JsonProperty("code")
          public String code;

          @JsonProperty("message")
          public String message;
      }

      static class APIMetadata {
          @JsonProperty("request_id")
          public String requestId;

          @JsonProperty("timestamp")
          public String timestamp;
      }

      static class APIResponse {
          @JsonProperty("status")
          public String status;

          @JsonProperty("data")
          public APIData data;

          @JsonProperty("errors")
          public List<APIError> errors;

          @JsonProperty("metadata")
          public APIMetadata metadata;
      }

      void main() {
          AnthropicClient client = AnthropicOkHttpClient.fromEnv();

          StructuredMessageCreateParams<APIResponse> params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .outputConfig(APIResponse.class)
              .addUserMessage("Process this request: ...")
              .build();

          StructuredMessage<APIResponse> response = client.messages().create(params);
          APIResponse result = response.content().stream()
              .flatMap(block -> block.text().stream())
              .findFirst().orElseThrow().text();
          IO.println(result.status);
      }
      ```

      ```php PHP
      use Anthropic\Lib\Attributes\Constrained;
      use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
      use Anthropic\Lib\Contracts\StructuredOutputModel;

      $client = new Client();

      class Payload implements StructuredOutputModel { use StructuredOutputModelTrait; public string $message; }

      class APIError implements StructuredOutputModel { use StructuredOutputModelTrait; public string $code; public string $detail; }

      class Metadata implements StructuredOutputModel { use StructuredOutputModelTrait; public string $request_id; }

      class APIResponse implements StructuredOutputModel
      {
          use StructuredOutputModelTrait;

          public string $status;
          public Payload $data;
          #[Constrained(itemClass: APIError::class)]
          public ?array $errors;
          public Metadata $metadata;
      }

      $message = $client->messages->create(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => 'Process this request: ...']
          ],
          model: 'claude-opus-5-5',
          outputConfig: ['format' => APIResponse::class],
      );

      $result = $message->parsedOutput();
      if ($result instanceof APIResponse) {
          echo "{$result->status}: {$result->data->message}\n";
      }
      ```

      ```ruby Ruby
      client = Anthropic::Client.new

      class Payload < Anthropic::BaseModel
        required :message, String
      end

      class APIError < Anthropic::BaseModel
        required :code, String
        required :detail, String
      end

      class Metadata < Anthropic::BaseModel
        required :request_id, String
      end

      class APIResponse < Anthropic::BaseModel
        required :status, String
        required :data, Payload
        optional :errors, Anthropic::ArrayOf[APIError]
        required :metadata, Metadata
      end

      message = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        output_config: {format: APIResponse},
        messages: [
          {role: "user", content: "Process this request: ..."}
        ]
      )
      puts message.parsed_output
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## 엄격한 도구 사용

문법 제약 샘플링으로 도구 입력에 JSON Schema 준수를 적용하려면 [엄격한 도구 사용](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/strict-tool-use)을 참조하세요.

### 두 기능을 함께 사용하기

JSON 출력과 엄격한 도구 사용은 서로 다른 문제를 해결하며 함께 작동합니다:

* **JSON 출력**은 Claude의 응답 형식을 제어합니다(Claude가 말하는 내용)
* **엄격한 도구 사용**은 도구 매개변수를 검증합니다(Claude가 함수를 호출하는 방식)

결합하면 Claude는 유효성이 보장된 매개변수로 도구를 호출하고 구조화된 JSON 응답을 반환할 수 있습니다. 이는 신뢰할 수 있는 도구 호출과 구조화된 최종 출력이 모두 필요한 에이전트 워크플로에 유용합니다.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [
        {
          "role": "user",
          "content": "Help me plan a trip to Paris departing May 15, 2026"
        }
      ],
      "output_config": {
        "format": {
          "type": "json_schema",
          "schema": {
            "type": "object",
            "properties": {
              "summary": {"type": "string"},
              "next_steps": {"type": "array", "items": {"type": "string"}}
            },
            "required": ["summary", "next_steps"],
            "additionalProperties": false
          }
        }
      },
      "tools": [
        {
          "name": "search_flights",
          "strict": true,
          "input_schema": {
            "type": "object",
            "properties": {
              "destination": {"type": "string"},
              "date": {"type": "string", "format": "date"}
            },
            "required": ["destination", "date"],
            "additionalProperties": false
          }
        }
      ]
    }'
  ```

  ```bash CLI
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: Help me plan a trip to Paris departing May 15, 2026
  # JSON outputs(JSON 출력): 구조화된 응답 형식
  output_config:
    format:
      type: json_schema
      schema:
        type: object
        properties:
          summary:
            type: string
          next_steps:
            type: array
            items:
              type: string
        required: [summary, next_steps]
        additionalProperties: false
  # Strict tool use(엄격한 도구 사용): 도구 매개변수 보장
  tools:
    - name: search_flights
      strict: true
      input_schema:
        type: object
        properties:
          destination:
            type: string
          date:
            type: string
            format: date
        required: [destination, date]
        additionalProperties: false
  YAML
  ```

  ```python Python
  response = client.messages.create(
      model="claude-opus-5-5",
      max_tokens=1024,
      messages=[
          {
              "role": "user",
              "content": "Help me plan a trip to Paris departing May 15, 2026",
          }
      ],
      # JSON 출력: 구조화된 응답 형식
      output_config={
          "format": {
              "type": "json_schema",
              "schema": {
                  "type": "object",
                  "properties": {
                      "summary": {"type": "string"},
                      "next_steps": {"type": "array", "items": {"type": "string"}},
                  },
                  "required": ["summary", "next_steps"],
                  "additionalProperties": False,
              },
          }
      },
      # 엄격한 도구 사용: 도구 매개변수 보장
      tools=[
          {
              "name": "search_flights",
              "strict": True,
              "input_schema": {
                  "type": "object",
                  "properties": {
                      "destination": {"type": "string"},
                      "date": {"type": "string", "format": "date"},
                  },
                  "required": ["destination", "date"],
                  "additionalProperties": False,
              },
          }
      ],
  )

  print(response)
  ```

  ```typescript TypeScript
  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Help me plan a trip to Paris departing May 15, 2026" }],
    // JSON 출력: 구조화된 응답 형식
    output_config: {
      format: {
        type: "json_schema",
        schema: {
          type: "object",
          properties: {
            summary: { type: "string" },
            next_steps: { type: "array", items: { type: "string" } }
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    // 엄격한 도구 사용: 도구 매개변수 보장
    tools: [
      {
        name: "search_flights",
        description: "Search for available flights to a destination on a specific date",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: { type: "string" },
            date: { type: "string", format: "date" }
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  });

  // Claude는 먼저 도구를 호출하거나(tool_use) JSON으로 응답할 수 있습니다(text)
  console.log("Stop reason:", response.stop_reason);
  for (const block of response.content) {
    switch (block.type) {
      case "tool_use":
        console.log(`Tool call: ${block.name}(${JSON.stringify(block.input)})`);
        break;
      case "text":
        console.log("Response:", block.text);
        break;
    }
  }
  ```

  ```csharp C#
  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new() { Role = Role.User, Content = "Help me plan a trip to Paris departing May 15, 2026" }],
      // JSON 출력: 구조화된 응답 형식
      OutputConfig = new OutputConfig
      {
          Format = new JsonOutputFormat
          {
              Schema = new Dictionary<string, JsonElement>
              {
                  ["type"] = JsonSerializer.SerializeToElement("object"),
                  ["properties"] = JsonSerializer.SerializeToElement(new
                  {
                      summary = new { type = "string" },
                      next_steps = new { type = "array", items = new { type = "string" } },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "summary", "next_steps" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              },
          },
      },
      // 엄격한 도구 사용(strict tool use): 도구 매개변수 보장
      Tools =
      [
          new Tool
          {
              Name = "search_flights",
              Strict = true,
              InputSchema = new InputSchema(new Dictionary<string, JsonElement>
              {
                  ["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>
                  {
                      ["destination"] = new { type = "string" },
                      ["date"] = new { type = "string", format = "date" },
                  }),
                  ["required"] = JsonSerializer.SerializeToElement(new[] { "destination", "date" }),
                  ["additionalProperties"] = JsonSerializer.SerializeToElement(false),
              }),
          }
      ],
  };

  var message = await client.Messages.Create(parameters);
  Console.WriteLine(message);
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Help me plan a trip to Paris departing May 15, 2026")),
  	},
  	// JSON 출력: 구조화된 응답 형식
  	OutputConfig: anthropic.OutputConfigParam{
  		Format: anthropic.JSONOutputFormatParam{
  			Schema: map[string]any{
  				"type":                 "object",
  				"additionalProperties": false,
  				"properties": map[string]any{
  					"summary":    map[string]any{"type": "string"},
  					"next_steps": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
  				},
  				"required": []string{"summary", "next_steps"},
  			},
  		},
  	},
  	// 엄격한 도구 사용: 도구 매개변수 보장
  	Tools: []anthropic.ToolUnionParam{
  		{OfTool: &anthropic.ToolParam{
  			Name:   "search_flights",
  			Strict: anthropic.Bool(true),
  			InputSchema: anthropic.ToolInputSchemaParam{
  				Properties: map[string]any{
  					"destination": map[string]any{"type": "string"},
  					"date":        map[string]any{"type": "string", "format": "date"},
  				},
  				Required: []string{"destination", "date"},
  				ExtraFields: map[string]any{
  					"additionalProperties": false,
  				},
  			}}},
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }
  fmt.Println(response.Content)
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  // JSON 출력: 구조화된 응답 형식
  JsonOutputFormat.Schema outputSchema = JsonOutputFormat.Schema.builder()
      .putAdditionalProperty("type", JsonValue.from("object"))
      .putAdditionalProperty("properties", JsonValue.from(Map.of(
          "summary", Map.of("type", "string"),
          "next_steps", Map.of("type", "array", "items", Map.of("type", "string"))
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("summary", "next_steps")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  // 엄격한 도구 사용: 도구 매개변수 보장
  InputSchema toolSchema = InputSchema.builder()
      .properties(JsonValue.from(Map.of(
          "destination", Map.of("type", "string"),
          "date", Map.of("type", "string", "format", "date")
      )))
      .putAdditionalProperty("required", JsonValue.from(List.of("destination", "date")))
      .putAdditionalProperty("additionalProperties", JsonValue.from(false))
      .build();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Help me plan a trip to Paris departing May 15, 2026")
      .outputConfig(OutputConfig.builder()
          .format(JsonOutputFormat.builder().schema(outputSchema).build())
          .build())
      .addTool(Tool.builder()
          .name("search_flights")
          .description("Search for available flights to a destination on a specific date")
          .strict(true)
          .inputSchema(toolSchema)
          .build())
      .build();

  Message response = client.messages().create(params);
  IO.println(response);
  ```

  ```php PHP
  use Anthropic\Lib\Concerns\StructuredOutputModelTrait;
  use Anthropic\Lib\Contracts\StructuredOutputModel;
  use Anthropic\Messages\ToolUseBlock;

  $client = new Client();

  class TripPlan implements StructuredOutputModel
  {
      use StructuredOutputModelTrait;

      public string $summary;
      public array $next_steps;
  }

  $message = $client->messages->create(
      maxTokens: 1024,
      messages: [
          ['role' => 'user', 'content' => 'Help me plan a trip to Paris departing May 15, 2026']
      ],
      model: 'claude-opus-5-5',
      // JSON 출력: 구조화된 응답 형식
      outputConfig: ['format' => TripPlan::class],
      // 엄격한 도구 사용: 도구 매개변수 보장
      tools: [
          [
              'name' => 'search_flights',
              'strict' => true,
              'input_schema' => [
                  'type' => 'object',
                  'properties' => [
                      'destination' => ['type' => 'string'],
                      'date' => ['type' => 'string', 'format' => 'date']
                  ],
                  'required' => ['destination', 'date'],
                  'additionalProperties' => false
              ]
          ]
      ],
  );

  // Claude는 먼저 도구를 호출하거나(tool_use) JSON으로 응답할 수 있습니다(text)
  $plan = $message->parsedOutput();
  if ($plan instanceof TripPlan) {
      echo $plan->summary, "\n";
  } elseif ($toolUse = array_find($message->content, fn($block) => $block instanceof ToolUseBlock)) {
      echo "Tool call: {$toolUse->name}(", json_encode($toolUse->input), ")\n";
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  message = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 1024,
    messages: [
      {role: "user", content: "Help me plan a trip to Paris departing May 15, 2026"}
    ],
    # JSON 출력: 구조화된 응답 형식
    output_config: {
      format: {
        type: :json_schema,
        schema: {
          type: "object",
          properties: {
            summary: {type: "string"},
            next_steps: {type: "array", items: {type: "string"}}
          },
          required: ["summary", "next_steps"],
          additionalProperties: false
        }
      }
    },
    # 엄격한 도구 사용: 도구 매개변수 보장
    tools: [
      {
        name: "search_flights",
        strict: true,
        input_schema: {
          type: "object",
          properties: {
            destination: {type: "string"},
            date: {type: "string", format: "date"}
          },
          required: ["destination", "date"],
          additionalProperties: false
        }
      }
    ]
  )
  puts message
  ```
</CodeGroup>

## 중요 고려 사항

### 문법 컴파일 및 캐싱

구조화된 출력은 컴파일된 문법 아티팩트와 함께 제약된 샘플링을 사용합니다. 이로 인해 알아두어야 할 몇 가지 성능 특성이 있습니다:

* **첫 요청 지연 시간:** 특정 스키마를 처음 사용할 때 문법이 컴파일되는 동안 추가 지연 시간(latency)이 발생합니다

* **자동 캐싱:** 컴파일된 문법은 마지막 사용 후 24시간 동안 캐시되어 후속 요청이 훨씬 빨라집니다

* **캐시 무효화:** 다음을 변경하면 캐시가 무효화됩니다:

  * JSON 스키마 구조
  * 요청의 도구 집합(구조화된 출력과 도구 사용을 모두 사용하는 경우)
  * `name` 또는 `description` 필드만 변경하는 것은 캐시를 무효화하지 않습니다

### 프롬프트 수정 및 토큰 비용

구조화된 출력을 사용할 때 Claude는 예상 출력 형식을 설명하는 추가 시스템 프롬프트를 자동으로 받습니다. 이는 다음을 의미합니다:

* 입력 토큰 수가 약간 더 높아집니다
* 주입된 프롬프트는 다른 시스템 프롬프트와 마찬가지로 토큰 비용이 발생합니다
* `output_config.format` 매개변수를 변경하면 해당 대화 스레드의 [프롬프트 캐시](https://platform.claude.com/docs/ko/build-with-claude/prompt-caching)가 무효화됩니다

### JSON Schema 제한 사항

구조화된 출력은 일부 제한 사항이 있는 표준 JSON Schema를 지원합니다. JSON 출력과 엄격한 도구 사용 모두 이러한 제한 사항을 공유합니다.

<Accordion title="지원되는 기능">
  * 모든 기본 타입: object, array, string, integer, number, boolean, null
  * `enum` (문자열, 숫자, 불리언 또는 null만 가능 - 복합 타입 불가). 대소문자 관련 주의 사항은 [유효하지 않은 출력](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#invalid-outputs)을 참조하세요.
  * `const`
  * `anyOf` 및 `allOf` (제한 사항 있음 - `$ref`가 있는 `allOf`는 지원되지 않음)
  * `$ref`, `$def` 및 `definitions` (외부 `$ref`는 지원되지 않음)
  * 지원되는 모든 타입에 대한 `default` 속성
  * `required` 및 `additionalProperties` (객체의 경우 `false`로 설정해야 함)
  * 문자열 형식: `date-time`, `time`, `date`, `duration`, `email`, `hostname`, `uri`, `ipv4`, `ipv6`, `uuid`
  * 배열 `minItems` (값 0과 1만 지원됨)
</Accordion>

<Accordion title="지원되지 않음">
  * 재귀 스키마
  * 열거형 내의 복합 타입
  * 외부 `$ref` (예: `'$ref': 'http://...'`)
  * 숫자 제약 조건 (예: `minimum`, `maximum`, `multipleOf`)
  * 문자열 제약 조건 (`minLength`, `maxLength`)
  * `minItems` 0 또는 1 이외의 배열 제약 조건
  * `false` 이외의 값으로 설정된 `additionalProperties`

  지원되지 않는 기능을 사용하면 세부 정보와 함께 400 오류를 받게 됩니다.
</Accordion>

<Accordion title="패턴 지원 (정규식)">
  **지원되는 정규식 기능:**

  * 전체 매칭(`^...$`) 및 부분 매칭
  * 수량자: `*`, `+`, `?`, 간단한 `{n,m}` 경우
  * 문자 클래스: `[]`, `.`, `\d`, `\w`, `\s`
  * 그룹: `(...)`

  **지원되지 않음:**

  * 그룹에 대한 역참조 (예: `\1`, `\2`)
  * 전방/후방 탐색 단언 (예: `(?=...)`, `(?!...)`)
  * 단어 경계: `\b`, `\B`
  * 큰 범위를 가진 복잡한 `{n,m}` 수량자

  간단한 정규식 패턴은 잘 작동합니다. 복잡한 패턴은 400 오류를 일으킬 수 있습니다.
</Accordion>

<Tip>
  SDK 헬퍼는 [API에서 지원하지 않는 제약 조건](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#json-schema-limitations)이 포함된 스키마를 변환할 수 있습니다. [SDK 변환 작동 방식](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#how-sdk-transformation-works)을 참조하세요.
</Tip>

### 속성 순서

구조화된 출력을 사용할 때 객체의 속성은 스키마에서 정의된 순서를 유지하지만, 한 가지 중요한 주의 사항이 있습니다: **필수 속성이 먼저 나타나고, 그 다음에 선택적 속성이 나타납니다**.

예를 들어, 다음 스키마가 주어지면:

```json
{
  "type": "object",
  "properties": {
    "notes": { "type": "string" },
    "name": { "type": "string" },
    "email": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}
```

출력은 속성을 다음과 같이 정렬합니다:

1. `name` (필수, 스키마 순서)
2. `email` (필수, 스키마 순서)
3. `notes` (선택, 스키마 순서)
4. `age` (선택, 스키마 순서)

이는 출력이 다음과 같이 보일 수 있음을 의미합니다:

```json
{
  "name": "John Smith",
  "email": "john@example.com",
  "notes": "Interested in enterprise plan",
  "age": 35
}
```

출력의 속성 순서가 애플리케이션에 중요한 경우, 모든 속성을 필수로 표시하거나 파싱 로직에서 이 재정렬을 고려하세요.

### 유효하지 않은 출력

구조화된 출력은 대부분의 경우 스키마 준수를 보장하지만, 출력이 스키마와 일치하지 않을 수 있는 시나리오가 있습니다:

**거부** (`stop_reason: "refusal"`)

Claude는 구조화된 출력을 사용할 때도 안전성과 유용성 속성을 유지합니다. Claude가 안전상의 이유로 요청을 거부하는 경우:

* 응답에 `stop_reason: "refusal"`이 있습니다
* 200 상태 코드를 받게 됩니다
* 생성된 토큰에 대해 요금이 청구됩니다
* 거부 메시지가 스키마 제약 조건보다 우선하기 때문에 출력이 스키마와 일치하지 않을 수 있습니다

모델의 사고 과정이나 단계별 추론을 요청하는 속성은 `reasoning_extraction` 거부로 이어질 수 있습니다. 대신 짧은 설명을 요청하세요. [추론을 사고 블록에 유지하기](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback#keep-reasoning-in-thinking-blocks)를 참조하세요.

**토큰 한도 도달** (`stop_reason: "max_tokens"`)

`max_tokens` 한도에 도달하여 응답이 잘린 경우:

* 응답에 `stop_reason: "max_tokens"`가 있습니다
* 출력이 불완전하고 스키마와 일치하지 않을 수 있습니다
* 완전한 구조화된 출력을 얻으려면 더 높은 `max_tokens` 값으로 재시도하세요

**열거형 값 대소문자**

구조화된 출력은 문자열 `enum` 및 `const` 값의 대소문자를 보장하지 않습니다: Claude는 스키마와 대소문자만 다른 값을 반환할 수 있으며, 일반적으로 공백 뒤에 오는 단어의 첫 글자에서 발생합니다. 예를 들어, 다음 스키마가 주어지면:

```json
{
  "type": "string",
  "enum": ["Conversation Topic 1", "Conversation Topic 2", "Conversation topic 3"]
}
```

출력에는 정확히 그 값이 열거형에 없더라도 `"Conversation Topic 3"`(대문자 "T")이 포함될 수 있습니다. 응답은 오류나 특별한 `stop_reason` 없이 정상적으로 완료됩니다. 이는 JSON 출력과 엄격한 도구 사용 모두에 적용됩니다. 열거형 값을 대소문자 구분 없이 비교하고, 대소문자만 다른 열거형 값은 피하세요.

### 스키마 복잡도 한도

구조화된 출력은 JSON 스키마를 Claude의 출력을 제약하는 문법으로 컴파일하여 작동합니다. 더 복잡한 스키마는 컴파일하는 데 더 오래 걸리는 더 큰 문법을 생성합니다. 과도한 컴파일 시간을 방지하기 위해 API는 여러 복잡도 한도를 적용합니다.

#### 명시적 한도

다음 한도는 `output_config.format` 또는 `strict: true`가 있는 모든 요청에 적용됩니다:

| 한도              | 값  | 설명                                                                                                                        |
| --------------- | -- | ------------------------------------------------------------------------------------------------------------------------- |
| 요청당 엄격한 도구      | 20 | `strict: true`가 있는 도구의 최대 수. 엄격하지 않은 도구는 이 한도에 포함되지 않습니다.                                                                 |
| 선택적 매개변수        | 24 | 모든 엄격한 도구 스키마와 JSON 출력 스키마에 걸친 총 선택적 매개변수 수. `required`에 나열되지 않은 각 매개변수가 이 한도에 포함됩니다.                                     |
| 유니온 타입이 있는 매개변수 | 16 | 모든 엄격한 스키마에 걸쳐 `anyOf` 또는 타입 배열(예: `"type": ["string", "null"]`)을 사용하는 총 매개변수 수. 이들은 지수적 컴파일 비용을 발생시키기 때문에 특히 비용이 많이 듭니다. |

<Note>
  이러한 한도는 단일 요청의 모든 엄격한 스키마에 걸친 합계에 적용됩니다. 예를 들어, 각각 6개의 선택적 매개변수가 있는 4개의 엄격한 도구가 있는 경우, 단일 도구가 복잡해 보이지 않더라도 24개 매개변수 한도에 도달하게 됩니다.
</Note>

#### 추가 내부 한도

앞의 표에 있는 명시적 한도 외에도 컴파일된 문법 크기에 대한 추가 내부 한도가 있습니다. 이러한 한도는 스키마 복잡도가 단일 차원으로 축소되지 않기 때문에 존재합니다: 선택적 매개변수, 유니온 타입, 중첩 객체, 도구 수와 같은 기능은 컴파일된 문법을 불균형적으로 크게 만들 수 있는 방식으로 서로 상호작용합니다.

이러한 한도를 초과하면 "Schema is too complex for compilation."이라는 메시지와 함께 400 오류를 받게 됩니다. 이러한 오류는 앞의 표에 있는 각 개별 한도가 충족되더라도 스키마의 결합된 복잡도가 효율적으로 컴파일할 수 있는 범위를 초과한다는 것을 의미합니다. 최종 안전장치로 API는 **180초의 컴파일 타임아웃**도 적용합니다. 모든 명시적 검사를 통과하지만 매우 큰 컴파일된 문법을 생성하는 스키마는 이 타임아웃에 도달할 수 있습니다.

#### 스키마 복잡도를 줄이기 위한 팁

복잡도 한도에 도달하는 경우, 다음 전략을 순서대로 시도하세요:

1. **중요한 도구만 엄격하게 표시하세요.** 도구가 많은 경우, 스키마 위반이 실제 문제를 일으키는 도구에만 사용하고, 더 간단한 도구에는 Claude의 자연스러운 준수에 의존하세요.

2. **선택적 매개변수를 줄이세요.** 가능한 경우 매개변수를 `required`로 만드세요. 각 선택적 매개변수는 문법 상태 공간의 일부를 대략 두 배로 늘립니다. 매개변수에 항상 합리적인 기본값이 있는 경우, 필수로 만들고 Claude가 해당 기본값을 명시적으로 제공하도록 하는 것을 고려하세요.

3. **중첩 구조를 단순화하세요.** 선택적 필드가 있는 깊게 중첩된 객체는 복잡도를 가중시킵니다. 가능한 경우 구조를 평탄화하세요.

4. **여러 요청으로 분할하세요.** 엄격한 도구가 많은 경우, 별도의 요청이나 하위 에이전트로 분할하는 것을 고려하세요.

유효한 스키마에서 지속적인 문제가 발생하면 스키마 정의와 함께 [지원팀에 문의](https://support.claude.com/en/articles/9015913-how-to-get-support)하세요.

## 베타에서 마이그레이션하기

`output_format` 매개변수는 `output_config.format`으로 이동했으며, 더 이상 베타 헤더가 필요하지 않습니다. `output_format` 매개변수는 지원 중단되었으며 향후 제거될 예정입니다. 그래도 사용하려면 `structured-outputs-2025-11-13` 베타 헤더를 추가하세요. 이 헤더가 없으면 API는 400 오류를 반환합니다.

Python SDK(v1.0 이상)는 `client.beta.messages.create()` 또는 `count_tokens()`에서 `output_format={...}`를 허용하지 않으며 `TypeError`를 발생시킵니다. 대신 `output_config`를 사용하세요. 업데이트된 API 형태는 [원시 JSON 스키마 사용하기](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#using-a-raw-json-schema)를 참조하세요.

## 데이터 보존

구조화된 출력을 사용할 때 프롬프트와 응답은 ZDR로 처리됩니다. 그러나 JSON 스키마 자체는 최적화 목적으로 마지막 사용 후 최대 24시간 동안 임시로 캐시됩니다. API 응답 이후에는 프롬프트나 응답 데이터가 보존되지 않습니다.

구조화된 출력은 HIPAA 적격이지만, **PHI는 JSON 스키마 정의에 포함되어서는 안 됩니다**. API는 JSON 스키마를 메시지 콘텐츠와 별도로 캐시되는 문법으로 컴파일하며, 이러한 캐시된 스키마는 프롬프트 및 응답과 동일한 PHI 보호를 받지 않습니다. 스키마 속성 이름, `enum` 값, `const` 값 또는 `pattern` 정규 표현식에 PHI를 포함하지 마세요. PHI는 HIPAA 보호 조치에 따라 보호되는 메시지 콘텐츠(프롬프트 및 응답)에만 나타나야 합니다.

모든 기능에 대한 ZDR 및 HIPAA 적격성은 [API 및 데이터 보존](https://platform.claude.com/docs/ko/manage-claude/api-and-data-retention)을 참조하세요.

## 기능 호환성

**함께 작동:**

* **[배치 처리](https://platform.claude.com/docs/ko/build-with-claude/batch-processing):** 50% 할인으로 구조화된 출력을 대규모로 처리
* **[토큰 카운팅](https://platform.claude.com/docs/ko/build-with-claude/token-counting):** 컴파일 없이 토큰 계산
* **[스트리밍](https://platform.claude.com/docs/ko/build-with-claude/streaming):** 일반 응답처럼 구조화된 출력 스트리밍
* **[결합 사용](https://platform.claude.com/docs/ko/build-with-claude/structured-outputs#using-both-features-together):** 같은 요청에서 JSON 출력(`output_config.format`)과 엄격한 도구 사용(`strict: true`)을 함께 사용

**호환되지 않음:**

* **[인용](https://platform.claude.com/docs/ko/build-with-claude/citations):** 인용은 인용 블록을 텍스트와 교차 배치해야 하며, 이는 엄격한 JSON 스키마 제약 조건과 충돌합니다. `output_config.format`과 함께 인용이 활성화되면 400 오류를 반환합니다.
* **메시지 프리필링:** JSON 출력과 호환되지 않음

<Tip>
  **문법 범위:** 문법은 Claude의 직접 출력에만 적용되며, 도구 사용 호출, 도구 결과 또는 사고 태그([사고](https://platform.claude.com/docs/ko/build-with-claude/thinking) 사용 시)에는 적용되지 않습니다. 문법 상태는 섹션 간에 재설정되어 Claude가 자유롭게 사고하면서도 최종 응답에서 구조화된 출력을 생성할 수 있습니다.
</Tip>

## 다음 단계

<CardGroup cols={2}>
  <Card title="인용" icon="book-bookmark" href="https://platform.claude.com/docs/ko/build-with-claude/citations">
    제공된 문서에 대한 질문에 답할 때 Claude가 출처를 인용하도록 하세요.
  </Card>

  <Card title="엄격한 도구 사용" icon="check" href="https://platform.claude.com/docs/ko/agents-and-tools/tool-use/strict-tool-use">
    문법 제약 샘플링으로 Claude의 도구 입력에 JSON Schema 준수를 적용하세요.
  </Card>

  <Card title="Claude와 함께하는 도구 사용" icon="wrench" href="https://platform.claude.com/docs/ko/agents-and-tools/tool-use/overview">
    Claude를 외부 도구 및 API에 연결하세요. 도구가 실행되는 위치와 에이전트 루프가 작동하는 방식을 알아보세요.
  </Card>

  <Card title="가격" icon="calculator" href="https://platform.claude.com/docs/ko/about-claude/pricing">
    모델 및 기능에 대한 Anthropic의 가격 구조에 대해 알아보세요.
  </Card>
</CardGroup>
