Claude SDK 스트리밍 응답 처리 — SSE 이벤트·실시간 출력·에러 복구

Claude 스트리밍의 SSE 이벤트 흐름(message_start→content_block_delta→message_stop), text·input_json·thinking delta 타입, SDK로 실시간 토큰 출력과 최종 메시지 수신, 세대별 에러 복구 전략을 공식 문서 기반으로 정리합니다.

Claude로 챗봇이나 긴 응답을 다루는 앱을 만들면 스트리밍이 거의 필수입니다. 응답을 한 번에 받는 대신 토큰이 생성되는 대로 받아 화면에 흘려보내면 체감 속도가 크게 좋아지고, 큰 max_tokens 요청에서 HTTP 타임아웃도 피할 수 있습니다. 이 글은 공식 문서 기준 스트리밍 이벤트 구조와 SDK 사용법을 정리합니다.

스트리밍 이벤트 흐름 (SSE) message_start → (블록 시작·delta·정지) × N → message_delta → message_stop message_start빈 content content_block_start content_block_delta × 多text_delta / input_json_delta/ thinking_delta content_block_stop message_deltausage 누적 message_stop content block 단위로 반복 (각 block에 index) ※ 중간에 ping 이벤트, 드물게 error(예: overloaded_error) 이벤트가 섞일 수 있음

스트리밍 이벤트 흐름

스트리밍은 SSE(Server-Sent Events)로 전달되며, 각 이벤트는 이름과 JSON 데이터를 가집니다. 공식 문서 기준 한 스트림의 흐름은 다음과 같습니다.

  1. message_startcontent가 빈 Message 객체로 시작.
  2. 여러 개의 content block — 각 블록은 content_block_start → 하나 이상의 content_block_deltacontent_block_stop으로 구성. 각 블록은 최종 content 배열에서의 위치를 가리키는 index를 가짐.
  3. 하나 이상의 message_delta — 최종 Message의 상위 레벨 변화(예: stop_reason) 전달. 여기 담긴 usage 토큰 수는 누적치입니다.
  4. 마지막에 message_stop.

중간에 ping 이벤트가 섞일 수 있고, 사용량이 많은 시기에는 스트림 안에서 error 이벤트(예: overloaded_error, 비스트리밍이면 HTTP 529에 해당)가 올 수 있습니다. 버전 정책상 새 이벤트 타입이 추가될 수 있으니 알 수 없는 이벤트 타입은 무시하도록 안전하게 처리해야 합니다.

delta(증분) 타입

content_block_delta는 해당 index의 블록을 갱신하는 delta를 담습니다. 주요 타입:

  • text_delta — 텍스트 조각. 예: {"type":"text_delta","text":"Hello"}
  • input_json_delta — 도구 사용(tool_use) 블록의 input 갱신. 부분 JSON 문자열로 오므로, content_block_stop까지 모은 뒤 파싱하거나 SDK 헬퍼로 점진 파싱합니다. (최종 tool_use.input은 항상 객체)
  • thinking_delta — extended thinking 스트리밍 시 사고 과정 조각. content_block_stop 직전에 무결성 검증용 signature_delta가 한 번 옵니다.
SDK 스트리밍 처리, 두 가지 방식 ① 실시간 토큰 출력 도착하는 즉시 화면에 표시 for text in stream.text_stream: print(text, end="") → 챗 UI 타이핑 효과, 체감 응답속도 향상 이벤트 직접 처리 가능 ② 최종 메시지만 받기 스트리밍은 내부적으로, 결과는 통째로 with client.messages.stream(...) as stream: msg = stream.get_final_message() → 큰 max_tokens 요청에서 HTTP 타임아웃 방지용

SDK로 스트리밍하기

공식 Python·TypeScript SDK는 여러 스트리밍 방식을 제공합니다(Python은 동기·비동기 모두). PHP SDK는 createStream()으로 스트리밍합니다. 대표적인 두 패턴:

① 실시간 토큰 출력

토큰이 도착하는 즉시 처리하려면 컨텍스트 매니저로 스트림을 열고 text_stream을 반복합니다.

client = anthropic.Anthropic()

with client.messages.stream(
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}],
    model="claude-opus-4-8",
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

② 최종 메시지만 받기

토큰을 실시간으로 처리할 필요가 없다면, 내부적으로는 스트리밍을 쓰되 완성된 Message 객체를 받을 수 있습니다. .create()와 동일한 결과를 주며, max_tokens 요청에서 HTTP 타임아웃을 피하는 용도로 특히 유용합니다.

with client.messages.stream(
    max_tokens=128000,
    messages=[{"role": "user", "content": "Write a detailed analysis..."}],
    model="claude-opus-4-8",
) as stream:
    message = stream.get_final_message()
print(message.content[0].text)

언어별로 누적 방식이 다릅니다. Python get_final_message(), TypeScript finalMessage(), Go는 루프 안에서 message.Accumulate(event), Java는 MessageAccumulator, Ruby는 .accumulated_message를 사용합니다. 직접 HTTP 연동이 아니라면 가능한 한 공식 SDK를 쓰는 것이 권장됩니다.

스트리밍 중 에러 복구

네트워크 문제·타임아웃으로 스트림이 중단되면 처음부터 다시 받지 않고 이어받을 수 있습니다. 공식 문서는 모델 세대에 따라 방법이 다르다고 명시합니다.

  • Claude 4.5 이하: 받은 부분 응답을 assistant 메시지의 시작 부분으로 넣어 이어서 요청.
  • Claude 4.6 이상: 부분 응답을 담은 user 메시지로 "중단된 지점부터 계속하라"고 지시. (예: "Your previous response was interrupted and ended with [...]. Continue from where you left off.")

주의: tool_use·thinking 블록은 부분 복구가 안 되며, 가장 최근의 text 블록부터 이어받을 수 있습니다. 가능하면 SDK의 메시지 누적·에러 처리 기능을 활용하세요.

정리

스트리밍의 핵심은 ① SSE 이벤트 흐름(start→delta→stop) 이해, ② delta 타입별 처리(text·input_json·thinking), ③ SDK로 실시간 출력 또는 최종 메시지 수신, ④ 알 수 없는 이벤트·에러 안전 처리, ⑤ 세대별 복구 전략입니다. 함께 보면 좋은 글: Claude SDK 시작하기, 에러 처리·재시도 가이드.

본 글은 Anthropic 공식 문서(platform.claude.com/docs)의 공개 정보를 바탕으로 정리했습니다. SDK API와 이벤트 형식은 버전에 따라 달라질 수 있으니 실제 구현 시 공식 문서를 확인하세요. 본 사이트는 Anthropic 공식 사이트가 아닙니다.

이어서 읽어보세요

궁금한 점이 있거나 활용법을 나누고 싶나요?

커뮤니티에서 다른 사용자들과 팁과 노하우를 나눠보세요. 더 많은 가이드도 준비되어 있어요.