⚡ 중급2026-04-167분
스트리밍으로 첫 토큰 지연 3배 줄이기
사용자는 완성까지 기다리지 않는다. `stream: true`와 서버-센트 이벤트로 체감 속도 개선.
apistreaming
왜 스트리밍인가
800토큰짜리 응답은 Sonnet 기준 4~8초 걸린다. 그동안 사용자가 로딩 스피너만 보면 이탈한다. 스트리밍은 첫 토큰을 300~500ms에 받아 체감 지연을 10배 줄인다.
기본 예제
import Anthropic from "@anthropic-ai/sdk"
const client = new Anthropic()
const stream = client.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "블로그 글 800자 써줘" }],
})
for await (const event of stream) {
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
process.stdout.write(event.delta.text)
}
}
const final = await stream.finalMessage()
console.log("\n사용 토큰:", final.usage)
Next.js App Router에서 브라우저로 흘려보내기
// app/api/chat/route.ts
export async function POST(req: Request) {
const { prompt } = await req.json()
const stream = client.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: 2048,
messages: [{ role: "user", content: prompt }],
})
const encoder = new TextEncoder()
const body = new ReadableStream({
async start(controller) {
for await (const ev of stream) {
if (ev.type === "content_block_delta" && ev.delta.type === "text_delta") {
controller.enqueue(encoder.encode(ev.delta.text))
}
}
controller.close()
},
})
return new Response(body, {
headers: { "content-type": "text/plain; charset=utf-8" },
})
}
주의할 함정
- tool_use 이벤트는 JSON 조각이다. 문자열로 합친 뒤 파싱해야 함
- max_tokens 한계에 닿으면
stop_reason: "max_tokens". UI에 "...계속" 버튼 달아라 - 중간에 끊기면 usage 집계 안 됨.
finalMessage()로 확정 - 모바일 브라우저는 버퍼링으로 한꺼번에 오는 경우가 있음.
X-Accel-Buffering: no헤더 시도
체크리스트
- [ ] 첫 토큰 지연 측정해봤나 (300~500ms 목표)
- [ ] max_tokens 도달 시 UX 처리
- [ ] 취소 버튼으로 AbortController 연결
- [ ] 사용량 집계는 finalMessage에서