빠른 시작: Vue
Vue 3 앱에 AI 채팅을 추가하려고 합니다. 이 가이드를 마치면 TanStack AI와 OpenAI로 구동되는 스트리밍 채팅 컴포넌트를 사용할 수 있습니다.
팁: 개별 AI 제공업체에 가입하고 싶지 않다면 OpenRouter를 사용하면 하나의 API 키로 300개 이상의 모델에 액세스할 수 있어 가장 쉽게 시작할 수 있습니다.
설치
npm install @tanstack/ai @tanstack/ai-vue @tanstack/ai-openai
# or
pnpm add @tanstack/ai @tanstack/ai-vue @tanstack/ai-openai
# or
yarn add @tanstack/ai @tanstack/ai-vue @tanstack/ai-openai
서버 설정
Vue 앱은 일반적으로 별도의 백엔드를 사용합니다. 다음은 채팅 응답을 스트리밍하는 Express 서버입니다.
import express from 'express'
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const app = express()
app.use(express.json())
app.post('/api/chat', async (req, res) => {
const { messages } = req.body
if (!process.env.OPENAI_API_KEY) {
res.status(500).json({ error: 'OPENAI_API_KEY not configured' })
return
}
try {
// `chat()` uses the AG-UI `threadId` for devtools correlation
// when available — no need to plumb `conversationId` manually.
const stream = chat({
adapter: openaiText('gpt-5.5'),
messages,
})
const response = toServerSentEventsResponse(stream)
res.writeHead(response.status, Object.fromEntries(response.headers))
const body = response.body
if (body) {
const reader = body.getReader()
const pump = async () => {
const { done, value } = await reader.read()
if (done) {
res.end()
return
}
res.write(value)
await pump()
}
await pump()
}
} catch (error) {
res.status(500).json({
error: error instanceof Error ? error.message : 'An error occurred',
})
}
})
app.listen(3000, () => console.log('Server running on port 3000'))
팁: TanStack AI SSE 형식을 반환하는 모든 백엔드가 작동하므로 Fastify, Hono, Nitro 또는 다른 Node.js 프레임워크를 사용할 수 있습니다.
클라이언트 설정
useChat 컴포저블을 사용하여 Chat.vue 컴포넌트를 생성합니다.
<script setup lang="ts">
import { ref } from 'vue'
import { useChat, fetchServerSentEvents } from '@tanstack/ai-vue'
const input = ref('')
const { messages, sendMessage, isLoading } = useChat({
connection: fetchServerSentEvents('/api/chat'),
})
function handleSubmit() {
if (input.value.trim() && !isLoading.value) {
sendMessage(input.value)
input.value = ''
}
}
</script>
<template>
<div class="chat">
<div class="messages">
<div
v-for="message in messages"
:key="message.id"
:class="message.role"
>
<strong>{{ message.role === 'assistant' ? 'Assistant' : 'You' }}</strong>
<div v-for="(part, idx) in message.parts" :key="idx">
<p v-if="part.type === 'text'">{{ part.content }}</p>
</div>
</div>
</div>
<form @submit.prevent="handleSubmit">
<input
v-model="input"
placeholder="Type a message..."
:disabled="isLoading"
/>
<button type="submit" :disabled="!input.trim() || isLoading">
Send
</button>
</form>
</div>
</template>
환경 변수
API 키가 포함된 .env 파일(설정에 따라 .env.local)을 생성합니다.
# OpenRouter (recommended — access 300+ models with one key)
OPENROUTER_API_KEY=sk-or-...
# OpenAI
OPENAI_API_KEY=your-openai-api-key
서버는 런타임에 이 키를 읽습니다. 브라우저에 절대 노출하지 마세요.
Vue 관련 참고 사항
반응형 상태는 ShallowRef를 사용합니다. useChat 컴포저블은 DeepReadonly<ShallowRef<>>로 래핑된 상태를 반환합니다. <script setup>에서는 .value로 내부 값을 읽어야 합니다. <template>에서는 Vue가 ref를 자동으로 언래핑하므로 이름만 사용합니다.
<script setup lang="ts">
// In script, use .value
if (isLoading.value) { /* ... */ }
const count = messages.value.length
</script>
<template>
<!-- In template, Vue unwraps the ref automatically — no .value -->
<span v-if="isLoading">Loading...</span>
<span>{{ messages.length }} messages</span>
</template>
자동 정리. 컴포저블은 내부적으로 onScopeDispose를 호출하므로 컴포넌트가 언마운트되면 진행 중인 요청이 중지됩니다. 수동 정리는 필요하지 않습니다.
React와 동일한 API 형태. @tanstack/ai-react에서 넘어왔다면 Vue 컴포저블도 동일한 속성을 반환합니다(messages, sendMessage, isLoading, error, status, stop, reload, clear). 유일한 차이는 ShallowRef 래퍼입니다.
이것으로 끝입니다!
이제 작동하는 Vue 채팅 애플리케이션이 완성되었습니다. useChat 컴포저블은 다음을 처리합니다.
- 메시지 상태 관리
- 스트리밍 응답
- 로딩 상태
- 오류 처리
다음 단계
- 함수 호출을 추가하려면 도구에 대해 알아봅니다.
- 다른 제공업체에 연결하려면 어댑터를 확인합니다.
- 프레임워크를 비교 중이라면 React 빠른 시작을 참고합니다.