k
korAI
중급 전체
중급2026-07-157분

Streaming과 토큰 관리로 UX와 비용을 동시에 잡는 법

Anthropic SDK의 스트리밍 API를 활용하면 긴 응답도 첫 글자부터 즉시 표시해 체감 속도를 높일 수 있습니다. max_tokens 제어와 조합하면 응답 품질을 유지하면서 API 비용도 예측 가능하게 관리됩니다.

streamingtoken-managementapi

Streaming이 필요한 상황

챗봇, 코드 생성기, 긴 보고서 작성 도구처럼 응답이 수 초 이상 걸리는 기능에서 사용자는 빈 화면을 기다리는 것을 싫어합니다. 스트리밍은 모델이 토큰을 생성하는 즉시 클라이언트에 전달해 첫 번째 토큰까지의 지연(TTFT) 만큼만 기다리면 됩니다.

max_tokens로 비용 예측하기

토큰 관리의 핵심 원칙은 다음과 같습니다.

| 설정 | 역할 | 권장값 | |---|---|---| | max_tokens | 출력 상한 강제 | 용도별로 명시 | | temperature | 창의성 vs 일관성 | 0.0~0.3(사실형), 0.7~1.0(창작형) |

max_tokens를 명시하지 않으면 모델이 필요 이상으로 길게 응답해 비용이 튀는 경우가 있습니다. 응답 유형별 상한을 미리 정의해두는 것이 좋습니다.

import anthropic

client = anthropic.Anthropic()

# 응답 유형별 토큰 예산 정의
TOKEN_BUDGET = {
    "summary": 256,
    "code_review": 1024,
    "full_report": 4096,
}

def stream_response(
    prompt: str,
    response_type: str = "summary",
    temperature: float = 0.3,
) -> str:
    """
    스트리밍으로 응답을 출력하고, 완성된 전체 텍스트를 반환합니다.
    """
    max_tokens = TOKEN_BUDGET.get(response_type, 512)
    full_text = ""

    print(f"[{response_type} | max_tokens={max_tokens} | temp={temperature}]\n")

    # stream 컨텍스트 매니저 사용
    with client.messages.stream(
        model="claude-sonnet-4-6",
        max_tokens=max_tokens,
        temperature=temperature,
        messages=[{"role": "user", "content": prompt}],
    ) as stream:
        for text_chunk in stream.text_stream:
            print(text_chunk, end="", flush=True)  # 실시간 출력
            full_text += text_chunk

    # 스트림 종료 후 사용량 확인
    final_message = stream.get_final_message()
    usage = final_message.usage
    print(f"\n\n--- 사용량 ---")
    print(f"입력 토큰: {usage.input_tokens}")
    print(f"출력 토큰: {usage.output_tokens}")
    print(f"출력 토큰 한도 대비: {usage.output_tokens}/{max_tokens} ({usage.output_tokens/max_tokens*100:.1f}%)")

    return full_text


if __name__ == "__main__":
    result = stream_response(
        prompt="Python의 GIL(Global Interpreter Lock)이 멀티스레딩 성능에 미치는 영향을 설명해줘.",
        response_type="summary",
        temperature=0.2,
    )

스트리밍 + 에러 핸들링 패턴

스트림 도중 네트워크가 끊기거나 max_tokens에 도달해 응답이 잘릴 수 있습니다. stop_reason을 확인해 불완전한 응답을 감지하세요.

final = stream.get_final_message()
if final.stop_reason == "max_tokens":
    print("\n⚠️  응답이 토큰 한도로 잘렸습니다. max_tokens를 늘리거나 프롬프트를 단축하세요.")
elif final.stop_reason == "end_turn":
    print("\n✅  정상 완료")

적용 체크리스트

  • [ ] 응답 유형별 max_tokens 예산을 코드에 상수로 정의했다
  • [ ] client.messages.stream() 컨텍스트 매니저를 사용해 스트림을 안전하게 닫는다
  • [ ] 스트림 종료 후 get_final_message().stop_reason을 확인한다
  • [ ] stop_reason == "max_tokens" 일 때 사용자에게 경고 또는 재시도 로직을 추가했다
  • [ ] 사실 기반 응답에는 temperature 0.0~0.3, 창작에는 0.7~1.0을 적용했다