Start
시작하기
opt-chat을 설치하고 첫 번째 채팅 UI를 만드는 과정을 안내합니다.
reopt designUpdated
1. 설치
bash
bun add @reopt-ai/opt-chat2. 피어 의존성
opt-chat은 React 19 이상을 요구합니다. 이미 설치되어 있다면 이 단계는 건너뛰어도 됩니다.
bash
# 피어 의존성 (이미 설치되어 있다면 생략)
bun add react@^19 react-dom@^193. CSS 설정
Tailwind v4와 opt-ui 토큰 위에 opt-chat 스타일을 추가합니다. opt-chat의 CHAT_* 토큰은 opt-ui의 --opt-* 변수를 참조하여 자동으로 테마를 상속합니다.
css
/* app/globals.css — Tailwind v4 + opt-ui 토큰 */
@import "tailwindcss";
@import "@reopt-ai/opt-ui/styles.css";
@import "@reopt-ai/opt-chat/styles.css";4. API 라우트
AI SDK의 streamText로 스트리밍 응답을 반환하는 API 라우트를 만듭니다.
ts
// app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { convertToModelMessages, streamText } from "ai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}5. 기본 예제
useChatSession으로 세션을 생성하고, Conversation 안에 Message 루프와 PromptInput을 배치하면 완성입니다.
tsx
"use client";
import { DefaultChatTransport } from "ai";
import {
useChatSession,
Conversation,
ConversationContent,
Message,
MessageContent,
MessageParts,
PromptInput,
PromptInputActionAddAttachment,
PromptInputAttachments,
PromptInputFooter,
PromptInputSubmit,
PromptInputTextarea,
} from "@reopt-ai/opt-chat";
export default function ChatPage() {
const session = useChatSession({
transport: new DefaultChatTransport({ api: "/api/chat" }),
});
return (
<div className="mx-auto flex h-dvh max-w-2xl flex-col py-8">
<Conversation>
<ConversationContent>
{session.messages.map((msg) => (
<Message key={msg.id} from={msg.role}>
<MessageContent>
<MessageParts parts={msg.parts} isStreaming={session.isStreaming} />
</MessageContent>
</Message>
))}
</ConversationContent>
</Conversation>
<PromptInput onSubmit={session.handleSubmit} isLoading={session.isLoading} onStop={session.stop}>
<PromptInputAttachments />
<PromptInputTextarea placeholder="메시지를 입력하세요..." />
<PromptInputFooter>
<PromptInputActionAddAttachment />
<div className="flex-1" />
<PromptInputSubmit />
</PromptInputFooter>
</PromptInput>
</div>
);
}