버퍼링이 필요한 이유
LLM은 토큰 단위로 출력해요. 마크다운
코드블록 시작은 보냈는데 닫히는 이 아직 안 온 상태에서 react-markdown에 던지면 페이지 끝까지 코드블록이 펼쳐져요. 표 |---| 도 마찬가지예요. 따라서 incomplete syntax는 버퍼에 보관하고 "확실히 닫힌 마크다운 단위까지"만 렌더하는 streaming-safe parser가 필요합니다.Vercel AI SDK 골격
// app/api/chat/route.ts import { streamText } 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-6'), messages, }) return result.toDataStreamResponse() } // app/chat/page.tsx 'use client' import { useChat } from 'ai/react' export default function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat() return ( <div> {messages.map(m => <div key={m.id}>{m.content}</div>)} <form onSubmit={handleSubmit}> <input value={input} onChange={handleInputChange} /> </form> </div> ) }