Facebook Chatbot
Messenger 기반 고객 응대 자동화 봇
시스템 아키텍처
구조내부 운영용으로 구축한 규칙 기반 메시징 어시스턴트입니다. 수신 메시지를 분류하고 정책에 매칭한 뒤, UI/제어(데스크톱), 자동화 로직, 영속성/내보내기 계층을 명확히 분리해 예측 가능한 흐름으로 라우팅합니다.
도메인 원칙
원칙목표는 실제 운영 환경의 변동성 속에서도 일관된 동작을 유지하는 것입니다. 메시지 분류는 결정적으로 수행되고, 정책 조회는 중앙화되며, 불확실한 경우에는 침묵적으로 추측하지 않고 명시적인 폴백(질문 감지/에스컬레이션) 경로로 전환합니다.
Impact: 반복적인 수동 응답을 줄이고 더 빠르고 일관된 고객 응답을 가능하게 했습니다. 또한 정책(키워드/템플릿)은 데이터로 관리되어, 핵심 오케스트레이션 로직을 크게 수정하지 않고도 동작을 점진적으로 개선할 수 있습니다.
Trade-offs: 중앙화된 정책 엔진은 소규모 내부 도구에선 운영이 단순하지만, 정책이 늘어나면 복잡도가 증가할 수 있습니다. 향후 개선에서는 전략(Strategy) 핸들러와 더 강한 타입의 메시지 의도 모델을 도입해 관심사를 분리하고 확장성을 높일 수 있습니다.
정책 우선 워크플로우
메시지는 예측 가능한 파이프라인으로 처리됩니다: 분류 → 정책 매칭 → 응답 → 로깅 → 필요 시 이관(에스컬레이션).
명시적 폴백
확신 있는 매칭이 없으면 임의로 추측하지 않고, 질문 감지 또는 에스컬레이션 등 명시적인 폴백 경로로 전환합니다.
스토리지 경계
영속성 및 내보내기(SQLite/CSV/Excel)는 헬퍼 계층 뒤로 격리하여, 정책 로직이 읽기 쉽고 변경에 안전하도록 유지합니다.
운영 중심
실제 운영을 전제로 설계했습니다: 예측 가능한 규칙, 로그 기반 관찰 가능성, 그리고 사람이 개입할 수 있는 안전한 이관.
Example: PocketQuest와 동일한 경계 원칙을 적용했습니다. 오케스트레이션 흐름은 간결하게 유지하고, 인프라 세부사항(Selenium/SMTP/저장소)은 헬퍼 계층 뒤에 격리했습니다.
코드 발췌
코드다음 발췌문은 메시징 오케스트레이션의 핵심 경계를 담고 있습니다: 결정적 분기, 중앙화된 정책 조회, 격리된 인프라 헬퍼, 그리고 명시적인 담당자 이관 경로.
오케스트레이터 분기 (핵심 정책 흐름)
핵심 워크플로우: 구조화된 세부사항 추출, 결정적 분기, 결과 로깅, 필요 시 담당자에게 이관.
// src/models/Automate.java (excerpt)
HashMap<String, String> details = Util.fetchDetails(customerMsg);
String email = details.get("email");
String phone = Util.formatPhoneNumber(details.get("number"));
if(!email.contentEquals("")) {
String reply = BotAnswers.getInstance()
.getAnswerHasEmail()
.replace(AnswerReferences.AGENT_NAME.toString(), agentName)
.replace(AnswerReferences.AGENT_NUMBER.toString(), agentNumber);
sendMessage(reply);
ExcelHelper.saveMessage(name, customerMsg);
Email.send(record.getString("subject"),
record.getString("email"),
mailText);
return;
}
// Keyword-based response
String msg = DBHelper.getKeywordBasedMessage(itemId, customerMsg);
if(msg != null && !totalMessage.contains(msg)) {
sendMessage(msg);
return;
}
// Question fallback → escalate
if(customerMsg.contains("?")) {
Email.send("Need Manual Reply", agentEmail, customerMsg);
}
정책 조회 엔진 (AND/OR/단일/전역)
규칙이 명확한 우선순위로 평가되어 정책 진화 시에도 동작이 예측 가능하게 유지됩니다.
// src/storages/DBHelper.java (excerpt)
public static String getKeywordBasedMessage(String itemId, String keyword)
throws SQLException {
String response;
response = getAndFromTagBased(itemId, keyword);
if(!response.isEmpty()) return response;
response = getOrFromTagBased(itemId, keyword);
if(!response.isEmpty()) return response;
response = getOnlyOneTagBased(itemId, keyword);
if(!response.isEmpty()) return response;
return getFromAllTagBased(keyword);
}
SQLite PreparedStatement (정책은 데이터에 유지)
키워드 정책이 SQLite에 있어 오케스트레이션 코드 변경 없이 동작을 업데이트할 수 있습니다.
// src/storages/DBHelper.java (excerpt)
PreparedStatement ps = DB.getInstance().prepareStatement(
"SELECT * FROM keyword " +
"WHERE ? LIKE '%'||keyword.keyword||'%' " +
"AND product_id = ?"
);
인프라 경계: ChromeDriver 생명주기
WebDriver 생성/설정이 중앙화되어 오케스트레이션을 읽기 쉽고 복원력 있게 유지합니다.
// src/models/apis/ChromeDriverHelper.java (excerpt)
if(conn == null) {
System.setProperty(WEB_DRIVER_ID, WEB_DRIVER_PATH);
ChromeOptions options = new ChromeOptions();
options.addArguments("--disable-extensions");
options.addArguments("--ignore-certificate-errors");
conn = new ChromeDriver(options);
}
return conn;
의미 있는 실패 시맨틱 (래퍼 + 커스텀 예외)
Selenium 실패가 도메인 수준 예외로 변환되어 실패가 명시적이고 진단 가능해집니다.
// src/models/utils/Util.java (excerpt)
public static WebElement findElement(FindElementBy by, String target)
throws ElementNotFoundException {
try {
WebDriver driver = ChromeDriverHelper.getInstance().getConnection();
return driver.findElement(By.xpath(target));
} catch (Exception e) {
throw new ElementNotFoundException(
Util.getStackTrace(Thread.currentThread().getStackTrace())
);
}
}
이 발췌문들은 시스템 우선 설계를 보여줍니다: 결정적 분기가 있는 단일 오케스트레이터, 데이터 기반 정책 계층(SQLite), 격리된 인프라 헬퍼(Selenium/SMTP), 그리고 낮은 신뢰도 사례를 위한 명시적 에스컬레이션 경로.
Java 데스크톱 도구이지만, 이 경험은 이후 풀스택 작업에도 그대로 이어졌습니다. 명시적 경계, 중앙화된 정책, 운영과 유지보수가 쉬운 예측 가능한 동작이라는 원칙을 일관되게 적용합니다.