Vercel AI SDK로 스트리밍 챗봇 구현하기
AI 챗봇을 만들 때 가장 중요한 사용자 경험 중 하나는 실시간 스트리밍입니다. 응답 전체가 오기를 기다리는 대신, 글자가 하나씩 나타나는 방식은 체감 속도를 크게 향상시킵니다. Vercel AI SDK는 이를 쉽게 구현할 수 있도록 설계된 TypeScript 기반의 오픈소스 라이브러리입니다.
이 글에서는 Next.js App Router + Vercel AI SDK를 활용해 실시간 스트리밍 챗봇을 처음부터 끝까지 구현하는 방법을 살펴봅니다.
Vercel AI SDK란?
Vercel AI SDK는 OpenAI, Google Gemini, Anthropic Claude 등 다양한 AI 모델 제공업체와의 통합을 추상화해주는 라이브러리입니다. 핵심 기능은 두 가지입니다.
- AI SDK Core: 서버 사이드에서 LLM 호출 및 스트리밍 처리 (
streamText,generateText등) - AI SDK UI: 클라이언트 사이드에서 채팅 상태 관리 (
useChat,useCompletion훅)
주요 패키지 비교
| 패키지 | 역할 | 사용 위치 |
|---|---|---|
| `ai` | Core SDK - streamText, generateText | 서버 (API Route) |
| `@ai-sdk/react` | useChat, useCompletion 훅 | 클라이언트 (React) |
| `@ai-sdk/openai` | OpenAI 모델 어댑터 | 서버 |
| `@ai-sdk/google` | Google Gemini 어댑터 | 서버 |
| `@ai-sdk/anthropic` | Anthropic Claude 어댑터 | 서버 |
1단계: 프로젝트 설정
npx create-next-app@latest my-ai-chatbot --typescript --app
cd my-ai-chatbot
npm install ai @ai-sdk/react @ai-sdk/openai zod
.env.local 파일에 API 키를 추가합니다.
OPENAI_API_KEY=sk-your-openai-api-key
2단계: API 라우트 생성 (서버 사이드)
app/api/chat/route.ts 파일을 생성합니다. 이 라우트는 클라이언트로부터 메시지를 받아 AI 모델에 전달하고 스트리밍 응답을 반환합니다.
// app/api/chat/route.ts
import { streamText, convertToModelMessages } from "ai";
import { openai } from "@ai-sdk/openai";
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o-mini"),
system: "당신은 친절하고 유능한 AI 어시스턴트입니다. 한국어로 답변해 주세요.",
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}
핵심 포인트:
streamText: LLM 응답을 청크 단위로 스트리밍convertToModelMessages: useChat 형식의 메시지를 모델용으로 변환toUIMessageStreamResponse(): React 클라이언트와 호환되는 스트림 응답 포맷maxDuration = 30: Vercel 서버리스 함수의 최대 실행 시간 (스트리밍 필수)
3단계: 채팅 UI 구현 (클라이언트 사이드)
// app/page.tsx
"use client";
import { useChat } from "@ai-sdk/react";
import { useEffect, useRef } from "react";
export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error } =
useChat({ api: "/api/chat" });
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">AI 챗봇</h1>
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.map((message) => (
<div key={message.id} className={message.role === "user" ? "text-right" : "text-left"}>
<span className={message.role === "user"
? "bg-blue-500 text-white rounded-lg px-4 py-2 inline-block"
: "bg-gray-100 rounded-lg px-4 py-2 inline-block"}>
{typeof message.content === "string" ? message.content : null}
</span>
</div>
))}
{isLoading && (
<div className="text-left">
<span className="bg-gray-100 rounded-lg px-4 py-2 inline-block animate-pulse">
답변 생성 중...
</span>
</div>
)}
{error && <div className="text-red-500">오류: {error.message}</div>}
<div ref={bottomRef} />
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="메시지를 입력하세요..."
disabled={isLoading}
className="flex-1 border rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="bg-blue-500 text-white rounded-lg px-6 py-2 disabled:opacity-50">
전송
</button>
</form>
</div>
);
}
4단계: 고급 기능 추가
smoothStream으로 부드러운 스트리밍
import { streamText, smoothStream } from "ai";
const result = streamText({
model: openai("gpt-4o-mini"),
messages: convertToModelMessages(messages),
experimental_transform: smoothStream({ delayInMs: 20 }),
});
smoothStream은 청크 단위의 텍스트 출력을 더 자연스럽게 만들어 사용자 경험을 향상시킵니다.
도구(Tool) 호출 추가
import { streamText, tool } from "ai";
import { z } from "zod";
const result = streamText({
model: openai("gpt-4o"),
messages: convertToModelMessages(messages),
tools: {
getCurrentTime: tool({
description: "현재 시간을 반환합니다",
parameters: z.object({}),
execute: async () => new Date().toLocaleString("ko-KR"),
}),
},
});
도구 호출을 통해 챗봇이 외부 데이터나 실시간 정보에 접근할 수 있습니다.
AI SDK 버전별 주요 변경점
| 버전 | 주요 변경사항 |
|---|---|
| v3.x | 기본 streamText, useChat 도입 |
| v4.x | toUIMessageStreamResponse() 추가, RSC 지원 강화 |
| v5.x | convertToModelMessages() 도입, 에이전트 추상화 계층 |
| v6.x | 도구 실행 승인, 개발자 도구, 에이전트 중심 설계 |
베스트 프랙티스 요약
서버 사이드:
- API 키는 반드시 서버 사이드에서만 사용 (
.env.local,process.env) maxDuration설정으로 스트리밍 타임아웃 방지- try/catch로 에러 처리 및 클라이언트 친화적 메시지 반환
클라이언트 사이드:
isLoading상태로 중복 요청 방지useEffect+ ref로 자동 스크롤 구현- 에러 상태 UI 처리 필수
성능 최적화:
- 개발 환경엔 gpt-4o-mini, 프로덕션엔 gpt-4o 같은 모델 분리 전략
smoothStream으로 체감 응답 속도 개선- Rate limiting과 인증 미들웨어 적용
코드벤터는 Vercel AI SDK와 같은 최신 AI 개발 도구를 적극 활용해 빠르고 실용적인 제품을 만드는 것을 지향합니다. 복잡한 AI 인프라를 추상화하고 비즈니스 가치에 집중하는 것, 그것이 코드벤터와 함께하는 개발의 방향입니다. 스트리밍 챗봇 구현에 관해 궁금한 점이 있다면 언제든지 코드픽 커뮤니티에서 질문해 주세요.