블로그 목록
AI

Vercel AI SDK smoothStream으로 스트리밍 텍스트 부드럽게 흘리기 — 뚝뚝 끊기는 출력 잡기

챗봇을 만들어서 스트리밍을 붙여놓고 보니, 글자가 예쁘게 한 자씩 흐르는 게 아니라 문장 덩어리가 툭, 툭 튀어나왔다. 모델이 토큰을 몰아서 보낼 때가 있어서 그렇다. 특히 모델을 바꾸거나 캐시가 걸리면 청크 크기가 들쭉날쭉해져서, 어떤 답변은 스르륵 흐르고 어떤 답변은 몇 덩어리로 뚝뚝 끊긴다.

기능상 문제는 아니다. 근데 사용자 입장에서 "AI가 생각하며 답하는" 느낌이 스트리밍의 핵심인데, 덩어리로 튀면 그 느낌이 확 깨진다. 처음엔 프론트에서 버퍼링해서 직접 흘려볼까 했는데, Vercel AI SDK에 이미 smoothStream이라는 트랜스폼이 있었다.

experimental_transform에 끼우기

streamText의 experimental_transform 옵션에 smoothStream()을 넘기면 끝이다. SDK가 청크를 받아서 일정한 간격으로 잘게 쪼개 다시 흘려보낸다.

// app/api/chat/route.ts
import { streamText, smoothStream } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: anthropic('claude-sonnet-4-5'),
    messages,
    experimental_transform: smoothStream({
      delayInMs: 20,   // 청크 사이 간격
      chunking: 'word', // 단어 단위로 쪼개기
    }),
  });

  return result.toUIMessageStreamResponse();
}

chunking은 'word'(기본값)와 'line'이 있다. 단어 단위면 타이핑하듯 흐르고, 줄 단위면 코드 블록처럼 한 줄씩 뱉는 게 자연스러운 경우에 쓴다.

한국어는 정규식으로 쪼갠다

여기서 한 번 걸렸다. 기본 'word' 청킹은 공백 기준이라, 띄어쓰기가 적은 한국어는 문장이 통째로 한 덩어리로 나간다. chunking에 정규식을 직접 넘기면 글자 단위로 쪼갤 수 있다.

smoothStream({
  delayInMs: 20,
  chunking: /[가-힣]|\S+\s+/, // 한글 한 글자 또는 단어
});

한글 음절 하나하나가 흘러나와서 훨씬 부드럽다. 영어·숫자는 기존처럼 단어 단위로 묶인다.

프론트는 건드릴 게 없다

useChat을 쓰고 있다면 클라이언트 코드는 그대로다. 트랜스폼은 서버에서 스트림을 다듬는 거라, 받는 쪽은 원래 하던 대로 messages만 렌더링하면 된다.


정리하면, 스트리밍이 뚝뚝 끊겨 보이는 건 모델 탓이지 버그가 아니고, smoothStream 한 줄이면 해결된다. delayInMs로 속도를 조절하고, 한국어라면 chunking에 정규식을 넣는 것만 기억하면 된다. 체감 품질 대비 드는 노력이 거의 없어서, 사용자에게 보이는 챗봇이라면 그냥 기본으로 깔고 가도 좋다.