AI.zip
  • AI 모델
  • 방법론
  • AI 서비스
  • 가격 비교
  • 블로그

AI.zip

AI 모델, 서비스, 방법론을 큐레이션하는 에디토리얼 플랫폼

탐색

  • AI 모델
  • AI 서비스
  • 방법론
  • 블로그

커뮤니티

  • 소개
  • 디스코드 참여
  • 문의

법적고지

  • 이용약관
  • 개인정보처리방침

© 2026 ai.zip. All rights reserved.

Discord 커뮤니티
블로그Vercel AI SDK 완전 가이드: Next.js에 AI 채팅·스트리밍 붙이기 (2025)

Vercel AI SDK 완전 가이드: Next.js에 AI 채팅·스트리밍 붙이기 (2025)

튜토리얼
2026년 3월 28일약 3분

핵심 포인트

  • 1.Vercel AI SDK란
  • 2.Vercel AI SDK는 Next.js(및 Node.js) 앱에 AI 기능을 붙이는 가장 빠른 방법입니다
  • 3.OpenAI, Anthropic, Google 등 주요 AI 공급자를 통합 인터페이스로 다룰 수 있고, 스트리밍·채팅·구조화 출력을 위한 React Hook과 서버 유틸리티를 제공합니다

Vercel AI SDK란?

Vercel AI SDK는 Next.js(및 Node.js) 앱에 AI 기능을 붙이는 가장 빠른 방법입니다. OpenAI, Anthropic, Google 등 주요 AI 공급자를 통합 인터페이스로 다룰 수 있고, 스트리밍·채팅·구조화 출력을 위한 React Hook과 서버 유틸리티를 제공합니다.

bash
pnpm add ai @ai-sdk/openai @ai-sdk/anthropic

3

0초 만에 스트리밍 채팅 만들기

서버 (app/api/chat/route.ts)

typescript
import { openai } from '@ai-sdk/openai'
import { streamText } from 'ai'

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

  const result = streamText({
    model: openai('gpt-4o-mini'),
    system: '당신은 친절한 AI 어시스턴트입니다.',
    messages,
  })

  return result.toDataStreamResponse()
}

클라이언트 (app/chat/page.tsx)

tsx
'use client'

import { useChat } from 'ai/react'

export default function ChatPage() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat()

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
      <div className="flex-1 overflow-y-auto space-y-4">
        {messages.map(m => (
          <div key={m.id} className={m.role === 'user' ? 'text-right' : 'text-left'}>
            <span className="inline-block bg-gray-100 rounded-lg px-4 py-2">
              {m.content}
            </span>
          </div>
        ))}
        {isLoading && <div className="text-gray-400">생각 중...</div>}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2 mt-4">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="메시지를 입력하세요..."
          className="flex-1 border rounded-lg px-4 py-2"
        />
        <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded-lg">
          전송
        </button>
      </form>
    </div>
  )
}

이게 전부입니다. useChat 훅 하나로 메시지 상태, 스트리밍, 로딩 상태를 모두 관리합니다.


모델 교체: 코드 변경 최소화

AI SDK의 핵심 장점은 공급자 교체가 쉽다는 것입니다:

typescript
// OpenAI → Claude로 교체: 딱 2줄
import { anthropic } from '@ai-sdk/anthropic'
// import { openai } from '@ai-sdk/openai'

const result = streamText({
  model: anthropic('claude-sonnet-4-6-20251001'),
  // model: openai('gpt-4o'),
  messages,
})

구조화 출력 (Structured Output)

JSON 형식으로 안정적인 데이터를 받아야 할 때:

typescript
import { generateObject } from 'ai'
import { openai } from '@ai-sdk/openai'
import { z } from 'zod'

// Zod 스키마로 출력 형식 정의
const ProductSchema = z.object({
  name: z.string().describe('상품명'),
  category: z.enum(['전자제품', '의류', '식품', '기타']),
  sentiment: z.enum(['긍정', '부정', '중립']),
  keyIssues: z.array(z.string()).describe('주요 문제점 목록'),
  rating: z.number().min(1).max(5),
})

export async function analyzeReview(reviewText: string) {
  const { object } = await generateObject({
    model: openai('gpt-4o-mini'),
    schema: ProductSchema,
    prompt: `다음 리뷰를 분석하세요: ${reviewText}`,
  })

  return object
  // { name: "...", category: "전자제품", sentiment: "부정", keyIssues: [...], rating: 2 }
}

generateText 대신 generateObject를 쓰면 파싱 코드 없이 타입 안전한 JSON을 받을 수 있습니다.


Tool Use (함수 호출)

AI가 도구를 사용해 실제 작업을 수행하게 합니다:

typescript
import { streamText, tool } from 'ai'
import { openai } from '@ai-sdk/openai'
import { z } from 'zod'

const result = streamText({
  model: openai('gpt-4o'),
  tools: {
    getWeather: tool({
      description: '특정 도시의 날씨를 조회합니다',
      parameters: z.object({
        city: z.string().describe('도시명'),
      }),
      execute: async ({ city }) => {
        // 실제 날씨 API 호출
        const response = await fetch(`https://api.weather.com/${city}`)
        return response.json()
      },
    }),

    searchDatabase: tool({
      description: '제품 데이터베이스에서 검색합니다',
      parameters: z.object({
        query: z.string(),
        limit: z.number().default(5),
      }),
      execute: async ({ query, limit }) => {
        return await db.products.search(query, limit)
      },
    }),
  },
  messages,
  maxSteps: 5, // 최대 도구 호출 횟수
})

useCompletion: 단발성 텍스트 생성

채팅이 아닌 단발성 생성(문서 요약, 번역 등)에는 useCompletion을 사용합니다:

tsx
'use client'

import { useCompletion } from 'ai/react'

export function SummaryButton({ article }: { article: string }) {
  const { completion, complete, isLoading } = useCompletion({
    api: '/api/summarize',
  })

  return (
    <div>
      <button
        onClick={() => complete(article)}
        disabled={isLoading}
        className="bg-blue-500 text-white px-4 py-2 rounded"
      >
        {isLoading ? '요약 중...' : '요약하기'}
      </button>
      {completion && (
        <div className="mt-4 p-4 bg-gray-50 rounded">
          {completion}
        </div>
      )}
    </div>
  )
}

미들웨어와 에러 처리

프로덕션에서는 레이트 리밋과 에러 처리가 필수입니다:

typescript
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai'
import { streamText } from 'ai'
import { NextResponse } from 'next/server'

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

    // 레이트 리밋 확인
    const allowed = await checkRateLimit(userId)
    if (!allowed) {
      return NextResponse.json(
        { error: '요청 한도를 초과했습니다. 잠시 후 다시 시도해주세요.' },
        { status: 429 }
      )
    }

    const result = streamText({
      model: openai('gpt-4o'),
      messages,
      onFinish: async ({ usage, text }) => {
        // 사용량 로깅
        await logUsage({ userId, tokens: usage.totalTokens, chars: text.length })
      },
    })

    return result.toDataStreamResponse()
  } catch (error) {
    console.error('AI API 에러:', error)
    return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 })
  }
}

주요 Hook 정리

Hook용도반환값
useChat멀티턴 채팅messages, input, handleSubmit
useCompletion단발성 생성completion, complete, isLoading
useAssistantOpenAI Assistants APImessages, submitMessage
useObject실시간 구조화 출력object, submit, isLoading

결론

Vercel AI SDK는 Next.js 개발자에게 최적화된 AI 통합 도구입니다. 핵심 장점:

  1. 통합 인터페이스: OpenAI, Claude, Gemini를 동일한 코드로 교체
  2. React 최적화: 스트리밍, 로딩 상태, 에러 처리를 Hook으로 추상화
  3. 타입 안전: TypeScript + Zod로 AI 출력을 안전하게 처리
  4. 서버 컴포넌트 지원: Next.js App Router와 완벽 통합

AI 기능을 처음 Next.js에 붙여보고 싶다면 공식 문서(sdk.vercel.ai)의 예제부터 시작하세요.

이 글에서 다루는 AI

Anthropic: Claude Sonnet 4.6

Anthropic · 모델

OpenAI: GPT-4o

OpenAI · 모델

Anthropic: Claude Sonnet 4.5

Anthropic · 모델

관련 글 더 보기

가이드2026년 4월 6일

AI 재귀적 자기 개선 완전 가이드: 특이점 논쟁부터 실전 활용까지

비교2026년 4월 6일

Cursor vs GitHub Copilot vs Windsurf: AI 코딩 어시스턴트 비교 (2026)

비교2026년 4월 6일

GPT-5.4 vs Claude Opus 4.6 vs Gemini 2.5 Pro: 2026 플래그십 AI 비교

비교2026년 4월 6일

Auto Research vs AutoML: LLM 자율 연구와 자동 ML의 핵심 차이

ai.zip 커뮤니티에 참여하세요

AI 소식·유용한 링크 공유, 새 모델/서비스 토론까지 -- Discord에서 함께해요.

Discord 참여하기

이전글

멀티에이전트 시스템 설계: 여러 AI가 협업하는 아키텍처 패턴

다음글

Hugging Face 완전 가이드: AI 개발자의 GitHub (2025)

댓글

0개

댓글을 작성하려면

로그인

해주세요

글 정보

튜토리얼
2026년 3월 28일3분

관련 글

AI 재귀적 자기 개선 완전 가이드: 특이점 논쟁부터 실전 활용까지

가이드

Cursor vs GitHub Copilot vs Windsurf: AI 코딩 어시스턴트 비교 (2026)

비교

GPT-5.4 vs Claude Opus 4.6 vs Gemini 2.5 Pro: 2026 플래그십 AI 비교

Google: Gemini 2.5 Flash

Google · 모델

OpenAI API Platform

서비스

Claude API

서비스

v0

서비스

Foundation Model API Strategy

방법론

Agentic Workflow

방법론

MCP (Model Context Protocol)

방법론

RAG (Retrieval-Augmented Generation)

방법론

비교

Auto Research vs AutoML: LLM 자율 연구와 자동 ML의 핵심 차이

비교

Cursor vs Claude Code vs Copilot: 2026 AI 코딩 어시스턴트 최종 비교

비교

관련 모델

Anthropic: Claude Sonnet 4.6

Anthropic

OpenAI: GPT-4o

OpenAI

Anthropic: Claude Sonnet 4.5

Anthropic

관련 서비스

OpenAI API Platform

Claude API

v0

관련 방법론

Foundation Model API Strategy

Agentic Workflow

MCP (Model Context Protocol)