---
title: 스트리밍 거부 처리하기
url: https://platform.claude.com/docs/ko/test-and-evaluate/strengthen-guardrails/handle-streaming-refusals
description: 스트리밍 응답에서 거부 중단 사유를 감지하고 처리하며, 거부된 요청을 폴백 모델에서 재시도합니다.
featureMetadata:
  supportedModels:
    - claude-fable-5-2
    - claude-mythos-5-2
    - 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-sonnet-5-5
    - claude-sonnet-5
    - claude-haiku-5-5
    - claude-haiku-4-5-20251001
    - claude-opus-4-8
    - claude-opus-4-7
    - claude-opus-4-6
    - claude-sonnet-4-6
    - claude-opus-4-5-20251101
    - claude-sonnet-4-5-20250929
  supportedPlatforms:
    Claude API: stable
    Claude Platform on AWS: stable
    Amazon Bedrock: stable
    Amazon Bedrock (InvokeModel): stable
    Claude Models on Google Cloud (Google managed): stable
    Microsoft Foundry: stable
---

**API 지원**: 제품별로 사용법이 다릅니다.

* **Messages API**: 스트림은 `stop_reason` `"refusal"`로 끝나며, `stop_details`(`category` 및 `explanation`)는 `message_delta` 이벤트로 전달됩니다.
* **Managed Agents**: 거부된 턴은 종료되고 세션은 유휴 상태가 됩니다. `stop_details.type` 또는 `stop_reason.type`이 `"refusal"`이면 해당 턴을 거부된 것으로 처리하세요.

Claude 4 모델부터, Claude API의 “streaming”(스트리밍) 응답은 스트리밍 분류기가 잠재적인 정책 위반을 처리하기 위해 개입할 때 **`stop_reason`: `"refusal"`**&#xC744; 반환합니다. 이 안전 기능은 실시간 스트리밍 중 콘텐츠 규정 준수를 유지하는 데 도움이 됩니다.

<Tip>
  이 페이지는 스트리밍 응답에서 거부가 어떻게 나타나는지를 다룹니다. 모든 `stop_reason` 값과 그 처리 방법은 [중단 사유와 폴백](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons)을 참조하세요. 거부된 요청을 다른 Claude 모델에서 재시도하려면 [거부와 폴백](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback)을 참조하세요.
</Tip>

## API 응답 형식

스트리밍 분류기가 Anthropic의 정책을 위반하는 콘텐츠를 감지하면, API는 다음 응답을 반환합니다:

```json
{
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Hello.."
    }
  ],
  "stop_reason": "refusal",
  "stop_details": {
    "type": "refusal",
    "category": "cyber",
    "explanation": "This request was declined because it could enable cyber harm."
  }
}
```

이벤트 스트림에서 `stop_details`는 `stop_reason`과 함께 `message_delta` 이벤트에 도착합니다.

<Note>
  스트리밍 분류기의 `refusal` 응답에는 `category`와 사용자에게 표시할 수 있는 사람이 읽을 수 있는 `explanation`이 포함된 `stop_details` 객체가 있습니다. 전체 응답 형태와 사용 가능한 카테고리는 [거부의 형태](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback#refusal-response)를 참조하세요.

  거부 시 `stop_details` 객체는 항상 존재하지만, 예를 들어 거부가 명명된 카테고리에 매핑되지 않는 경우처럼 `category` 및 `explanation` 필드는 `null`일 수 있습니다. `category`와 `explanation`이 채워져 있다고 가정하지 말고 `stop_reason` 또는 `stop_details.type`을 기준으로 분기하세요. 그리고 이 값들이 `null`일 때는 자체적인 사용자 대상 메시지를 제공하세요.
</Note>

## 거부 후 컨텍스트 재설정

**`stop_reason`: `refusal`**&#xC744; 받으면, 계속하기 전에 반드시 대화 컨텍스트를 재설정해야 합니다. 거부를 유발한 턴을 제거하거나 다시 표현할 수 있으며, 대화 기록을 완전히 지울 수도 있습니다. 재설정 없이 계속하려고 하면 거부가 계속 발생합니다.

<Note>
  응답이 거부된 경우에도 사용량 지표는 여전히 응답에 제공됩니다. 거부된 요청에 요금이 청구되는지 여부는 거부가 발생한 시점과 해당 카테고리에 따라 달라집니다. [거부에 대한 요금 부과 방식](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback#how-refusals-are-billed)을 참조하세요.
</Note>

<Tip>
  컨텍스트 재설정이 복구하는 유일한 방법은 아닙니다. 거부된 요청을 다른 Claude 모델에서 재시도할 수도 있으며, [거부와 폴백](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback) 페이지에서 서버 측 폴백, SDK 미들웨어 또는 수동 재시도로 이를 설정하는 방법을 보여줍니다.
</Tip>

## 구현 가이드

애플리케이션에서 스트리밍 거부를 감지하고 처리하는 방법은 다음과 같습니다:

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    스트림에서 거부를 감지하고 대화를 재설정하려면 단일 셸 명령보다 더 많은 로직이 필요합니다. 실행하려면 [Claude SDK](https://platform.claude.com/docs/ko/cli-sdks-libraries/overview)를 사용하세요.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    스트림에서 거부를 감지하고 대화를 재설정하려면 단일 셸 명령보다 더 많은 로직이 필요합니다. 실행하려면 [Claude SDK](https://platform.claude.com/docs/ko/cli-sdks-libraries/overview)를 사용하세요.
  </CodeGroupItem>

  ```python Python
  client = anthropic.Anthropic()
  messages = []


  def reset_conversation():
      """Reset conversation context after refusal"""
      global messages
      messages = []
      print("Conversation reset due to refusal")


  try:
      with client.messages.stream(
          max_tokens=1024,
          messages=messages + [{"role": "user", "content": "Hello"}],
          model="claude-opus-5-5",
      ) as stream:
          for event in stream:
              # 메시지 델타에서 거부 여부를 확인합니다
              if event.type == "message_delta":
                  if event.delta.stop_reason == "refusal":
                      reset_conversation()
                      break
  except Exception as e:
      print(f"Error: {e}")
  ```

  ```typescript TypeScript
  const client = new Anthropic();
  let messages: Anthropic.MessageParam[] = [];

  function resetConversation() {
    // 거부 후 대화 컨텍스트를 초기화합니다
    messages = [];
    console.log("Conversation reset due to refusal");
  }

  try {
    const stream = await client.messages.stream({
      messages: [...messages, { role: "user", content: "Hello" }],
      model: "claude-opus-5-5",
      max_tokens: 1024
    });

    for await (const event of stream) {
      // 메시지 델타에서 거부 여부를 확인합니다
      if (event.type === "message_delta" && event.delta.stop_reason === "refusal") {
        resetConversation();
        break;
      }
    }
  } catch (error) {
    console.error("Error:", error);
  }
  ```

  ```csharp C#
  List<Message> messages = new();
  AnthropicClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new UserMessageParam { Content = "Hello" }]
  };

  try
  {
      await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
      {
          if (
              streamEvent.TryPickDelta(out var deltaEvent)
              && deltaEvent.Delta.StopReason == StopReason.Refusal
          )
          {
              ResetConversation();
              break;
          }
      }
  }
  catch (Exception e)
  {
      Console.WriteLine($"Error: {e.Message}");
  }

  void ResetConversation()
  {
      messages.Clear();
      Console.WriteLine("Conversation reset due to refusal");
  }
  ```

  ```go Go
  var messages []anthropic.MessageParam

  func resetConversation() {
  	messages = []anthropic.MessageParam{}
  	fmt.Println("Conversation reset due to refusal")
  }
  // ...
  	client := anthropic.NewClient()

  	stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 1024,
  		Messages: []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
  		},
  	})

  streamLoop:
  	for stream.Next() {
  		event := stream.Current()
  		switch eventVariant := event.AsAny().(type) {
  		case anthropic.MessageDeltaEvent:
  			if eventVariant.Delta.StopReason == anthropic.StopReasonRefusal {
  				resetConversation()
  				break streamLoop
  			}
  		}
  	}

  	if err := stream.Err(); err != nil {
  		log.Fatal(err)
  	}
  ```

  ```java Java
  import com.anthropic.core.http.StreamResponse;
  import com.anthropic.models.messages.RawMessageStreamEvent;
  import com.anthropic.models.messages.StopReason;
  // ...

  List<MessageParam> messages = new ArrayList<>();

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

      MessageCreateParams params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("Hello")
          .build();

      try (StreamResponse<RawMessageStreamEvent> stream = client.messages().createStreaming(params)) {
          stream.stream().forEach(event -> {
              event.messageDelta().ifPresent(deltaEvent -> {
                  deltaEvent.delta().stopReason().ifPresent(stopReason -> {
                      if (stopReason.equals(StopReason.REFUSAL)) {
                          resetConversation();
                      }
                  });
              });
          });
      } catch (Exception e) {
          System.err.println("Error: " + e.getMessage());
      }
  }

  void resetConversation() {
      messages.clear();
      IO.println("Conversation reset due to refusal");
  }
  ```

  ```php PHP
  $client = new Client();
  $messages = [];

  function resetConversation(&$messages)
  {
      $messages = [];
      echo "Conversation reset due to refusal\n";
  }

  try {
      $stream = $client->messages->createStream(
          maxTokens: 1024,
          messages: [
              ['role' => 'user', 'content' => 'Hello']
          ],
          model: 'claude-opus-5-5',
      );

      foreach ($stream as $event) {
          if ($event->type === 'message_delta' && $event->delta->stopReason === 'refusal') {
              resetConversation($messages);
              break;
          }
      }
  } catch (\Exception $e) {
      echo 'Error: ' . $e->getMessage() . "\n";
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new
  messages = []

  def reset_conversation(messages)
    messages.clear
    puts "Conversation reset due to refusal"
  end

  begin
    stream = client.messages.stream(
      model: :"claude-opus-5-5",
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello" }]
    )

    stream.each do |event|
      if event.type == :message_delta && event.delta.stop_reason == :refusal
        reset_conversation(messages)
        break
      end
    end
  rescue => e
    puts "Error: #{e.message}"
  end
  ```
</CodeGroup>

## 현재 거부 유형

API는 현재 세 가지 방식으로 거부를 처리합니다:

| 거부 유형           | 응답 형식                        | 발생 시점                 |
| --------------- | ---------------------------- | --------------------- |
| 스트리밍 분류기 거부     | **`stop_reason`: `refusal`** | 스트리밍 중 콘텐츠가 정책을 위반할 때 |
| API 입력 및 저작권 검증 | 400 오류 코드                    | 입력이 검증 검사를 통과하지 못할 때  |
| 모델 생성 거부        | 표준 텍스트 응답                    | 모델 자체가 거부할 때          |

## 모범 사례

* **거부 모니터링:** 오류 처리에 **`stop_reason`: `refusal`** 검사를 포함하세요.
* **자동 재설정:** 거부가 감지되면 컨텍스트를 자동으로 재설정하도록 구현하세요.
* **다른 모델로 대체:** 거부된 요청이 사용자에게 거부로 표시되는 대신 다른 Claude 모델에서 재시도되도록 [서버 측 대체 또는 SDK 미들웨어](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback)를 구성하세요. Claude Haiku 5.5에는 서버 측 대체가 없으므로 클라이언트에서 재시도를 설정하세요.
* **수동 재시도 시 대체 크레딧 사용:** 재시도를 직접 구현하는 경우, 재시도 시 프롬프트 캐시 비용을 두 번 지불하지 않도록 거부의 [대체 크레딧](https://platform.claude.com/docs/ko/build-with-claude/fallback-credit) 토큰을 전달하세요. Claude Haiku 5.5 거부에는 대체 크레딧이 없습니다.
* **사용자 지정 메시지 제공:** 거부가 발생할 때 더 나은 UX를 위해 사용자 친화적인 메시지를 작성하세요.
* **거부 패턴 추적:** 거부 빈도를 모니터링하여 프롬프트의 잠재적인 문제를 파악하세요.

## 마이그레이션 참고 사항

이 기능이 처음 출시되었을 때 거부 처리를 구축했거나 기존 통합에 추가하는 경우, 다음 사항을 확인하세요:

* **거부는 오류가 아니라 응답입니다.** 거부는 `stop_reason`: `"refusal"`이 포함된 성공적인 HTTP 200 응답으로 도착하므로, 오류율만을 기반으로 구축된 모니터링에서는 드러나지 않습니다. 거부를 별도의 신호로 추적하세요.
* **거부에는 구조화된 세부 정보가 포함됩니다.** 모든 모델에서 거부에는 거절의 근거가 된 정책 카테고리를 식별하는 `stop_details` 객체도 포함됩니다. 전체 응답 형태는 [거부의 형태](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback#refusal-response)를 참조하세요.
* **다른 모델에서 재시도하세요.** 거부된 요청을 동일한 모델에 다시 보내면 대개 또 다른 거부가 발생합니다. 컨텍스트를 재설정하는 것에 그치지 말고 [서버 측 폴백, SDK 미들웨어 또는 수동 재시도](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback)를 사용하여 폴백 모델에서 재시도하고, 재시도를 직접 구현하는 경우 [폴백 크레딧](https://platform.claude.com/docs/ko/build-with-claude/fallback-credit)을 사용하세요. Claude Haiku 5.5에는 서버 측 폴백이 없고 그 거부에는 폴백 크레딧이 포함되지 않으므로 클라이언트에서 재시도를 설정하세요.
* **배치 결과에서 거부를 확인하세요.** [메시지 배치](https://platform.claude.com/docs/ko/build-with-claude/batch-processing)에서 거부된 요청은 오류 결과가 아니라 `stop_reason`: `"refusal"`이 포함된 성공 결과로 반환됩니다.
* **`stop_reason`을 중심으로 처리를 일원화하세요.** API는 계속해서 `stop_reason`: `"refusal"`을 중심으로 거부 처리를 통합하고 있으므로, 모델별 동작이 아닌 중단 사유를 기준으로 분기하세요.

## 다음 단계

<CardGroup cols={2}>
  <Card title="거부와 폴백" icon="arrows-clockwise" href="https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback">
    거부된 요청을 서버 측 또는 클라이언트에서 다른 Claude 모델로 재시도합니다.
  </Card>

  <Card title="중단 사유와 폴백" icon="code" href="https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons">
    모든 `stop_reason` 값과 그 처리 방법.
  </Card>

  <Card title="메시지 스트리밍" icon="lightning" href="https://platform.claude.com/docs/ko/build-with-claude/streaming">
    응답을 스트리밍하고 `message_delta` 이벤트가 도착하는 대로 `stop_reason`을 읽습니다.
  </Card>

  <Card title="다국어 지원" icon="text-aa" href="https://platform.claude.com/docs/ko/build-with-claude/multilingual-support">
    Claude의 교차 언어 기능으로 다양한 언어의 사용자에게 서비스를 제공합니다.
  </Card>
</CardGroup>

## 제공 여부

스트리밍 거부은(는) API에 따라 사용 가능 또는 베타 상태입니다.

### API 지원

#### [Messages API](https://platform.claude.com/docs/ko/build-with-claude/working-with-messages)

* 제공 여부: 항상 활성화
* 사용법: 스트림은 `stop_reason` `"refusal"`로 끝나며, `stop_details`(`category` 및 `explanation`)는 `message_delta` 이벤트로 전달됩니다.

| 상태    | 클라우드 플랫폼                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 사용 가능 | Claude API, [Claude Platform on AWS](https://platform.claude.com/docs/ko/build-with-claude/claude-platform-on-aws), [Amazon Bedrock](https://platform.claude.com/docs/ko/build-with-claude/claude-in-amazon-bedrock), [Amazon Bedrock (InvokeModel)](https://platform.claude.com/docs/ko/build-with-claude/claude-on-amazon-bedrock-legacy), [Claude Models on Google Cloud (Google managed)](https://platform.claude.com/docs/ko/build-with-claude/claude-models-on-google-cloud/google-managed), [Microsoft Foundry](https://platform.claude.com/docs/ko/build-with-claude/claude-in-microsoft-foundry) |

#### [Managed Agents](https://platform.claude.com/docs/ko/managed-agents/overview)

* 제공 여부: 항상 활성화
* 사용법: 거부된 턴은 종료되고 세션은 유휴 상태가 됩니다. `session.status_idle` 이벤트의 `stop_details.type` 또는 `stop_reason.type`이 `"refusal"`이면 해당 턴을 거부된 것으로 처리하세요. 거부된 턴에서 `stop_reason.type`은 `"end_turn"`일 수 있습니다. `stop_details`는 다른 모든 턴에서 `null`이며, 거부된 턴에서도 `null`일 수 있습니다.

| 상태 | 클라우드 플랫폼                                                                                                           |
| -- | ------------------------------------------------------------------------------------------------------------------ |
| 베타 | Claude API, [Claude Platform on AWS](https://platform.claude.com/docs/ko/build-with-claude/claude-platform-on-aws) |
