06 · Developer Guide · 레시피
AI 안내봇 (FAQ 즉답형)Chatbot / AI Assistant
예배·위치·새가족 등 자주 묻는 질문을 사이트 안에서 즉답하는 플로팅 안내봇.
외부 AI 키·서버 없이 키워드 매칭 FAQ로 동작하며, 나중에 Claude API 한 단계만 얹어 자연어 챗봇으로 확장할 수 있습니다.
언제 쓰는가
- 예배 시간·오시는 길·새가족·주보·교구 등 반복 문의를 24시간 즉답
- 사무실 전화 응대 부담 경감 (모르면 사무국 번호로 안내)
- 운영 비용 0원으로 먼저 도입하고, 트래픽이 늘면 AI로 승급
구조 — 3단 파이프라인
비용을 최소화하는 단계형 응답 구조입니다. 현재 시안은 ①단계(FAQ 즉답)만 사용하므로 API 비용이 0원입니다.
- ① FAQ 즉답 — 키워드 매칭으로 정해진 답을 반환. 모델 호출 없음, 비용 $0.
- ② 응답 캐시 (선택) — 과거 AI 답을 해시로 재사용.
- ③ AI 호출 (선택) — ①②가 실패할 때만 Claude 호출.
① FAQ 지식베이스 + 매칭
lib/preview/chatbot-faq.tstsx
// lib/preview/chatbot-faq.ts
export type FaqLink = { label: string; href: string };
export type FaqItem = {
id: string;
suggest?: string; // 추천 질문 칩 라벨
keywords: string[]; // 사용자 입력에 포함되면 가점
answer: string; // 답변 본문 (\n 줄바꿈 지원)
links?: FaqLink[]; // 답변 하단 바로가기 (내부 라우트만)
};
export const FAQ_ITEMS: FaqItem[] = [
{
id: "worship-time",
suggest: "예배 시간이 언제인가요?",
keywords: ["예배", "시간", "몇시", "언제", "주일", "새벽", "수요"],
answer:
"주일예배는 1부 07:00 · 2부 09:00 · 3부 11:00 · 4부 12:50 · 5부(청년) 14:30…",
links: [{ label: "예배 안내 자세히 보기", href: "/site/worship" }],
},
// … 위치 · 새가족 · 주보 · 교구 · 큐티 · 교역자 · 양육 · 선교 · 헌금 · 문의
];
// 키워드 가점으로 가장 잘 맞는 FAQ를 찾는다. 0점이면 null(→ fallback).
export function matchFaq(input: string): FaqItem | null {
const text = input.toLowerCase().replace(/\s+/g, " ").trim();
if (!text) return null;
let best: FaqItem | null = null;
let bestScore = 0;
for (const item of FAQ_ITEMS) {
let score = 0;
for (const kw of item.keywords) {
if (text.includes(kw.toLowerCase().trim())) score += kw.length >= 2 ? 2 : 1;
}
if (score > bestScore) { bestScore = score; best = item; }
}
return bestScore > 0 ? best : null;
}위젯 컴포넌트
components/preview/ChatWidget.tsxtsx
// components/preview/ChatWidget.tsx (요약)
"use client";
import { useState } from "react";
import { matchFaq, CHATBOT_INTRO, CHATBOT_FALLBACK, SUGGESTED } from "@/lib/preview/chatbot-faq";
export function ChatWidget() {
const [open, setOpen] = useState(false);
const [messages, setMessages] = useState([{ role: "bot", text: CHATBOT_INTRO }]);
function send(text: string) {
const q = text.trim();
if (!q) return;
const hit = matchFaq(q); // ① FAQ 즉답 (무료)
const bot = hit
? { role: "bot", text: hit.answer, links: hit.links }
: { role: "bot", text: CHATBOT_FALLBACK }; // ② 미스 → 사무실 안내
setMessages((m) => [...m, { role: "user", text: q }, bot]);
}
// 플로팅 버튼(+"안내봇" 뱃지) → 패널(모바일 풀스크린 / 데스크톱 380×560)
// 헤더(초기화·닫기) · 말풍선 · 추천질문 칩(SUGGESTED) · 입력창 · 면책 푸터
// …
}마운트
tsx
// app/(site)/layout.tsx — 모든 시안 페이지에 공통 마운트
import { ChatWidget } from "@/components/preview/ChatWidget";
export default function SiteLayout({ children }) {
return (
<div className="flex min-h-screen flex-col">
<PreviewHeader />
<div className="flex-1">{children}</div>
<PreviewFooter />
<ChatWidget /> {/* 우하단 플로팅 */}
</div>
);
}설계 포인트
확장 — Claude AI 하이브리드
자연어 대응이 필요해지면, 현재 구조를 그대로 두고 FAQ 미스일 때만 Claude를 호출하는 API route 한 단계만 추가하면 됩니다. (Anthropic API 키 + 월 소액 토큰 비용)
app/api/chat/route.ts (확장 예시)tsx
// (확장) FAQ 미스일 때만 Claude 호출 — app/api/chat/route.ts
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic(); // ANTHROPIC_API_KEY (서버 환경변수)
export async function POST(req: Request) {
const { question } = await req.json();
const msg = await client.messages.create({
model: "claude-haiku-4-5-20251001",
max_tokens: 400,
system: SYSTEM_PROMPT, // 지침 + 시안 데이터(예배·교구·선교…) + 링크 화이트리스트
messages: [{ role: "user", content: question }],
});
return Response.json({ answer: msg.content[0].text });
}
// 위젯 send(): matchFaq() 실패 시 fetch("/api/chat") 한 단계만 추가하면 됨