
Streaming Claude API: 핵심 기능 완벽 정리
Claude API 스트리밍의 작동 방식 — SSE 이벤트, 백엔드 프록시 구성, TTFT 및 비용 제어, 끊긴 스트림 복구, 그리고 프로덕션 안정성 팁까지.
Claude를 빠르게 느껴지게 하고 싶다면 스트리밍을 켜세요. 하나의 완전한 응답을 위해 15~25초를 기다리는 대신, 사용자는 보통 약 300~800ms 만에 첫 토큰을 볼 수 있습니다.
간단히 정리하면 다음과 같습니다:
- 응답이 생성되는 대로 나타나게 하고 싶다면 SSE 스트리밍을 사용합니다
- API 키를 보호하기 위해 Claude 호출은 백엔드 프록시 뒤에 둡니다
- UX와 비용을 제어하기 위해 TTFT(첫 토큰까지의 시간),
max_tokens, 그리고 연결 끊김을 주시합니다 - 스트리밍된 이벤트를 순서대로 파싱합니다:
message_start, 콘텐츠 블록 이벤트,message_delta, 그리고message_stop - 툴 JSON 조각은 콘텐츠 블록이 끝날 때까지 버퍼링합니다
- Claude는 스트림을 기본적으로 재개하지 않으므로, 연결 끊김에 대비한 계획을 세웁니다
몇 가지 눈에 띄는 수치가 있습니다:
- Haiku 4.5: 약 410ms TTFT
- Sonnet 4.6: 약 720ms TTFT
- Opus 4.7: 약 980ms TTFT
- 기본 프록시 타임아웃 30~60초는 긴 출력을 잘라낼 수 있습니다
- 더 긴 읽기 타임아웃 120~300초가 필요한 경우가 많습니다
- 배치 작업은 표준 토큰 요금보다 50% 저렴할 수 있습니다
쉽게 말하면, 스트리밍은 모델이 아니라 _전달 방식_을 바꿉니다. Claude API는 라이브 연결을 통해 작은 이벤트 청크로 텍스트를 보내고, 제 앱은 화면에서 최종 답변을 재구성합니다. 챗, 코파일럿, 어시스턴트의 경우, 이는 보통 더 나은 사용자 경험, 더 적은 유휴 타임아웃 문제, 그리고 클라이언트와 백엔드 양쪽에서 더 많은 작업을 의미합니다.
가장 중요한 것은 다음과 같습니다:
| 영역 | 제가 집중할 부분 |
|---|---|
| 속도 | 첫 토큰 시간, 프롬프트 크기, 모델 선택 |
| 설정 | stream: true, SSE 처리, 프록시 버퍼링 끄기 |
| UX | 먼저 스피너, 그다음 토큰 단위 텍스트, 그리고 Stop 버튼 |
| 비용 | 입력/출력 토큰 추적, max_tokens 설정, 죽은 세션 취소 |
| 안정성 | 재시도 가능한 오류만 재시도, 부분 텍스트 유지, 이어쓰기 프롬프트로 재시작 |
그래서 제가 이것을 설정한다면, 스트리밍을 단순한 API 플래그가 아니라 UX이자 인프라 선택으로 생각할 것입니다.

Claude API로 개발하기 - Part 4 - 응답 스트리밍

Claude API 스트리밍이 프로토콜 수준에서 작동하는 방식
스트리밍을 켠다고 해서 모델 자체가 바뀌지는 않습니다. 출력이 전달되는 방식이 바뀝니다. 하나의 완전한 응답을 기다리는 대신, Claude는 준비되는 대로 작은 청크를 보냅니다.
어떤 엔드포인트와 요청 설정이 스트리밍을 활성화하는가
스트리밍은 일반 요청과 동일한 Claude Messages API 엔드포인트인 /v1/messages를 사용합니다. 유일한 차이는 요청 본문에 "stream": true를 추가하는 것입니다 [1][3]. 이 플래그는 연결을 표준 요청-응답 흐름에서 서버가 생성되는 대로 청크를 푸시하는 **Server-Sent Events (SSE)**로 전환합니다.
공식 Python 및 TypeScript SDK에는 이벤트 파싱과 메시지 조립을 대신 처리해 주는 스트림 헬퍼가 포함되어 있습니다. 스트림이 끝나면 Python에서 .get_final_message(), TypeScript에서 .finalMessage()를 호출하여 토큰 수와 종료 사유와 함께 완성된 응답을 반환받습니다 [1][4].
이 설정들이 스트림을 시작합니다. 다음 부분은 연결을 통해 되돌아오는 이벤트 흐름입니다.
스트림 중에 어떤 이벤트가 도착하는가
스트림은 이름이 지정된 이벤트의 정해진 순서를 따릅니다. 각 이벤트는 클라이언트가 전체 응답을 올바르게 재구성하는 데 필요한 데이터를 담고 있습니다.
| 이벤트 유형 | 목적 | 핵심 데이터 |
|---|---|---|
message_start | 스트림 열기 | 메시지 ID, role, model, 그리고 input_tokens |
content_block_start | 새 콘텐츠 세그먼트 시작 | 블록 인덱스와 블록 타입(text, tool_use, 또는 thinking) |
content_block_delta | 부분 콘텐츠 전송 | 블록 인덱스와 text_delta, input_json_delta, 또는 thinking_delta |
content_block_stop | 콘텐츠 세그먼트 닫기 | 완료된 블록의 인덱스 |
message_delta | 메시지 수준 상태 업데이트 | 누적 output_tokens와 stop_reason |
message_stop | 스트림 종료 | 연결을 닫는 최종 신호 |
ping | 하트비트 | 모델 처리 중에 전송됨 |
error | 스트림 오류 보고 | 오류 유형과 메시지 |
이것이 클라이언트가 버퍼링하고 실시간으로 표시하는 데이터입니다. 일반 텍스트 출력의 경우, 이벤트가 도착하는 대로 각 text_delta를 로컬 버퍼에 추가합니다. 이렇게 전체 응답 문자열이 조금씩 합쳐집니다. 툴 호출 입력은 조금 다르게 동작합니다: input_json_delta 조각으로 도착하므로 먼저 버퍼링한 다음 content_block_stop 이후에만 파싱해야 합니다 [1][6].
Claude 스트리밍에서 APIMart가 유용한 경우

Claude 스트리밍이 멀티 모델 앱 안에 자리 잡고 있다면, APIMart는 통합 LLM API를 통해 Claude 접근과 스트리밍을 라우팅할 수 있는 하나의 창구를 제공합니다. 이는 웹과 모바일 클라이언트 전반에서 동일한 스트리밍 흐름을 원할 때 가장 큰 의미가 있습니다.
웹 및 모바일 앱에 Claude 스트리밍을 추가하는 방법
SSE, HTTP 스트리밍, WebSocket 래퍼: 무엇을 사용할 것인가
Claude가 스트리밍 이벤트를 어떻게 보내는지 이해했다면, 다음 단계는 그 이벤트를 웹과 모바일 클라이언트에 전달하는 것입니다. 핵심 질문은 간단합니다: 어떤 전송 방식이 내 앱에 가장 잘 맞는가?
| 전송 방식 | 설정 복잡도 | 브라우저 적합성 | 연결 동작 |
|---|---|---|---|
| Server-Sent Events (SSE) | 낮음 | 네이티브(EventSource/Fetch) | 단방향; 자동 재연결 |
| 일반 HTTP 스트리밍 | 중간 | ReadableStream 필요 | 내장 이벤트 구조나 재연결 없음 |
| WebSocket 래퍼 | 높음 | 라이브러리/래퍼 필요 | 양방향; 상태 유지; 일부 기업 방화벽에 차단될 수 있음 |
대부분의 브라우저 기반 앱에서는 SSE가 기본 선택지입니다. 대부분의 프록시 및 CDN과 잘 작동하고, 브라우저 지원 상황도 단순합니다.
WebSocket도 여전히 의미가 있을 수 있습니다. 앱이 이미 양방향 라이브 통신에 의존하고 있다면 잘 맞을 수 있습니다. 하지만 표준 챗 앱에서는 그 가치에 비해 관리해야 할 요소가 더 늘어나는 경우가 많습니다.
백엔드 프록시가 대체로 가장 안전한 아키텍처인 이유
전송 방식을 고른 후에는 스트림 처리를 서버 뒤에 둡니다. 다양한 모델의 API 키를 보호하기 위해 Claude 요청을 백엔드 프록시 뒤에 두세요 [9][5].
그 프록시 계층은 다음 작업에도 적합한 위치입니다:
- 시스템 프롬프트 주입
- 사용자별 속도 제한 적용
- 첫 토큰 및 마지막 토큰 타이밍 로깅
프록시 버퍼링을 멈추려면 X-Accel-Buffering: no를 설정하세요 [2][8]. 또한 클라이언트의 중단 신호를 스트림에 연결하세요. 그러면 사용자가 생성을 멈출 때, 아무도 읽지 않을 응답에 토큰을 소모하는 대신 요청이 즉시 취소됩니다.
한 가지 더 주의할 점: 기본 타임아웃 30~60초는 긴 응답을 잘라낼 수 있습니다 [9][2]. 프로덕션에서는 더 긴 생성을 위해 약 120~300초의 읽기 타임아웃을 사용하세요.
라이브 응답 중 좋은 클라이언트 측 UX는 어떤 모습인가
스트림이 보호되고 중계되면, 작업의 초점은 UI로 옮겨갑니다. 여기서 스트리밍은 매끄럽게 느껴지거나 어설프게 느껴지게 됩니다.
사용자가 프롬프트를 제출하자마자 "Thinking..." 표시나 스피너를 보여주세요. 이것이 첫 토큰 지연을 가려줍니다. 첫 content_block_delta가 도착하는 즉시 표시를 제거하고 텍스트 렌더링을 시작하세요.
그다음 도착하는 대로 각 text_delta를 추가하여 응답이 타이핑 효과처럼 나타나게 하세요. 인터페이스가 버벅이지 않도록, requestAnimationFrame으로 업데이트를 묶어서 너무 많은 리렌더링이 발생하지 않게 하세요. 자동 스크롤은 생성 중인 응답을 따라가야 하지만, 사용자가 이전 콘텐츠를 읽기 위해 위로 스크롤하면 물러나야 합니다.
Fetch 요청을 취소할 수 있도록 항상 AbortController에 연결된 "Stop" 버튼을 포함하세요. 그러면 이미 화면에 있는 텍스트를 지우지 않으면서 스트림을 깔끔하게 종료할 수 있습니다.
연결이 끊기면 부분 출력을 계속 표시하세요. 복구를 위해서는 그 부분 텍스트를 보관하고 이어쓰기 프롬프트로 재시작하세요. Claude는 끊긴 스트림을 기본적으로 재개하지 않기 때문입니다 [3][1].
프로덕션에서 지연 시간, 비용, 안정성을 관리하는 방법
스트림이 라이브 상태가 되면, 프로덕션 작업은 세 가지 제어로 귀결됩니다: 지연 시간, 지출, 그리고 복구.
스트리밍이 체감 지연 시간을 바꾸는 방식
여기서 핵심 UX 지표는 **Time to First Token (TTFT)**입니다: 첫 단어가 나타날 때까지의 시간이죠. 스트리밍 앱에서는 그 첫 번째로 보이는 토큰이 제품 전체의 느낌을 좌우합니다. 빠르게 나타나면 앱이 반응성 좋게 느껴집니다. 느릿하면 사용자가 알아챕니다.
벤치마크는 Claude 모델 간의 명확한 격차를 보여줍니다: Haiku 4.5는 약 410ms TTFT에 도달하고, Sonnet 4.6은 약 720ms, Opus 4.7은 대략 980ms를 기록합니다 [3]. 간단한 규칙은 여전히 품질 기준을 통과하는 가장 빠른 모델을 사용하는 것입니다.
프롬프트 크기도 중요합니다. 더 큰 컨텍스트 윈도우는 TTFT를 1~3초까지 밀어 올릴 수 있습니다 [5]. 따라서 시스템 프롬프트에 불필요한 지시사항, 오래된 규칙, 또는 비대해진 예시가 있다면, 이를 줄이면 앱이 훨씬 더 빠릿하게 느껴질 수 있습니다.
토큰 사용량을 추적하고 USD 비용을 제어하는 방법
스트리밍과 배치는 토큰당 비용이 동일합니다. 바뀌는 것은 출력이 도착하는 시점뿐입니다. 토큰 수는 스트림 자체에 포함됩니다: message_start 이벤트에는 usage.input_tokens가 포함되고, 끝 부분의 message_delta 이벤트에는 최종 누적 usage.output_tokens가 포함됩니다 [1][4]. 백엔드는 청구가 정확하게 유지되도록 스트림 종료 후 그 최종 사용량 데이터를 저장해야 합니다.
모든 요청에 max_tokens를 설정하세요. 이는 확실한 정지점을 제공하고 긴 생성이 비용을 밀어 올리는 것을 막아줍니다 [1][11]. 또한 서버 측에서 클라이언트 연결 끊김을 주시해야 합니다. 사용자가 떠났는데 생성이 계속 실행되고 있다면, 아무 이유 없이 여전히 토큰을 소모하고 있는 것입니다 [5][7].
라이브 출력이 필요 없는 작업의 경우, 계산이 달라집니다. 배치 요약, 오프라인 처리, 야간 리포트 생성이 좋은 예입니다. 이런 경우 Batch API는 일반 토큰 요금에 대해 50% 할인을 제공합니다 [5][10].
Claude 3.5 Sonnet은 표준 스트리밍에서 입력 토큰 100만 개당 약 $3.00, 출력 토큰 100만 개당 $15.00의 비용이 듭니다 [8]. Batch API를 사용하면 비동기 워크로드는 절반의 비용이 듭니다.
이러한 사용량 수치는 청구 및 속도 제한 모니터링에도 도움이 됩니다. 대용량 요청 관리에 대한 더 많은 전략은 AI API 비용 팁을 참고하세요.
끊긴 스트림을 방지하고 안전하게 복구하는 방법
Claude에는 서버 측 재개 기능이 없습니다 [1][3]. 스트림이 끊기면 부분 출력을 새 요청에 담아 보내고 Claude에게 중단 지점부터 이어가도록 요청하세요.
스스로 해결될 가능성이 높은 실패만 재시도하세요.
| 오류 코드 | 유형 | 조치 |
|---|---|---|
| 429 | 속도 제한 | 백오프로 재시도: 5s → 10s → 20s |
| 529 | 서버 과부하 | 30~60초 후 재시도 |
| 408 | 연결 타임아웃 | 즉시 재연결 |
| 4xx | 클라이언트 오류 | 재시도하지 않음; 요청 수정 |
그 후 마지막 단계는 어떤 스트리밍 기능이 가장 중요한지 고르는 것입니다.
결론: 어떤 Claude 스트리밍 기능이 가장 중요한가
지연 시간, 비용, 안정성을 살펴봤다면, Claude 스트리밍은 세 가지로 요약됩니다: 체감 반응성, 명확한 이벤트 신호, 그리고 견고한 오류 처리.
스트리밍이 중요한 이유는 첫 토큰 전달이 Claude를 빠르고 반응성 좋게 느껴지게 하기 때문입니다.
SSE 이벤트 흐름은 텍스트 델타, 사용량 수치, 오류 신호를 실시간으로 제공합니다 [1][3].
키를 보호하고, 버퍼링을 끄고, 연결 끊김을 처리하려면 백엔드 프록시를 사용하세요 [2][8]. 멀티 모델 앱의 경우, APIMart는 Claude 스트리밍, 로깅, 청구를 중앙화할 수 있습니다.
프로덕션에서는 빠른 TTFT, 사용량 추적, 프록시 회복력이 가장 중요합니다.
자주 묻는 질문
일반 Claude API 응답 대신 스트리밍을 언제 사용해야 하나요?
앱이 사용자를 대면하는 경우 스트리밍을 사용하세요. 토큰을 생성되는 대로 보내므로 응답이 거의 즉각적으로 느껴집니다. 그 작은 변화가 제품 전체를 더 빠르고 매끄럽게 느껴지게 할 수 있습니다.
스트리밍은 실시간 챗, 긴 답변, 툴 호출이 있는 에이전트 워크플로에 가장 적합합니다. 또한 출력이 길어지거나 토큰 한도가 높을 때 타임아웃을 피하는 데도 도움이 됩니다.
짧고 단순한 요청이나, 지연 시간보다 처리량이 더 중요한 배치 작업에는 사용하지 마세요.
Claude 스트림이 응답 도중 끊기면 어떻게 해야 하나요?
Claude 스트림이 끊기면 Server-Sent Events는 스스로 중단된 지점에서 이어가지 않습니다. 그 부분은 앱이 처리해야 합니다.
연결이 끊기면, 전체 요청을 재시도하거나 이미 저장한 부분 출력을 보여줄 수 있습니다. try/except 블록을 사용하여 APIConnectionError나 APIStatusError를 잡고, 지금까지 누적한 콘텐츠에 대한 참조를 유지하세요.
스트림을 더 매끄럽게 이어가고 싶다면, 마지막 이벤트 ID를 추적하고 그 지점부터 스트림을 수동으로 재생하세요.
UX를 해치지 않으면서 스트리밍 비용을 어떻게 줄일 수 있나요?
프롬프트 튜닝과 효율적인 세션 처리에 집중하세요. max_tokens 하드 한도를 설정하고, 프롬프트를 짧게 유지하며, 사용자가 필요한 것을 얻으면 생성을 멈출 수 있도록 조기 취소 옵션을 추가하세요.
즉각적인 상호작용이 필요 없는 배치 워크로드의 경우, 비스트리밍 모드를 사용하세요. 정확한 비용 추적을 위해서는 중간 스트림 청크로 추정하는 대신 최종 message_stop 이벤트를 기다리세요.
모델 마켓에서 원하는 모델을 선택하세요
APIMart 모델 마켓에서 채팅, 이미지, 비디오 모델을 사용해 보고 하나의 통합 API로 모델 기능을 빠르게 경험하세요.