스트리밍 AI 응답을 Next.js에 붙이는 건 이제 어렵지 않다. Route Handler 하나, useChat 훅 하나, AI SDK의 streamText와 toUIMessageStreamResponse()를 조합하면 토큰이 실시간으로 흘러내리는 채팅 UI가 뚝딱 나온다. dev.to에 올라온 실전 구현 가이드가 이를 잘 보여주는데, 코드 자체는 놀랄 만큼 간결하다. 문제는 그 바깥에서 시작된다.
진짜 난관은 코드가 아니라 '파이프' 안에 있다
로컬에서 완벽하게 스트리밍되던 응답이 프로덕션에선 한 덩어리로 뭉쳐서 온 경험, 해본 사람은 안다. 원인은 거의 항상 중간 어딘가에서 버퍼링하는 레이어다. gzip·brotli 압축 미들웨어는 작은 토큰 청크를 모아서 한 번에 내보내고, nginx의 proxy_buffering은 기본값이 'on'이다. 심지어 회사 망의 보안 프록시가 범인일 때도 있는데, 그건 개발자 통제 밖의 영역이다. 해법은 Cache-Control: no-transform과 X-Accel-Buffering: no 헤더를 응답에 심는 것—Vercel 같은 관리형 플랫폼은 알아서 처리하지만, 자체 서버 위에 Next.js를 올린 팀이라면 nginx 설정을 반드시 점검해야 한다.
또 하나 짚고 넘어갈 포인트가 있다. 브라우저 내장 EventSource API는 채팅에 쓸 수 없다. GET 요청만 지원하고 요청 바디를 실을 수 없기 때문이다. AI SDK가 fetch POST로 SSE 포맷을 읽어내는 이유가 여기 있다—SSE의 와이어 포맷 이점은 취하되, EventSource의 제약은 우회하는 설계다. 대신 자동 재연결 기능이 없으니, 재연결이 진짜 제품 요건이 되는 순간에는 resumable streams를 별도로 고려해야 한다.
AbortSignal은 UX이자 비용 제어다
streamText에 req.signal을 넘기는 한 줄이 단순한 에러 처리가 아닌 이유를 생각해볼 필요가 있다. 사용자가 탭을 닫거나 stop()을 누르는 순간, 그 시그널이 모델 프로바이더까지 전파돼 생성을 멈춘다—그리고 아무도 읽지 않는 토큰에 돈을 쓰는 일도 멈춘다. 반대로 consumeStream()을 쓰면 클라이언트 연결과 무관하게 생성을 끝까지 완료하고 onFinish로 결과를 퍼시스트할 수 있다. 어느 쪽이 맞는지는 기능의 성격에 따라 다르다—가벼운 대화형 채팅엔 abort-on-disconnect, 비용이 큰 장문 생성엔 consume-to-persist. 한 가지 정책을 전역으로 적용하는 게 실수다.
그런데, 스트리밍이 '동작'한다고 충분한가?
기술적으로 완벽하게 흐르는 스트림도 사용자에게 잘못된 신뢰를 심을 수 있다. 같은 dev.to에 올라온 흥미로운 회고가 있다. AI 에이전트가 스트리밍 플랫폼의 영화 추천을 마크다운 테이블로 내놨는데, 출력 앞머리엔 분명히 "지역 카탈로그는 자주 변경되므로 확인이 필요합니다"라는 면책 문구가 있었다. 그러나 저자는 테이블만 보고 행동했다. 결과적으로 8개 중 5개가 실제로 해당 플랫폼에 없는 타이틀이었다.
이 이야기가 AI 스트리밍과 무관해 보이는가? 그렇지 않다. 스트리밍 UI는 본질적으로 구조화된 출력 기계다. 토큰이 실시간으로 흘러와 테이블이 렌더링되고, 코드 블록이 채워지고, 리스트가 완성된다. 그 포맷은 내용의 신뢰도와 무관하게 '검증된 정보'처럼 보인다. 산문에 담긴 면책 문구는 구조화된 테이블의 권위를 이기지 못한다—그것이 인간의 인지 방식이다.
포맷이 신뢰를 설계한다
이 교훈은 프론트엔드 구현 차원에서 실질적인 설계 질문으로 이어진다. AI가 생성한 응답에서 어떤 정보가 '권위 컨테이너'—테이블, 번호 목록, 비교 매트릭스, 퍼센트 수치—안에 담기는가? 그 값이 실시간 소스에서 검증된 것인가, 아니면 모델의 recall에서 나온 것인가? 검증되지 않은 내용이 테이블에 들어가는 순간, 컨테이너가 내용을 업그레이드한다. 프론트엔드 개발자로서 우리가 해야 할 일은 단순히 스트림을 렌더링하는 게 아니라, 불확실한 정보가 확실해 보이는 포맷에 실리지 않도록 UI 레이어에서 제어하는 것이다.
실천 방향은 명확하다. 실시간 검증이 가능한 데이터는 API를 통해 확인하고 그 결과를 구조화해서 보여준다. 검증할 수 없는 recall 기반 정보라면 산문 형태로, 불확실성의 언어로 제공한다. 불확실한 내용을 아름답게 포맷하는 중간 옵션—그게 가장 위험한 선택이다.
전망: 스트리밍 기술보다 스트리밍 신뢰가 다음 과제다
Next.js App Router와 AI SDK의 조합은 스트리밍 구현의 기술적 허들을 사실상 제거했다. 남은 문제는 기술이 아니라 설계다—이 출력이 사용자에게 얼마나 믿을 수 있게 보이는가, 그리고 그 신뢰가 실제 정확도와 얼마나 일치하는가. 버퍼링 헤더를 설정하고 AbortSignal을 연결하는 것이 구현 완성이라면, 포맷이 만들어내는 신뢰 신호를 의도적으로 제어하는 것이 경험 완성이다. AI 스트리밍 UI를 프로덕트로 출시하는 팀이라면, 지금 이 질문을 던져야 할 타이밍이다: 우리 UI는 AI가 '아는 것'과 '모르는 것'을 다르게 보여주고 있는가?