
응답 생성 시간이 똑같아도 "빠르다"는 느낌이 완전히 달라질 수 있습니다. 고객 응대 챗봇에 스트리밍을 처음 도입했을 때, 저도 이 사실을 수치로 직접 확인하고 나서야 비로소 믿게 됐습니다.
TTFT가 UX를 바꾸는 이유
챗봇이 응답을 내놓기까지 4~6초를 기다리는 경험은 생각보다 불쾌합니다. 제가 비스트리밍 방식으로 챗봇을 운영하던 시절, 내부 사용자 피드백에서 "너무 느리다"는 불만이 반복적으로 올라왔습니다. 실제 응답 품질에는 문제가 없었는데도 말입니다.
핵심은 TTFT(Time To First Token)입니다. TTFT란 사용자가 질문을 보낸 뒤 모델이 첫 번째 토큰, 즉 첫 글자를 화면에 내보내기까지 걸리는 시간을 의미합니다. 전체 응답이 완성될 때까지 기다렸다가 한꺼번에 보여주는 방식과 달리, 스트리밍은 이 TTFT를 최소화해서 사용자가 즉각 무언가를 보고 있다는 느낌을 받게 합니다.
UX 연구 분야에서 오래전부터 확립된 기준이 있습니다. 1초 이내 응답은 사용자가 "즉각적"으로 느끼고, 10초를 넘어가면 집중력 자체가 끊긴다는 것입니다(출처: Nielsen Norman Group). 스트리밍은 전체 응답 완성 시간을 줄여주지는 않지만, 첫 반응이 1초 안에 화면에 찍히도록 만들어 사용자의 체감 대기 시간을 근본적으로 바꿔놓습니다.
제가 직접 경험한 수치가 이를 잘 보여줍니다. 스트리밍 전환 후 응답 생성 시간은 동일했지만, "빠르다"는 인식이 눈에 띄게 올라갔습니다. 같은 서버, 같은 모델, 같은 프롬프트였습니다. 달라진 건 오직 응답을 언제 보여주느냐, 그 하나였습니다.
SSE 스트림 처리에서 배운 것들
스트리밍 구현의 기술적 뼈대는 SSE(Server-Sent Events)입니다. SSE란 서버가 클라이언트에게 단방향으로 데이터를 지속적으로 밀어 보내는 HTTP 기반 프로토콜로, 웹소켓처럼 양방향 통신이 필요하지 않은 상황에서 훨씬 가볍게 쓸 수 있는 방식입니다(출처: MDN Web Docs). Anthropic API도 이 SSE 방식으로 스트림을 제공합니다.
제가 직접 Anthropic API의 SSE 스트림을 붙여보면서 몇 가지를 확실히 배웠습니다.
스트리밍 구현 시 반드시 고려해야 할 포인트는 다음과 같습니다.
- 재연결 로직: 스트림 도중 연결이 끊기는 경우는 실제로 발생합니다. 클라이언트가 자동으로 재연결을 시도하는 로직이 없으면 사용자 화면이 중간에 멈춰버립니다.
- 마크다운 렌더링 타이밍: 토큰이 하나씩 쌓이는 과정에서 마크다운 파서가 미완성 문법을 만나면 화면이 깨집니다. 특히 코드 블록이 절반만 도착한 상태에서 렌더러를 돌리면 레이아웃이 무너지는 상황이 생깁니다.
- 버퍼링 로직: 완성된 코드 블록이 닫히는 시점을 감지해서 그때 렌더링하는 버퍼링 처리가 별도로 필요합니다.
- 모바일 환경 고려: 잦은 소켓 연결과 해제는 배터리 소모와 네트워크 비용을 높입니다. 모바일 트래픽이 많은 서비스라면 이 부분을 사전에 따져봐야 합니다.
솔직히 이 부분은 예상 밖이었습니다. 스트리밍 자체는 API 파라미터 하나 바꾸면 되는 일이지만, 클라이언트 사이드에서 토큰 단위로 화면을 업데이트하면서 동시에 마크다운을 안정적으로 렌더링하는 건 꽤 손이 가는 작업이었습니다. 특히 코드 블록 버퍼링 문제는 처음 맞닥뜨렸을 때 원인 파악에만 한참 걸렸습니다.
스트리밍이 항상 정답은 아닌 이유
스트리밍은 비용 구조를 직접 바꾸지는 않습니다. 같은 프롬프트와 같은 응답이라면 스트리밍 여부와 무관하게 청구되는 토큰 수는 동일합니다. 다만 제 경험상 실질적인 절감 효과가 하나 있었습니다. 사용자가 스트리밍 도중 원하는 답변을 발견하고 생성을 중단(cancel)할 수 있게 되면서, 실제 운용 과정에서 불필요하게 완성되는 응답이 줄어들었습니다.
그렇다고 스트리밍이 모든 상황에서 최선이라는 생각에는 동의하지 않습니다. 백엔드 파이프라인에서 정밀한 JSON 파싱이나 구조화된 데이터를 받아야 할 때는 스트리밍이 오히려 처리 복잡도를 높입니다. 스트리밍 중에는 응답 전체가 완성되기 전까지 구조가 확정되지 않기 때문입니다.
이런 경우에는 비스트리밍 방식, 즉 응답이 완성된 뒤 전체를 한 번에 받는 배치(Batch) 방식이 더 안정적입니다. 배치 방식이란 API 응답이 완전히 생성된 이후에야 클라이언트에 전달하는 방식으로, 파싱 정확도가 중요한 자동화 파이프라인에서 적합합니다.
제 경험상 이 부분은 좀 다릅니다. "스트리밍이 더 좋다"는 인식이 생기면 모든 케이스에 무조건 적용하려는 경향이 생기는데, 사용자 인터페이스에는 스트리밍, 데이터 처리 파이프라인에는 비스트리밍, 이렇게 목적에 따라 명확히 나눠서 설계하는 것이 훨씬 합리적입니다.
결국 스트리밍은 도구입니다. UX가 중요한 대화형 인터페이스에서는 사실상 기본값으로 채택해야 할 만큼 효과가 검증되어 있지만, 응답 구조의 정확성이 최우선인 백엔드 로직에서는 오히려 짐이 될 수 있습니다. 처음 챗봇에 스트리밍을 붙이던 날, "이걸 왜 이제야 했지"라고 생각했던 기억이 납니다. 지금도 대화형 서비스를 새로 설계할 때 스트리밍을 빼는 경우는 거의 없습니다. 다만 어디서 쓰고 어디서 빼야 하는지를 처음부터 구분하고 들어가는 것, 그게 제가 배운 가장 중요한 교훈이었습니다.
참고:
- Anthropic. (2024). Streaming Messages. https://docs.anthropic.com/en/api/messages-streaming
- Nielsen, J. (1993). Response Times: The 3 Important Limits. Nielsen Norman Group. https://www.nngroup.com/articles/response-times-3-important-limits/
- MDN Web Docs. Server-sent events. https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events