SWE로서 레시피 Agent 시스템을 직접 구축한 경험을 공유합니다.
Perry • Software Enginner
안녕하세요, 채널톡 백엔드 엔지니어 페리입니다.
빠르게 성장하는 SaaS 서비스는 필연적으로 사용자 경험 최적화 문제에 직면하게 됩니다. 채널톡 역시 다양한 워크플로우와 마케팅 캠페인 템플릿인 레시피 시스템을 제공하고 있지만, 사용자가 수십 개의 옵션 중에서 자신에게 적합한 레시피를 찾는 것은 쉽지 않은 일이었습니다. 특히 워크플로우 레시피 50개와 활성화된 워크플로우 60,000개, 마케팅 레시피 300여개와 활성화된 마케팅 캠페인 15,000개 라는 방대한 데이터 속에서 개인화된 추천을 제공하는 것은 기존 방식으로는 한계가 명확했습니다.
이 글에서는 채널톡이 이러한 확장성 문제를 해결하기 위해 Google의 Agent2Agent(a2a) 프레임워크를 도입하여 AI 기반 레시피 추천 시스템을 구축한 과정을 공유하고자 합니다. 특히 머신러닝 엔지니어(MLE)가 아닌 소프트웨어 엔지니어(SWE) 관점에서 프로덕션 AI 시스템을 직접 구축한 경험과 인사이트를 중점적으로 다루겠습니다.
채널톡의 레시피 시스템은 고객사가 효율적인 고객 응대와 마케팅 자동화를 구현할 수 있도록 돕는 템플릿 집합입니다. 워크플로우 레시피는 챗봇 시나리오, IVR 설계, 상담원 업무 자동화 등의 패턴을 제공하며, 마케팅 레시피는 고객 세그먼테이션, 캠페인 메시지 구성, 전송 타이밍 최적화 등의 템플릿을 포함합니다.
하지만 기존 시스템은 다음과 같은 구조적 한계를 가지고 있었습니다:
발견성 문제: 사용자가 어떤 레시피가 존재하는지 쉽게 파악하기 어려운 UI/UX 구조
적합성 판단 어려움: 고객의 비즈니스 특성에 맞는 레시피를 식별하기 위한 명확한 가이드라인 부재
효과 예측 불가: 레시피 사용 시 예상되는 성과나 ROI에 대한 정보 부족
실제로 고객 피드백에서도 "워크플로우가 어렵다", "마케팅 설정이 복잡하다"는 의견이 지속적으로 제기되고 있었으며, 이는 레시피 활용도 저하로 이어지고 있었습니다.
이러한 확장성 문제를 해결하기 위해 다양한 기술적 접근 방법을 검토하던 중, AI를 활용한 유사성 판단이 가장 현실적인 대안임을 깨달았습니다.
특히 Google의 Agent-to-Agent(A2A) 프로토콜에 주목했습니다. A2A는 서로 다른 벤더나 프레임워크에서 개발된 AI 에이전트 간의 원활한 통신과 상호 운용성을 가능하게 하는 개방형 프로토콜입니다. 이를 구현하기 위해 Google이 개발한 오픈 소스 AI 애플리케이션 개발 프레임워크인 Genkit을 선택했습니다. Genkit은 TypeScript/JavaScript와 Go를 지원하며 다양한 AI 모델을 통합하여 에이전트를 구축할 수 있도록 돕습니다.
이제부터는 이러한 배경을 바탕으로, 채널톡이 어떻게 A2A 기반의 Recipe Agent 시스템을 설계하고 구현했는지, 그리고 그 과정에서 얻은 기술적 인사이트를 단계별로 공유하겠습니다.
채널톡의 레시피 개편 프로젝트는 단순한 UI 개선을 넘어 근본적인 사용자 경험 혁신을 목표로 했습니다. 핵심은 세 가지였습니다.
첫째, 능동적 레시피 추천 시스템 구축입니다. 고객의 비즈니스 특성을 분석하여 가장 적합한 워크플로우나 마케팅 캠페인 템플릿을 능동적으로 추천하는 것이었습니다. 둘째, 데이터 기반 성과 표기를 통한 동기부여 강화였습니다. "이 레시피를 사용하면 이런 효과를 볼 수 있다고? 이렇게 많은 채널에서 쓰고 있었다고?"라는 FOMO(Fear of Missing Out)를 자극하여 사용자의 레시피 채택을 유도하는 것입니다. 셋째, Self-serve 환경 조성으로 "워크플로우 어렵다", "마케팅 어렵다"는 지속적인 고객 피드백을 해결하는 것이었습니다.
이러한 목표를 달성하기 위해서는 각 레시피의 관심도(해당 레시피를 사용하는 unique 채널 수)와 성과(누적 트리거 횟수, 목표 달성률 등)를 정확히 집계해야 했습니다. 어떤 워크플로우나 마케팅 캠페인이 특정 레시피와 유사한지를 판단하는 것이 핵심 과제였습니다.
레시피의 관심도와 성과를 집계하려면, 레시피로부터 직접 생성된 엔티티뿐만 아니라 고객이 직접 제작한 워크플로우나 캠페인 중에서도 특정 레시피와 핵심 구성 요소가 유사한 것들을 찾아내야 했습니다. 이는 신규 레시피의 Cold Start 문제(새로운 레시피가 퍼블리시 되었을 때 초기 사용자나 데이터가 부족하여 성능을 발휘하기 어려운 상황)를 해결하고, 실제 사용 데이터를 바탕으로 한 의미 있는 성과 지표를 제공하기 위한 필수 작업이었습니다.
예를 들어, "교육 강의 추천과 FAQ 해결" 워크플로우 레시피가 있다면, 이와 유사한 구조와 로직을 가진 모든 활성 워크플로우를 찾아내어 해당 레시피의 사용 현황과 성과로 집계해야 했습니다. 마케팅 레시피의 경우에도 마찬가지로, 특정 목표(주문완료율, 회원가입률 등)와 메시지 구성이 유사한 캠페인들을 식별해야 했습니다.
문제는 이러한 유사성 판단 작업의 규모였습니다. 채널톡의 현재 데이터 규모를 고려하면, 다음과 같은 비교 작업이 필요했습니다:
워크플로우 영역: 50개의 레시피 × 60,000개의 활성 워크플로우 = 3,000,000번의 유사성 비교
마케팅 영역: 20개의 리팩토링된 신규 레시피 × 15,000개의 활성 캠페인 = 300,000번의 유사성 비교
총 330만 번의 복잡한 유사성 판단 작업이 필요한 상황이었습니다. 더욱 문제가 되는 것은 이것이 일회성 작업이 아니라는 점입니다. 신규 레시피가 하나 추가될 때마다 워크플로우 60,000번, 마케팅 15,000번의 추가 비교가 필요하고, 새로운 워크플로우나 캠페인이 생성될 때마다 각각 50번, 20번의 비교 작업이 발생합니다.
이러한 유사성 판단은 단순한 키워드 매칭이나 카테고리 분류가 아닙니다. JSON 구조 분석, 로직 패턴 비교, 트리거 조건 검토, 데이터 필드 매핑 등 다차원적이고 복잡한 분석이 필요한 작업입니다. 이는 수작업이나 결정론적 코드 기반의 작업으로 수행하기에는 일관된 기준을 적용하기 어려우며, 무엇보다 물리적으로 불가능한 규모였습니다.
330만 번의 복잡한 유사성 판단 작업을 해결하기 위해 먼저 전통적인 방법들을 검토해보았습니다. 하지만 모든 접근법이 근본적인 한계를 드러냈습니다.
규칙 기반 매칭은 가장 직관적인 방법이었지만 확장성 문제가 심각했습니다. 워크플로우의 트리거 조건만 해도 "특정 시간대 방문", "특정 페이지 조회", "특정 상품 구매" 등 수백 가지 패턴이 존재했고, 각각에 대해 하드코딩된 규칙을 작성하는 것은 현실적으로 불가능했습니다. 새로운 패턴이 등장할 때마다 규칙을 추가해야 하고, 예외 케이스가 늘어날수록 코드 복잡도는 기하급수적으로 증가했습니다.
키워드 기반 분류 역시 정확도 한계가 명확했습니다. 동일한 "결제" 키워드라도 "결제 완료 알림"과 "결제 실패 처리"는 완전히 다른 비즈니스 로직을 가지며, 구조적 유사성을 놓치는 경우가 빈번했습니다. 특히 JSON 구조의 다양성과 복잡성을 고려하면, 단순 키워드 매칭으로는 의미 있는 유사성을 판단하기 어려웠습니다.
결정론적 코드 구현의 근본적 문제는 로직 패턴의 미묘한 차이를 판단하는 것이 거의 불가능하다는 점이었습니다. 예를 들어, 두 워크플로우가 모두 "고객 문의 → 상담원 배정 → 응답 전송"이라는 플로우를 가지더라도, 상담원 배정 조건이나 응답 메시지 구성 방식의 차이를 코드로 정확히 판단하기는 매우 어려웠습니다.
이러한 전통적 방법의 한계를 마주하며, LLM(Large Language Model)의 구조적 분석 능력에 주목하게 되었습니다. 자연어 처리에 특화된 LLM의 패턴 인식과 추상화 능력을 JSON 구조 분석에 활용할 수 있다면, 기존 방법으로는 불가능했던 의미적 유사성 판단이 가능할 것으로 판단했습니다.
특히 JSON 패턴 인식과 의미적 유사성 판단 영역에서 LLM의 강점이 두드러졌습니다. 트리거 조건의 의미적 동등성을 판단하고, 액션 시퀀스의 로직적 유사성을 분석하며, 데이터 필드 매핑을 자동화하는 작업들이 가능했습니다. 예를 들어, "payment_completed"와 "order_finished"가 비즈니스 맥락에서 유사한 의미를 가진다는 것을 LLM은 이해할 수 있었습니다.
무엇보다 대규모 데이터 처리와 일관된 기준 적용이 가능하다는 점이 결정적이었습니다. 330만 번의 비교 작업을 자동화할 수 있고, 인간 판단자와 달리 피로나 주관적 편향 없이 일관된 기준을 적용할 수 있었습니다. 또한 잘못된 판단 사례를 학습하여 정확도를 지속적으로 개선할 수 있는 가능성도 있었습니다.
AI 기반 접근법을 결정한 후, 구현 방식을 선택해야 했습니다. 에이전트 간 표준화된 통신의 필요성이 핵심 고려사항이었습니다. 워크플로우와 마케팅 영역의 유사성 판단 로직은 서로 다르지만, 두 에이전트가 독립적으로 운영되면서도 일관된 인터페이스를 제공해야 했습니다. 향후 다른 도메인의 레시피가 추가될 가능성도 고려하여 확장 가능한 아키텍처가 필요했습니다.
Genkit + TypeScript 조합을 선택한 이유는 개발 효율성 때문이었습니다. 저희 채널팀은 이미 TypeScript에 높은 숙련도를 가지고 있었고, 저 또한 기존 MCP(Model Context Protocol) 개발 경험을 통해 에이전트 통신 패턴에 대한 노하우를 보유하고 있었습니다. Java 대비 작은 서비스에서 빠른 프로토타이핑과 반복 개발이 가능한 TypeScript는 촉박한 일정 내에서 검증과 최적화를 병행해야 하는 상황에 적합했습니다.
또한 Gemini 2.5-flash-lite 모델과의 원활한 통합이 가능하고, API 비용 구조가 합리적이라는 점도 중요한 선택 요인이었습니다. 330만 번의 API 호출을 고려하면 비용 효율성은 프로젝트 성공의 핵심 요소였습니다.
이제 구체적인 시스템 아키텍처 설계로 넘어가 보겠습니다.
330만 번의 유사성 판단이라는 대규모 작업을 효율적으로 처리하기 위해서는 체계적인 아키텍처 설계가 필수였습니다. 저희는 pnpm 모노레포 구조를 선택하여 코드 재사용성과 타입 안정성을 동시에 확보했습니다.
모노레포를 선택한 핵심 이유는 공용 타입 정의 공유의 필요성이었습니다. 워크플로우와 마케팅 에이전트가 서로 다른 도메인을 다루지만, 유사성 판단의 기본 구조와 응답 형식은 일관되어야 했습니다. 또한 NestJS API 서버와 각 에이전트 간의 통신에서도 동일한 타입 정의를 사용해야 런타임 에러를 방지할 수 있었습니다.
pnpm-workspace
를 통해 packages/*
와 agents/*
구조로 관리하면서, 의존성 중복을 최소화하고 빌드 파이프라인을 통합할 수 있었습니다. 특히 개발 중에 타입 변경이 빈번했던 상황에서, 모든 관련 패키지가 자동으로 업데이트되는 것은 큰 장점이었습니다.
전체 시스템은 5개의 주요 패키지로 구성됩니다.
*packages/agent
*는 Google A2A 샘플 코드를 기반으로 한 공통 인프라스트럭처입니다. https://github.com/a2aproject/a2a-samples/tree/main/samples/js 를 참고하여 A2A 서버 구현체(/server
), 클라이언트 구현체(/client
), 프롬프트 로더(/server/prompt-loader
) 등 재사용 가능한 모듈들을 제공합니다. 처음부터 구현하지 않고 Google의 검증된 코드베이스를 활용함으로써 개발 속도를 높이고 A2A 프로토콜 스펙을 정확히 준수할 수 있었습니다.
*packages/schema
*는 Zod를 활용한 공용 타입 정의의 핵심입니다. 특히 package.json
의 exports 필드를 통해 세밀한 모듈 노출 제어를 구현했습니다. ./workflow-recipe/extract-core-logic.request
, ./marketing-recipe/compare-with-campaign.response
등 각 기능별로 request/response 타입을 분리하여, 필요한 타입만 정확히 import할 수 있도록 설계했습니다.
*packages/api
*는 포트 3000에서 실행되는 NestJS 프록시 서버로, 가장 중요한 역할을 담당합니다. 이 서버를 별도로 둔 핵심 이유는 A2A 추상화입니다. 백엔드 서버가 A2A 프로토콜의 복잡한 필드들을 알 필요 없이 간단한 HTTP API로 에이전트 기능을 사용할 수 있게 합니다. 또한 A2A의 표준 SSE 스트리밍 응답을 모두 수집한 후 완성된 HTTP JSON 응답으로 변환하여 반환합니다. /workflow-recipe
와 /marketing-recipe
엔드포인트를 분리하여 각각 해당하는 에이전트로 요청을 프록시합니다.
agents/workflow-recipe-agent
(포트 41241)와 agents/marketing-recipe-agent
(포트 41242)는 각각 워크플로우와 마케팅 도메인의 유사성 판단을 담당합니다.
포트를 3000, 41241, 41242로 분리한 전략은 서비스 독립성과 확장성 확보가 목적이었습니다. 각 에이전트가 서로 다른 포트에서 실행되어 개발 단계에서 독립적인 테스트와 디버깅이 가능했고, 요청 라우팅이 명확해져 문제 발생 시 원인 파악이 용이했습니다. 또한 향후 필요에 따라 서비스별 독립 배포로 전환할 수 있는 아키텍처 기반을 마련할 수 있었습니다.
전체 시스템의 요청 흐름은 다음과 같습니다:
백엔드 서버 → NestJS API(3000): 간단한 HTTP POST 요청
NestJS API → Agent(41241/41242): A2A 프로토콜 스펙에 맞는 요청 변환
Agent → Gemini API: 실제 AI 유사성 판단 수행
Gemini → Agent → NestJS API: SSE 스트리밍으로 순차적 응답
NestJS API: 모든 SSE 응답을 수집하고 집계
NestJS API → 백엔드 서버: 완성된 HTTP JSON 응답
이 구조의 핵심 가치는 통신 방식 호환성입니다. A2A 표준은 SSE 기반 스트리밍을 사용하지만, 기존 백엔드 시스템은 HTTP 요청-응답 패턴을 기대합니다. NestJS 프록시가 이 두 방식의 브릿지 역할을 하여, 기존 시스템 변경 없이 A2A 에이전트를 활용할 수 있게 했습니다.
공용 스키마를 통한 end-to-end 타입 보장은 이 아키텍처의 가장 큰 장점 중 하나입니다. 클라이언트에서 보낸 요청이 NestJS API를 거쳐 에이전트까지 도달하는 전 과정에서 동일한 타입 정의를 사용합니다. Zod를 활용한 런타임 검증까지 더해져, 타입 불일치로 인한 오류를 개발 단계에서 완전히 차단할 수 있었습니다.
이제 이 아키텍처 위에서 실제로 AI가 어떻게 레시피를 이해하고 유사성을 판단하는지 살펴보겠습니다.
AI 기반 유사성 판단을 구현하면서 가장 중요한 발견은 2단계 처리의 필요성이었습니다. 초기 POC에서는 복잡한 레시피의 JSON 구조를 직접 비교하도록 했지만, 결과가 일관되지 않고 정확도가 떨어졌습니다.
문제의 핵심은 복잡한 레시피의 JSON 구조에 있었습니다. 워크플로우 하나만 해도 trigger
, sections
, actions
등 수십 개의 필드가 중첩된 구조로 이루어져 있고, 핵심 로직이 여러 곳에 산재되어 있었습니다. 예를 들어, "고객 문의 시 상담원 배정" 로직이 trigger.filter
, sections[].actions[]
등 여러 위치에 분산되어 있어 AI가 전체적인 맥락을 파악하기 어려웠습니다.
extract-core-logic 단계를 도입한 후 성능이 현저히 개선되었습니다. 복잡한 레시피의 JSON을 자연어로 정리된 핵심 로직으로 변환하면, AI가 비즈니스 의도를 더 정확히 이해할 수 있었습니다. "IVR 메뉴를 통해 운영시간 안내 또는 상담원 연결 선택 제공"처럼 구조화된 설명으로 변환하면, 유사한 워크플로우와의 비교가 훨씬 매끄럽게 진행되었습니다.
이 2단계 접근법의 핵심은 각 단계의 품질 향상입니다. 첫 번째 단계에서는 JSON 파싱과 로직 추출에 집중하고, 두 번째 단계에서는 순수한 의미적 비교에만 집중할 수 있게 되었습니다.
실제 비교 과정에서는 정교한 가중치 시스템을 구축했습니다. 단순히 전체적인 유사도를 판단하는 것이 아니라, 비즈니스 중요도에 따라 카테고리별로 세밀하게 점수를 배분했습니다.
워크플로우 비교의 7개 카테고리는 다음과 같습니다:
Trigger Type (25점): 가장 중요한 요소로, 워크플로우의 시작점이 동일해야 함
Core Features (20점): IVR, 메시지 전송, 팀 배정 등 핵심 기능의 일치도
Trigger Filter (15점), Decision Logic (15점): 조건부 로직과 분기 처리
Target Medium (10점): 채널(전화, 채팅 등) 일치도
Data Collection (8점), Integrations (7점): 세부 기능들
마케팅 비교의 8개 카테고리는 비즈니스 우선순위를 더욱 세밀하게 반영합니다:
Trigger Strategy (25점): 마케팅 시작점의 비즈니스 목적 일치
Channel Strategy (20점): 전달 채널과 방식의 호환성
Goal Definition (15점): 전환 퍼널과 비즈니스 목표 정렬
Target Audience (15점): 타겟 고객층의 논리적 일치성
나머지 4개 카테고리는 각각 5-10점으로 세부 최적화 요소들
프롬프트 설계에서 가장 중요했던 것은 Few-shot Learning 예시였습니다. "회원가입 유도 레시피 vs PageView 트리거 + SignUp 목표 캠페인 = 높은 점수", "회원가입 레시피 vs 기존 회원 대상 구매 캠페인 = 낮은 점수" 같은 구체적 예시를 통해 AI가 비즈니스 로직을 학습하도록 했습니다.
절대 최대값 제한도 중요한 설계 요소였습니다. triggerStrategy: MAX 25
처럼 각 카테고리의 상한선을 명확히 설정하여, AI가 과도한 점수를 부여하는 것을 방지했습니다.
최종적으로는 복잡한 카테고리별 점수를 Match/No Match라는 단순한 결과로 변환합니다. 이는 비즈니스에서 실제로 필요한 것이 "이 워크플로우가 해당 레시피와 유사한가?"라는 명확한 답변이기 때문입니다.
실제 계산 과정을 보면, 먼저 각 카테고리별로 세밀한 점수를 산출합니다. 예를 들어 "교육 강의 추천과 FAQ 해결" 레시피와 "전화 상담 IVR" 워크플로우를 비교할 때:
{
"triggerType": 0, // startChatByUserAtLounge vs startCallByManager
"coreFeatures": 12, // IVR 기능은 유사하나 채널이 다름
"triggerFilter": 3, // 필터 로직이 상이함
"decisionLogic": 8, // 메뉴 선택 로직은 부분적으로 유사
"targetMedium": 0, // inAppChat vs call
"dataCollection": 2, // 일부 데이터 수집 패턴 유사
"integrations": 4 // 팀 배정 로직 유사
}
이렇게 계산된 카테고리 점수들을 종합하여 최종 유사도 점수를 산출하고, 미리 정의된 임계값과 비교하여 Match 여부를 결정합니다.
여기서 중요한 발견이 있었습니다. 초기에는 AI가 카테고리별 점수 계산부터 최종 Match 판단까지 모든 과정을 한 번에 처리하도록 설계했습니다. 하지만 AI가 단순한 덧셈을 자주 틀리는 문제가 발생했습니다. 25+20+15+8+... 같은 기본적인 계산에서도 오류가 생겨 전체 시스템의 신뢰성이 떨어졌습니다.
이 문제를 해결하기 위해 역할을 분담했습니다. AI는 복잡한 의미적 판단(카테고리별 점수 계산)에만 집중하고, 결정론적 코드가 단순한 수학적 계산(합계, 임계값 비교, Match 판단)을 담당하도록 변경했습니다:
// AI가 반환하는 카테고리 점수
const categoryScores = {
triggerType: 0,
coreFeatures: 12,
triggerFilter: 3,
decisionLogic: 8,
targetMedium: 0,
dataCollection: 2,
integrations: 4,
};
const finalScore = Object.values(categoryScores).reduce(
(sum, score) => sum + score,
0
);
// 단순한 boolean 반환 (임계값: 워크플로우 78점, 마케팅 70점)
const match = finalScore >= 78;
이 경험을 통해 "결정론적으로 풀 수 있는 부분은 결정론적으로 푸는 것이 더 좋다"는 중요한 인사이트를 얻었습니다. AI와 전통적 프로그래밍 방식의 하이브리드 접근법이 각각의 강점을 최대한 활용할 수 있는 방법임을 확인했습니다. 비즈니스 관점에서 의미 있는 유사성을 가진 경우만 Match로 분류하여, 레시피의 관심도와 성과 집계에 활용됩니다.
이제 이러한 정교한 AI 로직이 실제 개발 과정에서 어떻게 최적화되었는지, 그리고 대규모 데이터 처리를 위한 인프라 구축 여정을 살펴보겠습니다.
330만 번의 AI 비교 작업을 마주했을 때, 가장 먼저 직면한 것은 현실적인 비용 문제였습니다. Gemini 2.5-flash-lite의 API 비용을 계산해보니, 단순히 모든 조합을 AI에게 맡기는 것은 예산을 훨씬 초과하는 접근법이었습니다.
이 문제를 해결하기 위해 사람의 논리적 판단을 활용한 사전 필터링 전략을 도입했습니다. AI가 복잡한 의미적 유사성을 판단하기 전에, 명백히 다른 경우들을 미리 걸러내는 것이었습니다.
언어별 분리가 첫 번째 필터였습니다. 한국어로 작성된 레시피는 한국어 워크플로우와만 비교하고, 영어로 번역된 글로벌 레시피는 글로벌 워크플로우와만 비교하도록 했습니다. 언어가 다른 경우 구조적으로 유사하더라도 실제 사용자에게는 의미가 없기 때문입니다.
메신저별 분리는 더욱 효과적이었습니다. 전화 레시피는 전화 워크플로우와만, 채팅 레시피는 채팅 워크플로우와만 비교하도록 제한했습니다. 아무리 비즈니스 로직이 유사하더라도 전화와 채팅은 근본적으로 다른 사용자 경험을 제공하기 때문에 크로스 매칭은 의미가 없었습니다.
크기 제한 필터링도 필수적이었습니다. Gemini 서버의 응답 크기 제한으로 인해 너무 큰 워크플로우는 처리할 수 없었습니다. 복잡한 멀티스텝 워크플로우들을 사전에 제외함으로써 API 호출 실패를 방지할 수 있었습니다.
이러한 스마트 필터링의 결과는 놀라웠습니다. 전체 비교 횟수가 330만 번에서 30만 번으로, 10분의 1로 감소했습니다. 중요한 것은 의미 있는 비교 기회를 잃지 않으면서도 비용을 대폭 절감했다는 점입니다. AI 이전 단계의 논리적 필터링이 얼마나 중요한지를 보여주는 사례였습니다.
다음으로, 프롬프트의 성능을 증가시켜야만 했습니다. 이때 AI 성능 평가 지표로는 F1 스코어를 선택했습니다. F1 스코어는 정밀도(Precision)와 재현율(Recall)의 조화평균으로, 레시피 매칭이라는 이진 분류 문제에 가장 적합한 지표였습니다. 특히 False 케이스가 압도적으로 많은 불균형 데이터셋에서 정확한 성능 측정이 가능했습니다.
베이스라인 설정이 중요했습니다. 무작위 판단을 기준으로 테스트했을 때 F1 스코어는 10점대 초반에 불과했습니다. 이는 테스트 셋에서 실제로 매칭되어야 하는 케이스보다 매칭되지 않아야 하는 케이스가 압도적으로 많았기 때문입니다.
초기 AI 성능은 마케팅 레시피 28.8점, 워크플로우 레시피 47점이었습니다. 베이스라인 대비 의미 있는 수준이었지만, 프로덕션 환경에서 사용자에게 유용한 추천을 제공하기에는 부족했습니다.
가장 중요한 결정은 데이터 기반 검증 도구를 먼저 구축하는 것이었습니다. 체계적인 분석 도구들을 개발하여 객관적인 성능 평가와 개선 방향을 찾을 수 있었습니다.
임계값 최적화 도구는 50점부터 95점까지 1점 단위로 모든 임계값에서 precision, recall, F1 스코어를 계산했습니다. ROC 커브 분석을 통해 초기 70점 임계값이 워크플로우 기준으로, 78점일 때 최적 성능을 보인다는 것을 발견했습니다.
오탐 케이스 패턴 분석을 통해 AI가 잘못 매칭하는 사례들의 공통점을 찾아냈습니다. 예를 들어, "결제" 키워드만으로 매칭하는 경우가 많아서, 비즈니스 맥락과 전체적인 워크플로우 목적을 더 강조하도록 프롬프트를 개선했습니다.
가중치 조합 최적화는 가장 정교한 과정이었습니다. 7개 카테고리의 가중치를 Random Search와 Grid Search를 조합하여 탐색했습니다. 수백 가지 조합을 테스트한 결과, triggerType 1.2, triggerFilter 1.4 등 현재 사용하는 최적 가중치를 도출했습니다.
반복적 개선 사이클이 핵심이었습니다. 가설 설정 → 프롬프트 수정 → 자동 테스트 실행 → 결과 분석 → 개선안 도출의 과정을 수십 번 반복했습니다. 각 사이클마다 F1 스코어의 미세한 변화를 추적하며 점진적으로 성능을 개선해 나갔습니다.
최종 성과는 만족스러웠습니다. 마케팅 레시피는 28.8점에서 49.3점으로 71% 개선, 워크플로우 레시피는 47점에서 58.7점으로 25% 개선되었습니다. 베이스라인(10점대) 대비 5-6배 성능으로, 프로덕션 환경에서 사용자에게 실질적인 가치를 제공할 수 있는 수준에 도달했습니다.
레시피 추천의 특성상 100% 정확도보다는 일관성 있고 유용한 결과가 더 중요했습니다. "틀려도 유용한" 추천이 "정확하지만 도움이 안 되는" 결과보다 비즈니스 가치가 높다는 판단 하에, 현재 수준에서 프로덕션 적용을 결정했습니다.
33만 번으로 줄어든 비교 작업이라도 여전히 Gemini API의 rate limit이라는 현실적 장벽이 있었습니다. Gemini API는 분당 300회의 호출 제한이 있어, 단일 계정으로는 전체 비교 작업을 완료하는 데 상당한 시간이 필요했습니다. 그런데 배포가 예정된 주가 되어서야 모든 구현을 완료할 수 있었고, 시간적 여유가 전혀 없는 상황이었습니다.
창의적 해결책이 필요했습니다. Google Cloud 계정을 10개 발급받고, 각각에 별도의 Gemini API 키를 설정했습니다. 이론적으로는 rate limit을 10배로 확장할 수 있는 상황이었지만, 이를 실제로 구현하려면 인프라가 뒷받침되어야 했습니다.
이를 해결하기 위해 데브옵스팀의 도움이 결정적이었습니다. Kubernetes StatefulSet을 활용하여 서버 10대를 병렬로 구성해 달라고 요청했고, 각 서버가 서로 다른 Google Cloud 계정의 API 키를 사용하도록 설정해 주셨습니다. 이후 앞단에 로드 밸런서를 달아서 분당 3,000회의 API 호출이 가능한 환경이 구축되었습니다.
병렬 처리의 성과는 극적이었습니다. 단일 서버로는 며칠이 걸릴 작업을 수 시간 만에 완료할 수 있었습니다. StatefulSet의 안정성 덕분에 중간에 서버가 다운되는 일 없이 모든 비교 작업을 안전하게 처리할 수 있었습니다.
이 경험을 통해 배포 직전의 물리적 한계를 인프라로 극복할 수 있다는 것을 배웠습니다. 아무리 좋은 알고리즘과 최적화가 있어도, 실제 데이터를 처리할 수 있는 인프라 지원 없이는 의미가 없다는 현실적 교훈을 얻었습니다. 특히 AI 시스템에서는 외부 API의 제약사항이 프로젝트 성공의 핵심 변수가 될 수 있음을 깨달았습니다.
채널톡 레시피 개편 프로젝트의 3가지 핵심 목표가 모두 달성되었습니다.
첫째, 능동적 레시피 추천 시스템이 구현되었습니다. 이제 고객의 기존 워크플로우와 마케팅 캠페인을 분석하여 가장 적합한 레시피를 자동으로 추천합니다. 고객이 수십 개의 레시피 중에서 헤매지 않고, AI가 분석한 유사성을 바탕으로 "당신에게 맞는 레시피"를 제안받을 수 있게 되었습니다.
둘째, 데이터 기반 성과 표기를 통한 동기부여 시스템이 완성되었습니다. 각 레시피마다 관심도(사용 중인 unique 채널 수)와 성과(누적 트리거 횟수, 목표 달성률)가 명확히 표시됩니다. "이 레시피를 사용하면 이런 효과를 볼 수 있다고? 이렇게 많은 채널에서 쓰고 있었다고?"라는 FOMO 효과를 통해 사용자의 레시피 채택을 자연스럽게 유도합니다.
셋째, 직관적 분류 체계와 발견성이 대폭 개선되었습니다. 기존의 복잡했던 레시피 탐색 과정이 단순화되어, 고객이 원하는 기능을 쉽게 찾고 적용할 수 있게 되었습니다.
배포 초기 단계이기 때문에 정량적 지표는 아직 수집 중이지만, 정성적 개선 효과는 이미 뚜렷하게 나타나고 있습니다.
가장 중요한 변화는 "워크플로우 어렵다", "마케팅 어렵다"라는 지속적인 고객 피드백이 해결되기 시작했다는 점입니다. Self-serve 환경이 구축되면서 고객들이 복잡한 설정 과정 없이도 검증된 레시피를 바로 활용할 수 있게 되었습니다.
레시피 발견율과 적합성 판단이 크게 향상되었습니다. AI 기반 추천 시스템 덕분에 고객이 자신의 비즈니스에 맞는 레시피를 찾는 시간이 단축되었고, 추천 정확도도 높아져 실제로 유용한 레시피를 선택할 가능성이 증가했습니다.
이러한 UX 개선을 바탕으로 레시피 채택률의 점진적 증가를 기대하고 있습니다. 특히 신규 고객의 온보딩 과정에서 적절한 레시피 추천을 통해 초기 설정 시간을 대폭 단축할 수 있을 것으로 예상됩니다.
장기적으로는 레시피 기반 비즈니스 성과 향상으로 이어질 것입니다. 검증된 템플릿을 통해 더 많은 고객이 효과적인 워크플로우와 마케팅 캠페인을 구축하게 되면, 전체적인 플랫폼 활용도와 고객 만족도가 동반 상승할 것으로 기대됩니다.
이 프로젝트를 통해 가장 확신하게 된 것은 머신러닝 엔지니어(MLE)가 아니어도 충분히 프로덕션 수준의 AI 시스템을 구축할 수 있다는 점입니다. 물론 딥러닝 모델을 직접 설계하거나 학습시키지는 않았지만, 실제 비즈니스 문제를 해결하는 AI 시스템을 만드는 것은 충분히 가능했습니다.
핵심은 기존 소프트웨어 개발 역량을 AI 영역으로 확장하는 것이었습니다. pnpm 모노레포로 확장 가능한 시스템 아키텍처를 설계하고, Zod를 활용한 end-to-end 타입 안정성을 확보하며, F1 스코어 기반의 체계적인 검증 파이프라인을 구축하는 경험들이 AI 프로젝트에서도 그대로 활용되었습니다. 특히 기존 MCP(Model Context Protocol) 개발 경험이 A2A 프로토콜 적응에 큰 도움이 되었고, Google의 A2A 프레임워크와 Genkit 같은 개발자 친화적인 도구들이 이런 전환을 더욱 수월하게 만들어 주었습니다.
돌이켜보면 이 프로젝트의 성공 요인은 문제를 단계별로 분해하고 각 단계에서 체계적으로 접근한 것이었습니다. 330만 번의 비교 작업이라는 거대한 문제를 스마트 필터링으로 30만 번으로 줄이고, F1 스코어 기반의 검증 도구를 먼저 만든 후 반복적으로 개선해 나갔습니다.
도구부터 만드는 접근법이 특히 효과적이었습니다. 임계값 최적화 도구, 오탐 패턴 분석 도구, 가중치 조합 최적화 도구 등 체계적인 검증 파이프라인이 있었기에 프롬프트 최적화를 과학적으로 진행할 수 있었습니다. 그리고 데브옵스팀과 협력하여 구축한 병렬 처리 인프라처럼 팀워크를 통한 창의적이면서도 실용적인 해결책을 찾는 것이 중요했습니다.
무엇보다 AI와 결정론적 코드의 하이브리드 접근에서 큰 교훈을 얻었습니다. AI가 복잡한 의미적 판단을 담당하고, 전통적 프로그래밍이 단순한 계산을 처리하는 역할 분담이 각각의 강점을 최대화할 수 있었습니다.
채널톡에서는 이런 도전적인 기술 문제들을 함께 해결해 나갈 개발자 동료들을 찾고 있습니다. AI 시대에도 소프트웨어 엔지니어의 역할은 더욱 중요해지고 있습니다. 새로운 기술에 대한 열린 마음과 실무 중심의 문제 해결 능력을 가진 분이라면, 경험의 깊이와 상관없이 함께 성장할 수 있는 환경이 준비되어 있습니다.
앞으로도 채널톡은 기술의 경계를 넘나들며 실제 고객 가치를 만들어내는 도전을 계속해 나갈 것입니다. 이런 여정에 함께하고 싶으신 분들은 언제든지 채널톡의 문을 두드려 주세요!
We Make a Future Classic Product