Skip to content

Commit 22e936a

Browse files
authored
Merge pull request #211 from Team-3-2/feat/chatbot
[#210] 리스트 페이지 챗봇 구현
2 parents 64cdc2b + 8304c10 commit 22e936a

File tree

11 files changed

+386
-7
lines changed

11 files changed

+386
-7
lines changed

package-lock.json

Lines changed: 89 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"chromatic": "chromatic --exit-zero-on-changes"
1414
},
1515
"dependencies": {
16+
"@chatscope/chat-ui-kit-react": "^2.1.1",
17+
"@chatscope/chat-ui-kit-styles": "^1.4.0",
1618
"@tanstack/react-query": "^5.90.2",
1719
"@tanstack/react-query-devtools": "^5.90.2",
1820
"axios": "^1.12.2",

public/icons/ic_gpt.svg

Lines changed: 3 additions & 0 deletions
Loading

src/app/api/chat/route.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
export const runtime = "edge";
2+
3+
export async function POST(req: Request) {
4+
const { message } = await req.json();
5+
6+
const res = await fetch(`${process.env.OPENAI_API_URL}/v1/chat/completions`, {
7+
method: "POST",
8+
headers: {
9+
"Content-Type": "application/json",
10+
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
11+
},
12+
body: JSON.stringify({
13+
model: "gpt-5-nano",
14+
messages: [
15+
{
16+
role: "system",
17+
content:
18+
"당신은 와인 추천 전문 어시스턴트입니다. 모든 대답은 자연스러운 한국어로 2~3문장 이내로 간결하게 작성하세요. 사용자의 질문이 와인과 관련이 없으면 '이 AI는 와인에 대한 내용만 답변 가능합니다.'라고만 답하세요. 추천 시에는 간단한 이유를 한 문장으로 덧붙이세요. 불확실한 정보는 추측하지 말고 '해당 정보는 정확히 알 수 없습니다.'라고 답하세요. 이미지나 그림, 사진을 생성하거나 설명해 달라는 요청이 있을 경우 '이 AI는 이미지 관련 기능을 지원하지 않습니다.'라고만 답하세요.",
19+
},
20+
{ role: "user", content: message },
21+
],
22+
}),
23+
});
24+
25+
if (!res.ok) {
26+
const err = await res.text();
27+
return new Response(JSON.stringify({ error: err }), { status: 500 });
28+
}
29+
30+
const data = await res.json();
31+
const reply = data.choices?.[0]?.message?.content ?? "";
32+
return new Response(JSON.stringify({ reply }), {
33+
headers: { "Content-Type": "application/json" },
34+
});
35+
}

src/app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import QueryProvider from "@/providers/query-provider";
66
import getMe from "@/api/user/get-me";
77
import KaKaoInitializer from "@/lib/kakao-initializer";
88
import ToastProvider from "@/providers/toast/toast-provider";
9+
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
910
import { ToastContainer } from "react-toastify";
1011

1112
export function generateMetadata() {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import { cn } from "@/lib/utils";
4+
import { IconButton } from "@/components";
5+
import { usePathname } from "next/navigation";
6+
import { useState } from "react";
7+
import ChatBot from "../chat-bot/chat-bot";
8+
9+
/**
10+
* 채팅 봇을 열기 위한 버튼 컴포넌트
11+
* @author jikwon
12+
*/
13+
14+
const ChatButton = () => {
15+
const pathname = usePathname();
16+
const [isOpen, setIsOpen] = useState(false);
17+
18+
if (pathname !== "/wines") return null;
19+
20+
return (
21+
<>
22+
<IconButton
23+
icon="GptIcon"
24+
iconSize="md"
25+
className={cn(
26+
"relative h-[40px] w-[40px] rounded-full border-gray-300",
27+
"tablet:h-[50px] tablet:w-[50px]",
28+
"pc:h-[50px] pc:w-[50px]"
29+
)}
30+
aria-label="채팅 봇 열기"
31+
onClick={() => setIsOpen(!isOpen)}
32+
/>
33+
{isOpen && <ChatBot open={isOpen} />}
34+
</>
35+
);
36+
};
37+
38+
export default ChatButton;

0 commit comments

Comments
 (0)