블로그워드프레스에 AI 고객지원 챗봇 만들기 (1) — 설계와 규칙 기반 엔진

워드프레스에 AI 고객지원 챗봇 만들기 (1) — 설계와 규칙 기반 엔진

사이트에 서비스 페이지가 50개를 넘어가면서 문제가 생겼다. 방문자가 원하는 걸 못 찾는다. 사이트맵 페이지를 만들어놨지만 누가 그걸 보겠나. 검색 기능도 있지만 “사주 보고 싶은데” 같은 자연어는 처리하지 못한다. 결국 이탈률만 높아진다.

챗봇을 달면 해결될 것 같았다. 근데 외부 서비스를 쓰자니 월 2~3만원은 기본이고, ChatGPT API를 바로 붙이면 트래픽 늘 때 비용 폭탄을 맞는다. 그래서 직접 만들기로 했다. 비용 0원으로 시작해서 단계적으로 AI를 붙이는 전략이다.

3단계 아키텍처 — 왜 이렇게 설계했나

처음부터 AI를 쓰면 안 되는 이유가 있다. 첫째, 비용. 둘째, 속도. 셋째, 정확도. “타로 보고 싶어요” 같은 명확한 요청에 GPT를 호출할 이유가 없다. URL 하나 던져주면 끝인데 3초씩 기다리게 할 필요가 있을까?

그래서 3단계로 설계했다:

단계처리 방식비용응답 속도
1단계규칙 기반 키워드 매칭 (JS)0원즉시 (~0.1초)
2단계WordPress DB 검색 (PHP)0원빠름 (~0.3초)
3단계Ollama/Claude AI (PHP)0원 또는 유료느림 (3~15초)

사용자 메시지가 들어오면 먼저 JS에서 키워드를 매칭한다. “운세”라는 단어가 들어있으면 운세 서비스 목록을 바로 보여준다. 매칭 실패하면 서버로 보내서 WordPress DB 검색을 한다. 검색에서도 결과가 없으면 그때서야 AI를 호출한다.

이 구조의 핵심은 대부분의 질문이 1단계에서 끝난다는 것이다. 실제 로그를 보면 70% 이상이 규칙 기반으로 처리된다. AI 호출 없이.

mu-plugin으로 만드는 이유

일반 플러그인 대신 mu-plugin(Must-Use Plugin)을 사용했다. 경로는 wp-content/mu-plugins/chatbot.php. 이렇게 하면 관리자 화면에서 실수로 비활성화하는 일이 없고, 테마 교체에도 영향을 안 받는다. 파일 하나에 PHP + CSS + JS를 다 넣어서 의존성도 없다.

<?php
/**
 * Plugin Name: Chatbot - 고객 지원 챗봇
 * Description: 규칙 기반 FAQ 챗봇 위젯 (비용 0원, 키워드 매칭 방식)
 * Version: 1.0.0
 * Author: nalkkul
 */

if (!defined('ABSPATH')) exit;

knowledgeBase — 규칙 기반 엔진의 심장

규칙 기반 엔진은 JS 객체 하나로 돌아간다. knowledgeBase라는 객체에 카테고리별로 키워드와 응답을 정의해 놓으면 된다. 구조는 이렇다:

var knowledgeBase = {
    fortune: {
        keywords: ["운세", "타로", "사주", "궁합", "점", "운명"],
        response: "운세 서비스를 이용해보세요! 🔮\n\n🃏 타로 카드 리딩 → /tarot/\n🏛️ 사주팔자 풀이 → /saju/\n💕 궁합 보기 → /gunghap/\n💑 이름 궁합 → /name-match/\n\n모두 무료입니다!",
        links: [
            {text: "🃏 타로 보러가기", url: "/tarot/"},
            {text: "🏛️ 사주 보러가기", url: "/saju/"},
            {text: "💕 궁합 보러가기", url: "/gunghap/"}
        ]
    },
    calculator: {
        keywords: ["계산기", "계산", "연봉", "대출", "퇴직금", "bmi", "학점", "글자수", "단위", "환율", "연차", "칼로리"],
        response: "다양한 계산기를 이용하세요! 🧮\n\n💰 연봉 실수령액 → /salary-calculator/\n🏦 대출 이자 → /loan-calculator/\n...",
        links: [
            {text: "💰 연봉 계산기", url: "/salary-calculator/"},
            {text: "🏦 대출 계산기", url: "/loan-calculator/"}
        ]
    },
    // ... 16개 카테고리
};

각 카테고리에는 세 가지 속성이 있다:

  • keywords — 이 카테고리를 트리거하는 단어 배열. “운세”든 “사주”든 하나만 걸리면 매칭된다.
  • response — 보여줄 텍스트. URL을 포함하면 자동으로 클릭 가능한 링크로 변환된다.
  • links / buttons — 응답 아래에 표시할 바로가기 링크 또는 추가 질문 버튼.

현재 정의된 16개 카테고리를 정리하면:

카테고리키워드 예시서비스 수
services (전체)서비스, 뭐 있, 할 수 있전체 안내
fortune (운세)운세, 타로, 사주, 궁합4개
quiz (심리테스트)심리, 테스트, mbti, 성격5개
game (게임)게임, 월드컵, 끝말, 야구7개
calculator (계산기)계산기, 연봉, 대출, bmi8개
info (정보조회)미세먼지, 약국, 증시, 주식11개
education (학습)맞춤법, 상식, 퀴즈, 사자성어5개
utility (유틸리티)qr, 비밀번호, 색상, 닉네임7개
community (커뮤니티)방명록, 투표, 의견2개
auth (회원)회원가입, 로그인, 비밀번호 찾기4개
blog (블로그)블로그 보기, 포스트 목록블로그 링크
greeting (인사)안녕, 하이, hello, 반가환영 메시지
thanks (감사)감사, 고마, 땡큐응답
contact (문의)문의, 연락, 이메일, 상담문의 폼 표시
about (소개)사이트, 소개, 누가 만들사이트 정보
help (도움)도움, help, 모르겠, 사용법사용 가이드

findResponse() — 매칭 로직의 핵심

사용자 입력이 들어오면 findResponse() 함수가 knowledgeBase를 순회하면서 키워드를 찾는다. 코드를 보자:

function findResponse(userMessage) {
    var msg = userMessage.toLowerCase().trim();

    // 검색/추천 의도가 있으면 AI로 바로 보냄 (규칙 기반 스킵)
    var searchIntents = ["검색", "찾아", "추천", "관련 글", "관련된", "최근 글", 
                         "최신 글", "올라온 글", "업데이트", "뭐 있어", "볼만한", "읽을만한"];
    for (var si = 0; si < searchIntents.length; si++) {
        if (msg.indexOf(searchIntents[si]) !== -1 && AI_ENGINE) {
            return null; // AI로 전달
        }
    }

    for (var key in knowledgeBase) {
        if (!knowledgeBase.hasOwnProperty(key)) continue;
        var data = knowledgeBase[key];
        for (var i = 0; i < data.keywords.length; i++) {
            if (msg.indexOf(data.keywords[i].toLowerCase()) !== -1) {
                return data;
            }
        }
    }

    // No match → return null to trigger AI
    if (AI_ENGINE) return null;

    // No AI engine → static fallback
    return {
        response: "죄송합니다, 정확히 이해하지 못했어요. 😅\n\n다음 중 선택해보세요:",
        buttons: [
            {text: "🔮 운세", action: "운세"},
            {text: "🧮 계산기", action: "계산기"},
            {text: "🎮 게임", action: "게임"},
            {text: "📊 정보조회", action: "정보조회"},
            {text: "📧 문의하기", action: "문의"}
        ]
    };
}

여기서 중요한 포인트가 두 가지 있다.

첫째, 검색 의도 우선 처리. "여행 관련 글 추천해줘" 같은 메시지는 키워드 매칭으로 처리하면 안 된다. "추천"이라는 단어가 검색 의도를 가지고 있으니까 서버로 보내서 DB 검색을 해야 한다. 그래서 searchIntents 배열에 검색/추천 관련 단어를 모아두고, 이 단어가 감지되면 규칙 기반을 건너뛰고 바로 null을 리턴한다.

둘째, AI 엔진 유무에 따른 폴백. AI 엔진이 설정되어 있으면(AI_ENGINE이 truthy) 매칭 실패 시 null을 리턴해서 AI를 호출한다. AI 엔진이 없으면 버튼들을 표시해서 사용자를 안내한다. 비용 0원 모드에서도 챗봇이 멈추지 않고 동작하는 이유가 이거다.

handleUserInput() — 1단계와 3단계의 분기점

사용자가 메시지를 보내면 handleUserInput()이 호출된다. 여기서 규칙 기반과 AI의 분기가 일어난다:

function handleUserInput(text) {
    if (!text || !text.trim()) return;
    text = text.trim();

    // Show user message
    addMessage(text, 'user');

    // Try rule-based first
    var result = findResponse(text);

    if (result !== null) {
        // Rule-based match found — log it
        var logFd = new FormData();
        logFd.append('action', 'nalkkul_chatbot_log');
        logFd.append('nonce', NONCE);
        logFd.append('question', text);
        logFd.append('answer', result.response.substring(0, 200));
        fetch(AJAX_URL, {method: 'POST', body: logFd});

        var typing = showTyping();
        setTimeout(function() {
            removeTyping();
            addMessage(result.response, 'bot', {
                buttons: result.buttons,
                links: result.links,
                showContactForm: result.showContactForm
            });
        }, 400 + Math.random() * 400);
    } else {
        // No match → call AI
        callAI(text);
    }
}

규칙 기반으로 매칭되면 서버에 로그만 남기고 즉시 응답한다. setTimeout으로 400~800ms 랜덤 딜레이를 주는 건 진짜 생각하고 있는 것처럼 보이게 하는 트릭이다. 즉시 응답하면 오히려 기계적으로 느껴진다.

매칭 실패하면 callAI(text)로 서버에 AJAX 요청을 보낸다. 이건 다음 파트에서 자세히 다룬다.

규칙 기반의 한계 — 솔직한 이야기

몇 달 운영해보니 한계가 명확하다:

  • 자연어 이해 불가. "오늘 뭐 재미있는 거 없나" 같은 질문은 어디에도 매칭되지 않는다. 키워드가 하나도 안 걸린다.
  • 수동 업데이트. 새 서비스 페이지를 추가하면 knowledgeBase도 수동으로 수정해야 한다. 까먹으면 챗봇이 안내 못 한다.
  • 동의어 처리 불가. "돈 계산" → 연봉 계산기? 대출 계산기? "뚱뚱한지 궁금" → BMI 계산기? 이런 건 절대 못 잡는다.
  • 맥락 무시. 대화의 흐름을 이해하지 못한다. "그거 말고 다른 거" 같은 후속 질문은 처리 불가.

하지만 장점도 확실하다. 비용 0원, 응답 속도 0.1초 미만, 서버 부하 없음, 100% 정확한 응답 (매칭되면). 대부분의 사이트에서 FAQ 수준의 챗봇은 이것만으로 충분하다. "사주 보고 싶어요" → 사주 페이지 링크. 이게 전부인 사이트가 많다.

다음 단계

규칙 기반 엔진은 뼈대다. 다음 글에서는 이 엔진 위에 올라가는 플로팅 UI와 대화 인터페이스를 구현한다. 채팅 창 디자인, 타이핑 인디케이터, 다크모드, 모바일 전체화면 대응까지. 코드를 파일 하나에 전부 넣는 방법도 다룬다.


이 시리즈의 다른 글

이 글이 도움이 되셨다면 공유해주세요!

댓글 남기기

무엇이든 물어보세요! 💬