Skip to content

Lightweight Headless Chatbot Engine for React. Features JSON-driven scenarios, multi-session support, and flexible state adapters.

License

Notifications You must be signed in to change notification settings

Nago730/chatbot-library

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

25 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@nago730/chatbot-library

JSON ν•˜λ‚˜λ‘œ λ§Œλ“œλŠ” ν”„λ‘œλ•μ…˜ λ ˆλ”” 챗봇 μ—”μ§„ β€” React ν™˜κ²½μ—μ„œ λ³΅μž‘ν•œ λŒ€ν™”ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 5λΆ„ μ•ˆμ— κ΅¬μΆ•ν•˜μ„Έμš”.

npm version license downloads


🎯 핡심 κΈ°λŠ₯ 3κ°€μ§€

κΈ°λŠ₯ μ„€λͺ… 효과
πŸ—‚οΈ JSON 기반 μ‹œλ‚˜λ¦¬μ˜€ μ½”λ“œ 없이 λŒ€ν™” 흐름 섀계 개발 μ‹œκ°„ 90% 단좕
πŸ”„ λ©€ν‹° μ„Έμ…˜ 관리 ν•œ μ‚¬μš©μžκ°€ μ—¬λŸ¬ 상담 μ§„ν–‰ μ‚¬μš©μž κ²½ν—˜ ν–₯상
πŸ”₯ ν”„λ‘œλ•μ…˜ λ ˆλ”” Firebase 연동 + λΉ„μš© μ΅œμ ν™” 운영 λΉ„μš© 98% 절감

⚑ 5λΆ„ λΉ λ₯Έ μ‹œμž‘

1. μ„€μΉ˜

npm install @nago730/chatbot-library

2. Flow μ •μ˜ (JSON)

const SUPPORT_FLOW = {
  start: {
    id: 'start',
    question: '무엇을 λ„μ™€λ“œλ¦΄κΉŒμš”?',
    type: 'button',
    options: ['μ£Όλ¬Έ 문의', '배솑 쑰회', 'μ·¨μ†Œ/ν™˜λΆˆ'],
    next: (answer) => {
      if (answer === '주문 문의') return 'order';
      if (answer === '배솑 쑰회') return 'delivery';
      return 'refund';
    }
  },
  order: {
    id: 'order',
    question: '주문번호λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”',
    type: 'input',
    next: 'complete'
  },
  complete: {
    id: 'complete',
    question: 'κ°μ‚¬ν•©λ‹ˆλ‹€. κ³§ μ—°λ½λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.',
    next: '',
    isEnd: true
  }
};

3. μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ‚¬μš©

import { useChat } from '@nago730/chatbot-library';

function ChatBot() {
  const { node, submitAnswer, submitInput, messages, isEnd } = useChat(
    SUPPORT_FLOW,
    'user_123'
  );

  if (isEnd) {
    return <div>βœ… {node.question}</div>;
  }

  return (
    <div>
      {/* λŒ€ν™” νžˆμŠ€ν† λ¦¬ */}
      {messages.map((msg, i) => (
        <div key={i}>
          <p>πŸ€– {msg.question}</p>
          <p>πŸ‘€ {msg.answer}</p>
        </div>
      ))}

      {/* ν˜„μž¬ 질문 */}
      <p>{node.question}</p>

      {/* λ²„νŠΌν˜• */}
      {node.type === 'button' && node.options?.map(opt => (
        <button key={opt} onClick={() => submitAnswer(opt)}>
          {opt}
        </button>
      ))}

      {/* μž…λ ₯ν˜• */}
      {node.type === 'input' && (
        <input onKeyDown={(e) => {
          if (e.key === 'Enter') submitInput(e.currentTarget.value);
        }} />
      )}
    </div>
  );
}

πŸŽ‰ μ™„λ£Œ! 이제 μž‘λ™ν•˜λŠ” 챗봇이 μƒκ²ΌμŠ΅λ‹ˆλ‹€.


πŸ“š 핡심 κ°œλ…

Flow ꡬ쑰

FlowλŠ” λ…Έλ“œ(Node)의 μ§‘ν•©μž…λ‹ˆλ‹€. 각 λ…Έλ“œλŠ” 질문과 λ‹€μŒ 단계λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.

interface ChatNode {
  id: string;                    // 고유 ID
  question: string;              // μ‚¬μš©μžμ—κ²Œ 보여쀄 질문
  type?: 'button' | 'input';     // λ‹΅λ³€ λ°›λŠ” 방식 (κΈ°λ³Έ: button)
  options?: string[];            // 선택지 (type='button'일 λ•Œ)
  next: string | ((answer) => string);  // λ‹€μŒ λ…Έλ“œ ID (동적 κ°€λŠ₯)
  isEnd?: boolean;               // λŒ€ν™” μ’…λ£Œ ν‘œμ‹œ
}

μ„Έμ…˜ 관리

ν•œ μ‚¬μš©μžκ°€ μ—¬λŸ¬ 번 상담을 μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const { sessionId, reset } = useChat(FLOW, userId, 'start', adapter, {
  sessionId: 'auto'  // 'auto' | 'new' | 'specific_id'
});

// μƒˆ 상담 μ‹œμž‘
<button onClick={() => reset()}>μƒˆ 상담</button>

μ €μž₯ μ „λž΅

const chat = useChat(FLOW, userId, 'start', adapter, {
  saveStrategy: 'onEnd'  // 'always' | 'onEnd'
});
μ „λž΅ μ €μž₯ μ‹œμ  μΆ”μ²œ λŒ€μƒ
'always' λ§€ λ‹΅λ³€λ§ˆλ‹€ 데이터 무결성이 μ€‘μš”ν•œ 경우
'onEnd' λŒ€ν™” μ’…λ£Œ μ‹œ λΉ„μš© 절감 (ꢌμž₯)

πŸ”₯ Firebase 연동 (ν”„λ‘œλ•μ…˜)

Quick Start

import { createHybridFirebaseAdapter } from '@nago730/chatbot-library/examples';
import { getFirestore } from 'firebase/firestore';

const db = getFirestore(app);
const adapter = createHybridFirebaseAdapter(db, {
  timeout: 5000,
  fallbackToLocal: true,
  debug: false
});

const chat = useChat(FLOW, userId, 'start', adapter, {
  saveStrategy: 'onEnd'  // λΉ„μš© 98% 절감!
});

λΉ„μš© μ΅œμ ν™”

10만 μ‚¬μš©μž, 일 10회 λŒ€ν™” κΈ°μ€€ (Firestore)

ꡬ성 μ›” λΉ„μš© 절감율
κΈ°λ³Έ μ„€μ • (always + 전체 데이터) $2,700 -
ν•˜μ΄λΈŒλ¦¬λ“œ + onEnd ⭐ $5.4 99.8%

핡심 κ°œμ„ μ‚¬ν•­

  • βœ… κΈ°κΈ° μ „ν™˜ 볡ꡬ: PC β†’ λͺ¨λ°”일 λŒ€ν™” 이어가기 100%
  • βœ… λ„€νŠΈμ›Œν¬ μ•ˆμ •μ„±: νƒ€μž„μ•„μ›ƒ + μžλ™ 폴백
  • βœ… νƒ€μž… μ•ˆμ „: Firebase Timestamp μžλ™ μ •κ·œν™”
  • βœ… λΉ„μš© μ΅œμ ν™”: 슀마트 μ €μž₯ μ „λž΅

πŸ“– Firebase 상세 κ°€μ΄λ“œ


πŸ”„ λ©€ν‹° μ„Έμ…˜

ν•œ μ‚¬μš©μžκ°€ μ—¬λŸ¬ 상담을 μ§„ν–‰ν•˜κ³  이전 λŒ€ν™”λ₯Ό 뢈러올 수 μžˆμŠ΅λ‹ˆλ‹€.

const { sessionId, reset, isEnd } = useChat(FLOW, userId, 'start', adapter, {
  sessionId: 'auto'
});

// UI 예제
<div>
  <p>ν˜„μž¬ μ„Έμ…˜: {sessionId}</p>
  
  {isEnd && (
    <button onClick={() => reset()}>
      μƒˆ 상담 μ‹œμž‘
    </button>
  )}
  
  <button onClick={() => reset('session_1706000000_abc')}>
    이전 상담 보기
  </button>
</div>

πŸ“– λ©€ν‹° μ„Έμ…˜ μ™„λ²½ κ°€μ΄λ“œ


πŸ“– API Reference

useChat

useChat(
  flow: Record<string, ChatNode>,
  userId: string,
  initialNodeId?: string,
  adapter?: StorageAdapter,
  options?: ChatOptions
)

Parameters

νŒŒλΌλ―Έν„° νƒ€μž… μ„€λͺ…
flow Record<string, ChatNode> μ‹œλ‚˜λ¦¬μ˜€ Flow 객체
userId string μ‚¬μš©μž ID (μ„Έμ…˜ ν‚€λ‘œ μ‚¬μš©)
initialNodeId string μ‹œμž‘ λ…Έλ“œ ID (κΈ°λ³Έ: 'start')
adapter StorageAdapter μ €μž₯μ†Œ μ–΄λŒ‘ν„° (선택)
options ChatOptions μΆ”κ°€ μ˜΅μ…˜ (선택)

ChatOptions

interface ChatOptions {
  saveStrategy?: 'always' | 'onEnd';  // μ €μž₯ μ‹œμ 
  scenarioId?: string;                 // μ‹œλ‚˜λ¦¬μ˜€ ID
  sessionId?: 'auto' | 'new' | string; // μ„Έμ…˜ μ „λž΅
}

Return Values

{
  node: ChatNode;              // ν˜„μž¬ λ…Έλ“œ
  submitAnswer: (value: any) => Promise<void>;  // λ²„νŠΌ λ‹΅λ³€ 제좜
  submitInput: (value: string) => Promise<void>; // ν…μŠ€νŠΈ λ‹΅λ³€ 제좜
  answers: Record<string, any>;  // μˆ˜μ§‘λœ λ‹΅λ³€
  messages: ChatMessage[];       // λŒ€ν™” νžˆμŠ€ν† λ¦¬
  isEnd: boolean;                // μ’…λ£Œ μ—¬λΆ€
  sessionId: string;             // ν˜„μž¬ μ„Έμ…˜ ID
  reset: (sessionId?: string) => void;  // μ„Έμ…˜ 리셋
}

StorageAdapter

interface StorageAdapter {
  saveState: (userId: string, state: ChatState) => Promise<void>;
  loadState: (userId: string) => Promise<ChatState | null>;
}

πŸ“š 전체 λ¬Έμ„œ

κ°€μ΄λ“œ

ν•™μŠ΅ 자료


⚠️ Common Pitfalls

개발 μ‹œ 자주 λ°œμƒν•˜λŠ” μ‹€μˆ˜λ“€:

  1. ❌ sessionId 없이 λ©€ν‹° 상담 κ΅¬ν˜„ β†’ reset() μ‚¬μš©ν•˜μ„Έμš”
  2. ❌ saveStrategy: 'always' + μ‹€μ‹œκ°„ 타이핑 β†’ 'onEnd' μ‚¬μš© ꢌμž₯
  3. ❌ Firebase Timestamp μ •κ·œν™” λˆ„λ½ β†’ μ–΄λŒ‘ν„° 예제 μ½”λ“œ μ‚¬μš©
  4. ❌ μ—λŸ¬ 핸듀링 μ—†μŒ β†’ fallbackToLocal: true μ„€μ • ν•„μˆ˜

πŸ“– 전체 Best Practices 보기


πŸš€ μ‹€μ „ 예제

고객 지원 챗봇

const SUPPORT_FLOW = {
  start: { /* ... */ },
  order_inquiry: { /* ... */ },
  delivery_status: { /* ... */ },
  refund: { /* ... */ }
};

function CustomerSupport() {
  const { node, submitAnswer, reset, sessionId } = useChat(
    SUPPORT_FLOW,
    customerId,
    'start',
    firebaseAdapter,
    { sessionId: 'auto', saveStrategy: 'onEnd' }
  );
  
  return <ChatUI node={node} onAnswer={submitAnswer} onReset={reset} />;
}

더 λ§Žμ€ 예제: Examples


πŸ› οΈ νƒ€μž… μ •μ˜

// ChatNode
interface ChatNode {
  id: string;
  question: string;
  type?: 'button' | 'input';
  options?: string[];
  next: string | ((answer: any) => string);
  isEnd?: boolean;
}

// ChatMessage
interface ChatMessage {
  nodeId: string;
  question: string;
  answer: any;
  timestamp: number;
}

// ChatState
interface ChatState {
  answers: Record<string, any>;
  currentStep: string;
  messages: ChatMessage[];
  flowHash: string;
  updatedAt: number;
}

🀝 κΈ°μ—¬ν•˜κΈ°

이 λΌμ΄λΈŒλŸ¬λ¦¬λŠ” ν”„λ¦¬λžœμ„œ μ™Έμ£Ό μž‘μ—…μ„ ν•˜λ©° λ°˜λ³΅λ˜λŠ” 챗봇 κ΅¬ν˜„μ— 지쳐 λ§Œλ“€μ–΄μ‘ŒμŠ΅λ‹ˆλ‹€.
AI 기반 κ°œλ°œμ— μ΅œμ ν™”λœ λ¬Έμ„œλ₯Ό λͺ©ν‘œλ‘œ ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

  • ⭐ Star ν•˜λ‚˜κ°€ 개발 동기뢀여가 λ©λ‹ˆλ‹€!
  • πŸ› 버그 제보: Issues
  • πŸ’‘ κΈ°λŠ₯ μ œμ•ˆ: Issues (κΈ°λŠ₯ μ œμ•ˆλ„ ν™˜μ˜ν•©λ‹ˆλ‹€!)

πŸ“„ λΌμ΄μ„ μŠ€

MIT License


Made with ❀️ for Vibe Coders β€” AI μ‹œλŒ€μ˜ 더 λ‚˜μ€ 개발 κ²½ν—˜μ„ μœ„ν•΄

About

Lightweight Headless Chatbot Engine for React. Features JSON-driven scenarios, multi-session support, and flexible state adapters.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published