Vercel AI SDK로 스트리밍 챗봇 구현하기
AI 챗봇을 만들 때 가장 중요한 사용자 경험 중 하나는 실시간 스트리밍 응답입니다. ChatGPT처럼 텍스트가 한 글자씩 흘러나오는 방식은 응답 완료를 기다리는 지루함을 없애고, 실제로 AI가 생각하고 있다는 느낌을 줍니다. Vercel AI SDK는 이 복잡한 스트리밍 로직을 몇 줄의 코드로 해결해줍니다.
Vercel AI SDK란?
Vercel AI SDK는 TypeScript 기반의 오픈소스 툴킷으로, LLM(Large Language Model) 연동과 스트리밍 응답 처리를 추상화해줍니다. OpenAI, Google Gemini, Anthropic Claude 등 주요 AI 공급자를 단일 인터페이스로 지원하며, React/Next.js와 긴밀하게 통합됩니다.
주요 패키지 구성
| 패키지 | 역할 | 주요 기능 |
|---|---|---|
| `ai` | 코어 SDK | streamText, generateText, tool 정의 |
| `@ai-sdk/react` | React 훅 | useChat, useCompletion, useAssistant |
| `@ai-sdk/openai` | OpenAI 어댑터 | GPT-4o, o1 등 모델 연결 |
| `@ai-sdk/google` | Google 어댑터 | Gemini Pro, Flash 등 모델 연결 |
| `@ai-sdk/anthropic` | Anthropic 어댑터 | Claude 3.5 등 모델 연결 |
프로젝트 설정
1단계: Next.js 프로젝트 생성 및 패키지 설치
npx create-next-app@latest ai-chatbot --typescript --app --eslint
cd ai-chatbot
npm install ai @ai-sdk/react @ai-sdk/openai
2단계: 환경 변수 설정
프로젝트 루트에 .env.local 파일을 생성합니다.
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
이 파일은 절대 Git에 커밋하지 마세요.
.gitignore에.env.local이 포함되어 있는지 확인하세요.
백엔드: API Route Handler 구현
Next.js App Router 기반의 스트리밍 API 라우트를 작성합니다.
// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
export const runtime = 'edge';
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: openai('gpt-4o'),
system: '당신은 친절하고 유능한 AI 어시스턴트입니다. 한국어로 답변하세요.',
messages,
});
return result.toDataStreamResponse();
}
streamText는 LLM의 응답을 청크 단위로 스트리밍하며, toDataStreamResponse()는 이를 클라이언트 측 useChat 훅이 이해할 수 있는 형식으로 변환합니다. runtime = edge를 설정하면 Vercel Edge Network에서 실행되어 더 빠른 응답을 기대할 수 있습니다.
프론트엔드: useChat 훅으로 UI 구성
useChat 훅은 채팅 상태 관리, 메시지 전송, 스트리밍 응답 수신을 모두 처리합니다.
// app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
export default function ChatPage() {
const {
messages,
input,
handleInputChange,
handleSubmit,
isLoading,
error,
reload,
stop,
} = useChat({
api: '/api/chat',
onError: (err) => console.error('Chat error:', err),
});
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-4 text-sky-400">AI 챗봇</h1>
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.map((msg) => (
<div
key={msg.id}
className={msg.role === 'user' ? 'bg-sky-100 ml-8' : 'bg-gray-100 mr-8'}
>
<span className="text-xs text-gray-500">{msg.role === 'user' ? '나' : 'AI'}</span>
<p className="whitespace-pre-wrap">{msg.content}</p>
</div>
))}
{isLoading && (
<div className="bg-gray-100 mr-8 p-3 rounded-lg">
<p className="text-gray-400 animate-pulse">응답 생성 중...</p>
</div>
)}
</div>
{error && (
<div className="mb-2 p-2 bg-red-100 text-red-600 rounded text-sm">
오류가 발생했습니다.
<button onClick={() => reload()} className="underline ml-1">다시 시도</button>
</div>
)}
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="메시지를 입력하세요..."
disabled={isLoading}
className="flex-1 border border-gray-300 rounded-lg px-4 py-2"
/>
{isLoading ? (
<button type="button" onClick={() => stop()} className="px-4 py-2 bg-red-500 text-white rounded-lg">중단</button>
) : (
<button type="submit" disabled={!input.trim()} className="px-4 py-2 bg-sky-500 text-white rounded-lg">전송</button>
)}
</form>
</div>
);
}
고급 기능: Tool Calling
단순한 대화를 넘어, Tool Calling을 활용하면 AI가 외부 API를 호출하거나 특정 작업을 수행하도록 할 수 있습니다.
// app/api/chat/route.ts (Tool Calling 버전)
import { streamText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools: {
getWeather: tool({
description: '특정 도시의 현재 날씨를 조회합니다.',
parameters: z.object({
city: z.string().describe('날씨를 조회할 도시명'),
}),
execute: async ({ city }) => {
return { city, temperature: 22, condition: '맑음' };
},
}),
},
maxSteps: 5,
});
return result.toDataStreamResponse();
}
멀티 모델 지원: 공급자 전환하기
Vercel AI SDK의 강점은 공급자를 손쉽게 바꿀 수 있다는 것입니다.
import { google } from '@ai-sdk/google';
import { anthropic } from '@ai-sdk/anthropic';
// Google Gemini로 전환
const result = await streamText({
model: google('gemini-2.0-flash-exp'),
messages,
});
// Anthropic Claude로 전환
const result2 = await streamText({
model: anthropic('claude-3-5-sonnet-20241022'),
messages,
});
코어 로직은 동일하게 유지하면서 모델만 바꿀 수 있어, A/B 테스트나 비용 최적화에 유리합니다.
대화 기록 영구 저장
기본 useChat은 페이지 새로고침 시 대화 기록이 사라집니다. 영구 저장을 위해 initialMessages와 서버 저장 로직을 결합합니다.
const { messages } = useChat({
initialMessages: previousMessages,
onFinish: async (message) => {
await fetch('/api/save-message', {
method: 'POST',
body: JSON.stringify({ message }),
});
},
});
정리: 왜 Vercel AI SDK인가?
Vercel AI SDK는 스트리밍 챗봇 구현의 복잡도를 획기적으로 낮춰줍니다. streamText로 백엔드 스트리밍을 처리하고, useChat으로 프론트엔드 상태를 관리하면, 수백 줄이 필요했던 작업을 수십 줄로 해결할 수 있습니다. Tool Calling, 멀티 모델 지원, Edge Runtime 최적화까지 갖춰져 있어 프로덕션 수준의 AI 애플리케이션을 빠르게 구축할 수 있습니다.
코드벤터는 Vercel AI SDK와 같은 최신 AI 개발 도구를 적극 활용해 실용적인 AI 서비스를 빠르게 만들어냅니다. 복잡한 기술을 단순하게, 아이디어를 현실로 바꾸는 것이 코드벤터가 추구하는 방향입니다. 함께 만들어가는 여정에 동참하고 싶다면 codepick.kr을 찾아주세요.