두 개의 MCP 서버, 하나의 채팅: 대시보드 없이 광고 지표와 양식 결과 통합하기
요약
Model Context Protocol(MCP)을 활용하여 서로 다른 두 SaaS 서비스의 데이터를 단일 AI 채팅 인터페이스에서 통합하는 방법을 다룹니다. 서버 간 직접적인 API 연동 대신, AI 클라이언트가 오케스트레이터 역할을 수행하여 데이터를 조인하는 새로운 디자인 패턴을 제시합니다.
핵심 포인트
- MCP를 통해 AI 클라이언트가 여러 외부 서비스에 동시에 연결 가능
- 데이터 조인(Join)의 주체를 서버에서 AI 클라이언트로 전환
- 서비스 간 결합도를 낮추는 독립적인 도구 설계 패턴
- LLM을 활용한 데이터 오케스트레이션의 효율성
한곳에 머물지 않는 특정한 종류의 업무가 있습니다. 한 화면에서는 광고를 실행하고, 다른 화면에서는 양식(form) 응답을 수집합니다. 저렴한 클릭이 실제로 등록으로 이어졌는지 확인하려면, 광고 대시보드에서 숫자를 복사하여 양식 관리자 페이지를 열고 수동으로 대조해야 합니다. 두 시스템은 서로를 알지 못하기 때문에, 사람이 조인(join) 계층 역할을 수행하게 됩니다.
저는 8일 동안 이 조인 작업이 제 머릿속을 벗어나 단일 대화 속으로 들어올 수 있는지 테스트했습니다. 설정은 다음과 같습니다: 동일한 AI 클라이언트에 연결된 두 개의 MCP 서버입니다. 하나는 실행 중인 캠페인의 지표를 반환하는 광고 측 커넥터(connector)이고, 다른 하나는 양식 결과를 반환하는 FORMLOVA의 커넥터입니다. MCP — 모델 컨텍스트 프로토콜 (Model Context Protocol) — 는 AI 클라이언트가 외부 서비스에 안전하게 연결되는 공유 관문입니다. 한 채팅창에서 두 관문이 모두 열려 있으면, LLM(대규모 언어 모델)은 한쪽에서 데이터를 가져오고, 다른 쪽에서도 가져와서 동일한 턴(turn) 내에서 결과를 융합할 수 있습니다.
이 글은 이러한 융합을 깔끔하게 만드는 디자인 패턴과, 그 과정에서 얻은 한 가지 솔직한 발견에 관한 것입니다.
변화: 누가 조인을 수행하는가
두 개의 SaaS 제품을 전통적인 방식으로 연결할 때는 보통 그중 하나가 다른 하나를 알고 있어야 합니다. 제품 A 내부에 제품 B의 API를 호출하고, 필드를 매핑하며, 조인된 결과를 저장하는 커넥터를 구축합니다. 해당 커넥터는 유지보수 측면에서 부담이 되며, 한 벤더의 데이터 모델을 다른 벤더의 모델에 고착시키게 됩니다.
MCP를 사용하면 이 과정을 건너뛸 수 있습니다. 두 제품 모두 MCP 서버를 노출한다면, AI 클라이언트가 동시에 두 곳에 연결할 수 있습니다. 클라이언트가 오케스트레이터(orchestrator)가 되는 것입니다. 클라이언트는 광고 측 서버에서 읽고, 양식 측 서버에서 읽은 뒤, 사용자가 요청하는 순간 대화 속에서 직접 조인을 수행합니다.
이는 각 서버가 무엇을 반환해야 하는지에 대한 관점을 재정의합니다. 서버는 더 이상 유용성을 확보하기 위해 경계를 넘어 다른 서비스의 데이터를 가져올 필요가 없습니다. 서버는 오직 자신의 데이터만을, 클라이언트가 다른 소스와 정렬할 수 있는 형태로 정확하게 반환하기만 하면 됩니다. 조인 (Join)은 서버의 역할이 아닙니다. 조인은 클라이언트의 역할입니다.
FORMLOVA의 문서는 이러한 책임 경계를 명시적으로 규정하고 있습니다. FORMLOVA의 역할은 정확한 데이터를 제공하고 도구의 입출력 형태 (Input/Output shapes)를 명확히 하는 것입니다. LLM의 역할은 여러 MCP 서버를 가로질러 오케스트레이션 (Orchestration)을 수행하고, 민감한 단계를 사용자와 확인하는 것입니다. 연결된 서비스의 역할은 수락하고 저장하는 것입니다. FORMLOVA 내부에는 결합될 수 있는 각 외부 서비스를 위한 커넥터 코드 (Connector code)가 존재하지 않습니다. 도구 정의 (Tool definition)가 곧 계약 (Contract)이며, 이 정의가 잘 되어 있다면 LLM이 나머지를 처리합니다.
의도적으로 단일 소스만을 반환하는 도구
이러한 경계가 구체적으로 표현된 것이 FORMLOVA 측의 광고 기여도 (Ad-attribution) 도구입니다. 이 도구의 역할은 좁고 의도적입니다. 양식 응답을 광고 ID, UTM 소스, 캠페인별로 분류하고, 광고당 조회수, 응답 수, 전환율을 반환하는 것입니다. 그것이 전부입니다. 이 도구는 광고 플랫폼을 호출하지 않습니다. 광고 플랫폼의 지출액, 노출수 또는 클릭수를 알지 못합니다. 이 도구는 다른 소스와 정렬될 수 있는 방식으로 키 (Key)가 지정된 FORMLOVA 자체의 데이터만을 반환합니다.
개념적으로 이 도구는 다음과 같은 행 (Rows)을 반환합니다:
// FORMLOVA의 광고 기여도 도구: 단일 소스, 광고 ID로 키 지정.
// 양식 측의 데이터(Truth)만을 반환하며, 광고 플랫폼으로부터는 아무것도 가져오지 않음.
type AdAttributionRow = {
...
주목할 만한 점은 바로 '부재'입니다. 이 핸들러 (Handler) 내부에는 광고 플랫폼으로 향하는 HTTP 호출이 없습니다. 이 도구는 의도적으로 그 자체로는 불완전합니다. 도구는 그림의 절반만을 반환하며, 클라이언트가 다른 MCP 서버로부터 나머지 절반을 제공할 것이라고 신뢰합니다.
만약 당신의 본능이 "도구는 사용자의 질문 전체에 답해야 한다"라고 말한다면, 이는 잘못된 것처럼 느껴질 수 있습니다. 하지만 여기서의 질문 전체, 즉 _"이 광고가 어떤 비용으로 성과를 냈는가?"_는 두 개의 서비스에 걸쳐 있습니다. 만약 FORMLOVA가 이를 단독으로 해결하려 했다면, FORMLOVA는 광고 플랫폼 커넥터(connector)를 내장하고, 이를 위한 자격 증명(credentials)을 보관하며, API 변경 사항을 추적하고, 사용자가 운영할 수 있는 모든 광고 플랫폼에 대해 이를 매번 다시 구현해야 했을 것입니다. 대신 FORMLOVA는 자신이 확실히 소유한 단 하나의 소스만을 반환하고, 두 소스가 이미 범위(scope) 내에 있는 곳, 즉 채팅에서 조인(join)이 일어나도록 합니다.
8일 동안의 조인(join) 과정
실제로 이 두 부분이 어떻게 결합되었는지 보여드리겠습니다.
첫 번째 부분은 광고 측 MCP에서 왔습니다. 채팅에서의 단순한 요청 한 번으로 표준 지표들이 반환되었습니다. 8일 동안 캠페인은 ¥6,597를 지출했으며, 노출(impressions) 5,578회, 도달(reach) 3,065명, 빈도(frequency) 1.82회, 클릭(clicks) 704회, 클릭률(CTR) 12.62%, CPM ¥1,183, 그리고 관리자가 반올림한 CPC ¥9를 기록했습니다 — 지출액을 클릭수로 나누면 실제 수치는 약 ¥9.4입니다. 지출은 하루에 대략 ¥570에서 ¥1,080 범위 내에서 꾸준히 이루어졌습니다.
이 수치들만 놓고 보면 좋아 보입니다. 12.62%의 CTR은 가입 유도 광고치고는 높습니다. 약 ¥9.4의 CPC는 저렴합니다. 하지만 광고 측 수치만으로는 그 클릭들이 무엇으로 변했는지 알 수 없습니다. 이것이 경계(boundary)의 핵심입니다. 광고 MCP는 "클릭이 발생했다"에서 끝납니다.
두 번째 부분은 동일한 채팅 내에서 FORMLOVA의 MCP로부터 왔습니다. 저는 커넥터를 전환하여 — 동일한 대화 내에서 다른 통로를 통해 — 광고 ID별 양식 응답 상세 내역을 가져왔습니다. 이제 두 소스가 모두 테이블 위에 올라왔습니다.
여기서 클라이언트 측 조인(client-side join)의 진가가 드러납니다. 광고 측 지출액을 양식 측 전환 수(conversion count)로 나누면, 획득 비용(acquisition cost)이 채팅창에 바로 나타납니다. 어떤 서버도 이 수치를 계산하지 않았습니다. 클라이언트가 계산한 것입니다. 클라이언트만이 양쪽 모두를 가지고 있었기 때문입니다. 하나의 MCP만 봐서는 결코 이 계산에 도달할 수 없습니다. 광고 측은 "클릭이 발생했다"에서 끝나고, 양식 측은 "응답이 도착했다"에서 끝납니다. 오직 하나의 대화 속에서 이 둘을 융합해야만 이들을 연결할 수 있습니다.
채팅창을 떠나지 않고도 차트를 그릴 수 있었습니다. 지출액(Spend), 노출수(Impressions), 클릭수(Clicks), 클릭률(CTR) — 지표를 전환해가며 8일간의 일일 트렌드를 즉석에서 도식화했습니다. 대시보드도, 필터를 다시 적용할 필요도, 보조 모니터도 필요 없었습니다.
신기루: 저렴한 클릭들이 실제로 향한 곳
일일 트렌드에서 무언가 이상한 낌새가 느껴졌습니다. 노출수는 전반부에 높게 유지되다가 기간 후반부에 거의 절반으로 줄어들었지만, CTR은 후반부에 오히려 상승했습니다. 노출당 비용(Price per impression)이 상승하면서 클릭률(Click rate)도 함께 올라가는 것은 다소 부자연스럽습니다. 경쟁이 치열한 광고 지면(Placements)이 일반적으로 클릭하기 더 쉬운 것은 아니기 때문입니다. 그래서 저는 지면별로 광고 게재(Delivery)를 나누어 보았습니다.
| 지면 (Placement) | 노출수 (Impressions) | 클릭수 (Clicks) | 지출액 (Spend) | CTR |
|---|---|---|---|---|
| Audience Network — 보상형 동영상 (rewarded video) | 3,477 | 476 | ¥2,832 | 13.69% |
| ... |
편중 현상은 극명했습니다. Audience Network의 두 슬롯만으로 전체 노출수의 약 96%, 지출액의 약 83%를 차지했습니다. 제가 실제로 도달하고자 했던 Facebook 및 Instagram 피드(Feeds)에 전달된 노출수는 각각 24회와 139회로, 사실상 반올림 오차 수준에 불과했습니다.
이는 겉보기에 좋아 보였던 상위 지표(Top-line)를 완전히 재해석하게 만듭니다. 저렴하고 높은 CTR을 기록한 게재는 대부분 Audience Network의 보상형 동영상 슬롯으로 흘러 들어갔습니다. 이는 앱 내에서 보상을 받기 위해 동영상을 시청하는 지면으로, 실수로 클릭하는 경우가 흔하며 CTR이 실제보다 높게 나타나는 경향이 있습니다. 효율성 수치가 훌륭해 보였던 이유는 목적지가 제가 예상했던 곳이 아니었기 때문입니다. 클릭 수치만으로는 그 클릭이 어떤 의미를 갖는지 결코 알 수 없습니다.
제가 이를 어떻게 알게 되었는지 정확히 말씀드리고 싶습니다. 지면별 상세 내역은 광고 측 MCP에서 가져왔습니다. 해당 클릭들이 실제로 유의미한 곳으로 이어졌는지에 대한 판단은 광고 측 데이터를 양식(Form) 측 데이터와 정렬한 것, 즉 클라이언트가 수행한 조인(Join)을 통해 얻은 것입니다. 어느 한쪽의 소스만 있었다면 저는 추측만 했을 것입니다. 하지만 두 소스를 함께 사용함으로써, 저는 직감이 아닌 데이터를 바탕으로 광고 게재가 대부분 신기루였다고 말할 수 있었습니다.
동일한 채팅 내에서 광고 재구성하기
문제가 눈에 보였기 때문에, 저는 광고 관리자(Ad Manager)를 열지 않고 광고 측 MCP를 통해 동일한 흐름 내에서 이를 해결했습니다.
저는 광고 세트(Ad set)를 다시 구성했습니다: 노출 위치(Placement)는 Facebook 및 Instagram 피드로만 제한하고, Audience Network는 제외했으며, 자동 타겟 확장(Automatic audience expansion)은 끄고, 대상은 일본의 25세 이상으로 설정하고, 기존에 사용하던 동일한 비디오 소재(Video creative)를 재사용했습니다. 재구성부터 활성화까지 모든 단계는 대화형 지시(Conversational instruction)로 이루어졌습니다.
이 점은 MCP 사용자들에게 두 가지 중요한 의미를 갖습니다. 첫째, 이는 단순히 읽는 것에 그치지 않습니다. 문제를 드러낸 것과 동일한 대화가 문제 해결을 위한 조치로 이어졌는데, 이것이 분석 인터페이스(Analytics surface)와 운영 인터페이스(Operations surface)의 차이입니다. 둘째, 각 작업은 되돌릴 수 있습니다. 광고 세트를 다시 활성화하거나 일시 중지함으로써 이전 상태로 복구할 수 있으므로, 채팅 내 진단에 따라 조치를 취하는 비용이 낮습니다.
이것이 캠페인에 대한 최종 결론이라고 주장하지는 않겠습니다. 아직 할 일이 남아 있습니다. 광고와 양식(Forms)을 연결하는 측정 파라미터(Measurement parameters)를 공고히 하고, 전환 이벤트(Conversion event)가 광고 측에서 수신되는지 확인하며, 이 채널이 이 제품에 적합한가라는 더 큰 질문에 답해야 합니다. 하지만 양쪽을 읽고, 융합하고, 문제를 찾아내고, 조치하는 이 루프(Loop) — 이 모든 것이 하나의 채팅 안에서 — 완성되었습니다.
이것이 일회성이 아닌 패턴인 이유
세부 사항을 걷어내면 재사용 가능한 디자인 패턴(Design pattern)이 남습니다.
제품을 MCP를 통해 노출할 때, 모든 도구가 스스로 교차 서비스 질문(Cross-service questions)에 답하게 만들고 싶은 유혹을 뿌리쳐야 합니다. "답변을 완성하기 위해" 다른 서비스의 데이터를 가져오는 도구는 위장된 커넥터(Connector)에 불과합니다. 이는 보유해서는 안 될 자격 증명(Credentials)을 운반하고, 제어할 수 없는 API에 종속시키며, 연결 가능한 서비스의 수가 늘어남에 따라 그 복잡성도 배가됩니다. 또한, 이제 두 서비스의 데이터 모델(Data models)이 하나의 불투명한 응답(Opaque response) 안에서 충돌하게 되므로 LLM의 작업도 더 어려워집니다.
대안은 깔끔하게 키(Key)가 지정된 자체 소스를 반환하고 클라이언트가 이를 조인(Join)하도록 하는 것입니다. 다른 소스가 그 옆에 나란히 배치될 수 있도록 출력을 설계하십시오. 즉, 안정적인 조인 키(여기서는 광고 ID), 명시적인 필드 이름, 서비스 간의 숨겨진 집계(Aggregation)가 없어야 합니다. 그러면 이미 두 MCP 서버에 모두 연결된 LLM이 별도의 비용 없이 조인 레이어(Join layer) 역할을 수행하게 됩니다. LLM은 질문을 받는 즉시 대화 속에서 서비스 간 지표(이 경우 지출액을 전환 수로 나눈 획득 비용)를 계산합니다.
이러한 책임의 분할이 설계의 핵심입니다. 서버는 정확한 단일 소스 데이터를 반환하고, 클라이언트는 이를 오케스트레이션(Orchestration) 및 조인하며, 쌍을 이루는 서비스는 데이터를 저장합니다. 각 부분이 세 가지 역할을 모두 수행하려 하지 않기 때문에 구조가 단순합니다. 그리고 그 보상은 구체적입니다. 저는 8일 동안 대시보드를 단 하나도 열지 않고 광고 지표를 양식 결과와 대조하고, 추세를 차트로 그리고, 배치 미라지(Placement mirage, 광고 위치 착시)를 진단하고, 광고를 재구축했습니다. 조인은 채팅 내에서 이루어졌는데, 이는 각 서버가 자신이 소유한 데이터만을 반환하는 것에 만족했기 때문입니다.
링크
- FORMLOVA 필드 리포트 (내부 표준): https://formlova.com/en/blog/meta-ads-mcp-formlova-verification-en
- 상위 허브 — FORMLOVA MCP 양식 서비스 가이드: https://formlova.com/en/blog/mcp-form-service-guide-en
FORMLOVA는 MCP 서버를 진입점으로 사용하는 채팅 우선(Chat-first) 양식 서비스로, Claude, ChatGPT, Gemini, Cursor, Windsurf와 같이 MCP를 지원하는 AI 클라이언트에서 사용할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기