Biz Next.js Chatbot OpenRouter — AI 챗봇 플로팅 위젯을 웹사이트에 1분만에 설치하기
이 스킬은 기존 Next.js 프로젝트에 AI 챗봇 플로팅 위젯을 추가합니다. 위젯은 화면 우측 하단에 위치하며, 3가지 화면 크기에 반응형으로 동작합니다. OpenRouter를 통해 LLM을 호출하고, FAQ + 강좌/제품 자료를 기반으로 지식 베이스를 구축합니다. 어시스턴트 말풍선에서 마크다운(숫자/가격 굵게, 글머리 목록, 클릭 가능한 링크, 인라인 코드)을 렌더링하며(react-markdown + remark-gfm 사용), 사용자가 채팅에 이름, 전화번호, 이메일 등 정보를 모두 제공하면 자동으로 추출하여 KV(Upstash Redis / Vercel KV)에 저장합니다. 관리자는 /admin 대시보드에서 리드를 확인할 수 있습니다. 목표: 솔로프리너 / 강좌 제작자가 랜딩 페이지에 24/7 자동 응답 봇 + 수동적 리드 캡쳐를 갖추어, 채팅을 통해 문의한 고객을 놓치지 않도록 하는 것입니다.
핵심 철학: 가능한 모든 것을 자동으로 감지합니다(Next.js 버전, App/Pages Router, TS/JS, Tailwind/CSS). 사용자에게 정말 필요한 4가지만 묻습니다: (1) 여러 프로젝트 중 선택, (2) FAQ, (3) 제품 문서, (4) 어조/브랜드명. 나머지는 코드 → 몇 분 내로 라이브.
사용 시기
사용자가 다음을 원할 때:
- 강좌/서비스 판매 랜딩 페이지에 고객 지원 AI 챗봇을 설치
- 반복적인 질문(가격, 커리큘럼, 환불, 대상 적합성 등)에 응답하는 부담을 줄이고자 할 때
- 콘텐츠를 가리지 않는 작고 컴팩트한 우측 하단 채팅 위젯이 필요할 때
- 챗봇이 자신의 강좌/제품 내용(FAQ 기반)을 정확히 이해하기를 원할 때
- 이미 Next.js 프로젝트가 있을 때(
/biz-sales-page-layout실행 완료 후, 또는 기존 프로젝트)
사용하지 말아야 할 때
- 프로젝트가 Next.js가 아닐 때(순수 React Vite, Vue, Svelte, 일반 HTML) → 이 스킬은 Next.js 전용입니다. 적용 가능하지만 최적화되지 않습니다.
- 수천 페이지 규모의 지식 베이스를 갖춘 RAG(검색 증강 생성) 챗봇이 필요할 때 → 벡터 DB(Pinecone/Supabase pgvector)가 필요합니다. 이 스킬은 20K 토큰 이하의 지식에 적합한 간단한 시스템 프롬프트를 사용합니다.
- 음성/멀티모달(이미지 전송) 챗봇이 필요할 때 → 이 스킬은 텍스트 전용입니다.
- 사용자별 채팅 기록을 DB에 저장해야 할 때 → 이 스킬은 상태 비저장(stateless)이며 임시 세션 방식입니다. 추후 확장 가능합니다.
- 백엔드가 Vercel 친화적이지 않을 때(롱 폴링, 영구 WebSocket 필요) → OpenRouter 스트리밍은 Vercel Edge에서 잘 작동하지만, 별도 WS가 필요하다면 상황이 다릅니다.
사용자가 받게 될 결과
스킬 실행 완료 후:
-
Next.js 프로젝트 내 새 파일:
components/Chatbot.tsx(또는.jsx) — 위젯 UI (react-markdown + remark-gfm을 통한 마크다운 렌더링)app/api/chat/route.ts(App Router) 또는pages/api/chat.ts(Pages Router) — OpenRouter 호출 + 스트림 후 자동 리드 추출 API 프록시lib/chatbot-knowledge.ts(또는.js) — 지식 베이스 + 시스템 프롬프트 (응답 형식 지정 블록 + 능동적 리드 수집 블록 포함)lib/chat-lead.ts— KV/Upstash가 있는 프로젝트에서만 생성 — 추출기 + 저장기, 전화번호 기준 중복 제거, TTL 90일, 네임스페이스chat-lead:{phone}- 마크다운 스타일
.chatbot-md가app/globals.css에 추가됨 (templates/chatbot-markdown-styles.css에서) .env.local파일 생성/업데이트 (OPENROUTER_API_KEY=플레이스홀더 포함).env.example파일 (없는 경우) 생성하여 환경 변수 문서화<Chatbot />가app/layout.tsx또는pages/_app.tsx에 마운트됨
-
추가로 설치되는 패키지 (락파일에서 감지된 패키지 매니저를 통해):
react-markdown— 안전한 마크다운 렌더링 (dangerouslySetInnerHTML불필요)remark-gfm— GitHub Flavored Markdown (테이블, 자동 링크, 취소선)
-
세션 종료 시 명확한 안내:
- 사용자가 직접 해야 할 3단계: (a) OpenRouter API 키 받기, (b)
.env.local에 붙여넣기, (c)npm run dev로 테스트 - Gemini와 Sonnet 간 모델 변경 방법
- 추후 지식 베이스 업데이트 방법
- 사용자가 직접 해야 할 3단계: (a) OpenRouter API 키 받기, (b)
워크플로우
1단계 — Next.js 프로젝트 탐지
Next.js 프로젝트 후보를 찾습니다. 실행:
# 최대 깊이 4에서 package.json에 "next"가 포함된 파일 찾기
find /Users/tonyhoang/Documents/GitHub -maxdepth 4 -name "package.json" -not -path "*/node_modules/*" 2>/dev/null | xargs grep -l '"next"' 2>/dev/null | head -20
(사용자가 특정 작업 디렉토리에 있는 경우, cwd부터 우선 검색하고, 없으면 ~/Documents/GitHub로 폴백합니다.)
분기 처리:
- 0개 프로젝트: 사용자에게 알리고 중단. 제안: "혹시 가지고 계신 Next.js 프로젝트가 있나요? 아직 없다면
/biz-sales-page-layout으로 새로 만든 후 다시 진행하시겠어요?" - 1개 프로젝트: 진행 전에 사용자에게 경로 확인: "[경로]에 챗봇을 설치하겠습니다. 괜찮으신가요?"
- 2개 이상 프로젝트: 경로 및 이름(package.json의
name)을 번호 목록으로 표시하고 사용자에게 선택 요청: "N개의 Next.js 프로젝트가 있습니다. 어디에 설치할까요?"
2단계 — 프로젝트 컨텍스트 조사
사용자가 프로젝트를 확인한 후, 다음을 읽습니다:
-
package.json→ 확인:- TypeScript (
typescript가 deps에 있는가?) - Tailwind (
tailwindcss가 devDeps에 있는가?) - 패키지 매니저 (락파일:
pnpm-lock.yaml→ pnpm,yarn.lock→ yarn, 기본npm) - Next.js 버전
- TypeScript (
-
디렉토리 구조 → 확인:
app/디렉토리 존재 → App Routerpages/디렉토리 존재 (그리고app/없음) → Pages Router- 둘 다 존재 → 새 파일은 App Router 우선 (Next.js가 전환 중)
src/래퍼 → 경로 조정 (예:src/app/,src/components/)
-
tsconfig.json→ 경로 별칭 확인 (@/*→./src/*인지./*인지) -
.env.local,.env.example→ 이미 존재하는지 확인 -
KV/Upstash 사용 가능 여부 (자동 리드 캡쳐용):
package.json에@upstash/redis또는@vercel/kv있음 → KV 있음 → chat-lead.ts 스캐폴딩 + 라우트 연결src/lib/kv.ts(또는lib/kv.ts) 파일이kv를 export → 기존 헬퍼 사용- KV 없음 → chat-lead.ts 스캐폴딩 생략, 사용자에게 알림: "프로젝트에 KV가 없어 자동 리드 캡쳐가 활성화되지 않았습니다.
/biz-setup-sepay-payment를 실행하여 KV를 먼저 설치한 후 다시 돌아와 리드 캡쳐를 활성화하거나, 건너뛸 수 있습니다."
이러한 관찰 내용을 작은 표로 저장하여 이후 단계에서 사용합니다.
3단계 — 사용자로부터 지식 베이스 수집
사용자에게 4가지 질문 (한 메시지에 모아서 사용자가 한 번에 답변할 수 있도록 하여 불필요한 대화를 줄임):
챗봇이 정확하게 답변하려면 4가지가 필요합니다:
1. **FAQ** — 자주 묻는 질문과 답변 목록 (가격, 커리큘럼, 환불, 대상, 학습 시간, 수강 후 지원...).
여기에 직접 붙여넣거나, 파일 경로(.md / .txt / .docx)를 알려주세요.
2. **제품 / 강좌 문서** — 강좌에 대한 상세 설명: 이름, 각 모듈 내용, 학습 성과, 강사, 보너스, 여러 티어의 가격 등.
역시 붙여넣거나 파일 경로를 알려주세요.
3. **브랜드명 + 어조** — 챗봇이 사용할 브랜드/강좌명 (예: "AI Mastery의 어시스턴트"). 사용할 어조: 친근한 존댓말(기본) / 격식체 / 편한 반말.
4. **모델** — 기본값은 `google/gemini-3-flash-preview` (빠르고 저렴하며 지원에 충분히 좋음). `anthropic/claude-sonnet-4.6` (더 높은 품질, 약 10배 비쌈)을 원하시면 말씀해주세요.
한 번에 답변해 주시겠어요?
사용자 응답 시:
- 파일 경로만 제공한 경우 → Read 도구로 해당 파일 읽기.
- 내용을 직접 붙여넣은 경우 → 붙여넣은 내용 사용.
- 사용자가 "플레이스홀더를 자동으로 생성해 줘"라고 말한 경우 → 일반적인 FAQ 플레이스홀더를 생성하고
// TODO: 사용자 입력 필요로 표시하여 사용자가 업데이트해야 함을 알림. - 브랜드명이 없는 경우 → 기본값 "AI 어시스턴트" 및 존댓말 사용.
4단계 — 프로젝트에 파일 생성
이 스킬의 templates/ 디렉토리에 있는 템플릿을 읽고 프로젝트에 맞게 조정합니다. 템플릿 목록:
templates/chatbot-knowledge.ts— 지식 베이스 + systemPrompt를 export하는 모듈. FAQ, 제품 정보, 브랜드명, 어조를 여기에 주입합니다. 응답 형식 지정 블록(마크다운 규칙) + 능동적 리드 수집 블록(LLM이 구매 신호 시 이름/전화번호/이메일을 요청하고, 사용자 제공 시 감사 인사 + 확인을 하되 "데이터베이스에 저장했습니다"라고 말하지 않도록 지시)이 이미 포함되어 있습니다.templates/Chatbot.tsx— 위젯 컴포넌트, Tailwind 우선.react-markdown+remark-gfm을 import하고, 어시스턴트 메시지를<ReactMarkdown remarkPlugins={[remarkGfm]}>로 렌더링합니다 (어시스턴트만 해당 — 사용자 메시지는 일반whitespace-pre-wrap). 프로젝트에 Tailwind가 없는 경우 →templates/Chatbot-noTailwind.tsx+templates/chatbot.module.css로 폴백.templates/chat-route-app.ts— App Router용 API 라우트 (app/api/chat/route.ts). 스트림 종료 후 파이어 앤 포겟 방식으로 실행되는persistLeadIfReady훅이 내장되어 있어 응답을 차단하지 않습니다.templates/chat-route-pages.ts— Pages Router용 API 라우트 (pages/api/chat.ts).templates/chat-lead.ts— 프로젝트에 KV/Upstash가 있는 경우에만 스캐폴딩 — 베트남 전화번호 + 이메일 + 이름 휴리스틱 정규식 추출("tên X" / "tôi là X" / "tên: X" 패턴), 전화번호 기준 중복 제거saveChatLead, TTL 90일, 키 네임스페이스chat-lead:{phone}.templates/chatbot-markdown-styles.css—.chatbot-md클래스용 CSS 스니펫. 반드시app/globals.css(또는src/app/globals.css/styles/globals.css) 끝에 추가해야 합니다. 추가 전에 프로젝트의 브랜드 팔레트에 맞게 강조 색상(#fde68a/#f59e0b)을 변경하세요.templates/env-instructions.md— OpenRouter 키 획득 방법 안내 스니펫, 마지막 단계에서 사용.
템플릿 조정 규칙:
- TS vs JS: 프로젝트에 JS만 있는 경우, 확장자를
.ts/.tsx→.js/.jsx로 변경하고 타입 애너테이션을 제거합니다 (파일명만 변경하는 것이 아님). 샘플 파일을 읽고, 파싱하여 타입을 제거한 후 작성합니다. - 경로 별칭:
tsconfig에서@/*→./src/*로 매핑된 경우, 템플릿의 import는@/lib/chatbot-knowledge를 사용하고 파일은src/lib/에 배치합니다. 별칭이 없으면 상대 경로를 사용합니다. - 모델:
__MODEL_ID__플레이스홀더를 사용자가 선택한 모델(google/gemini-3-flash-preview또는anthropic/claude-sonnet-4.6)로 교체합니다. - 브랜드:
__BRAND_NAME__,__BRAND_TONE__을 교체합니다. - 지식: FAQ + 제품 정보를 명확한 구조로
systemPrompt를 빌드합니다 (템플릿 참조). systemPrompt 내 응답 형식 지정 블록을 유지합니다 — 이 블록이 LLM에게 작은 채팅 말풍선에 적합한 마크다운(헤딩 대신 굵게, 한 줄짜리 짧은 글머리)을 출력하도록 지시합니다. - 마크다운 렌더링 패키지 설치 (필수 — 건너뛰면 채팅 말풍선에
**bold**와 같은 원시 텍스트가 표시됨):# 락파일 감지 → 올바른 패키지 매니저 선택 npm install react-markdown remark-gfm # 또는: pnpm add react-markdown remark-gfm # 또는: yarn add react-markdown remark-gfm - 마크다운 스타일 추가:
templates/chatbot-markdown-styles.css를 읽고, 프로젝트의 브랜드 팔레트에 맞게 강조 색상을 변경한 후 (globals.css에서--color-primary또는 이와 유사한 것을 찾아 확인),app/globals.css(또는src/app/globals.css/styles/globals.css) 끝에 추가합니다. - 위젯 마운트:
- App Router:
app/layout.tsx(또는src/app/layout.tsx)를 편집하여import Chatbot from '@/components/Chatbot'와<Chatbot />을</body>바로 앞에 추가합니다. 편집 전에 기존layout.tsx를 읽어 메타데이터, 폰트, 프로바이더를 유지합니다. - Pages Router:
pages/_app.tsx를 편집하여 return을<><Component {...pageProps} /><Chatbot /></>로 래핑합니다. 기존_app을 읽고 기존 프로바이더는 유지합니다.
- App Router:
- 자동 리드 캡쳐 연결 (2단계에서 KV를 감지한 경우에만 수행):
templates/chat-lead.ts를lib/chat-lead.ts(또는src/lib/chat-lead.ts)로 복사.- 프로젝트에 아직
lib/kv.ts가 없는 경우 → 생성:import { Redis } from "@upstash/redis"; export const kv = Redis.fromEnv(); - 라우트 핸들러에 import 추가:
import { extractLeadFromMessages, saveChatLead } from "@/lib/chat-lead". - 스트림 루프에서:
assistantText를 추적(델타 누적), 스트림 종료 시persistLeadIfReady(history, assistantText)호출 (파이어 앤 포겟, 응답 경로에서 await하지 않음). - 시스템 프롬프트:
chatbot-knowledge.ts에 능동적 리드 수집 블록이 포함되어 있는지 확인 (템플릿에 이미 있음). - 사용자에게 확인: "자동 리드 캡쳐가 활성화되었습니다. 고객이 채팅에 이름/전화번호/이메일을 입력하면 시스템이 자동으로 KV 네임스페이스
chat-lead:{전화번호}에 저장합니다./admin에서 리드를 확인하세요 (/biz-admin-leads-dashboard가 설치된 경우)."
5단계 — .env.local + .gitignore 설정
-
.env.local이 이미 존재하는지 확인:- 없음: 새로 생성:
# OpenRouter API 키 — https://openrouter.ai/keys 에서 발급 OPENROUTER_API_KEY= # 선택 사항: OpenRouter 추적용 사이트 URL (레이트 리밋에 도움) NEXT_PUBLIC_SITE_URL=http://localhost:3000 - 있음: 위 2개 변수가 없으면 추가하고, 기존 변수는 덮어쓰지 않음.
- 없음: 새로 생성:
-
.env.example업데이트 (없으면 새로 생성):OPENROUTER_API_KEY=sk-or-v1-xxxxx NEXT_PUBLIC_SITE_URL=https://your-domain.com -
.gitignore에.env.local이 있는지 확인. 없으면 →.env.local및.env*.local추가.
6단계 — 사용자에게 보고
4개 부분으로 구성된 명확한 요약 메시지 출력:
✅ [프로젝트명]에 챗봇 설치 완료
📁 생성된 파일:
- components/Chatbot.tsx (react-markdown + remark-gfm으로 마크다운 렌더링)
- app/api/chat/route.ts
- lib/chatbot-knowledge.ts
- app/globals.css (마크다운 스타일 .chatbot-md 추가)
- .env.local (플레이스홀더)
📦 설치된 패키지: react-markdown, remark-gfm
🔑 챗봇 실행을 위해 사용자가 직접 해야 할 3단계:
1. OpenRouter API 키 발급:
- https://openrouter.ai 방문 → 회원가입 (Google 로그인 가능)
- https://openrouter.ai/keys 방문 → 키 생성
- 크레딧 충전 (https://openrouter.ai/credits) — Gemini 3 Flash는 저렴, $5면 수천 번의 채팅 가능
- `sk-or-v1-xxxxx` 형식의 키 복사
2. `.env.local` 파일에 붙여넣기:
OPENROUTER_API_KEY=sk-or-v1-xxxxx-여기에-붙여넣기
3. 테스트 실행:
[npm/pnpm/yarn] run dev
http://localhost:3000 열기 → 우측 하단 말풍선 클릭
🔄 Gemini ↔ Sonnet 모델 변경:
`app/api/chat/route.ts`를 열고 파일 상단의 `model:` 변수를 수정하세요.
- `google/gemini-3-flash-preview` (기본값, $0.075/백만 입력 토큰, 빠름)
- `anthropic/claude-sonnet-4.6` (고급형, ~$3/백만 입력 토큰, 더 나은 품질)
📚 추후 지식 베이스 업데이트:
`lib/chatbot-knowledge.ts` 편집 — FAQ 및 productInfo를 추가/수정하고 저장하면 핫 리로드됩니다.
모바일 우선 반응형 디자인
위젯은 3가지 중단점을 충족해야 합니다:
| 중단점 | 동작 |
|---|---|
| ≤ 640px (모바일) | 말풍선 클릭 → 전체 화면 오버레이 열림 (z-index 9999), 헤더에 X 닫기 버튼. 키보드에 가려지지 않도록 safe-area-inset으로 입력창 하단 고정. |
| 641–1023px (태블릿) | 패널 380px × 70vh, 우측 하단에 16px 여백으로 고정. |
| ≥ 1024px (데스크탑) | 패널 400px × 600px, 우측 하단에 24px 여백으로 고정. 말풍선 60px 원형. |