grep

ChatGPT에서 요기요 배달 쓰기 — MCP + 위젯 연동 개발기

요기요

2026년 3월 12일

원문에서 보기 ↗

ChatGPT Apps SDK와 MCP(Model Context Protocol)로 요기요 배달 서비스를 연동한 프로젝트의 기술적인 설계와 구현, 그리고 개발 과정에서 얻은 인사이트를 공유합니다.

1. 왜 ChatGPT App인가

약 1년 전쯤, 한 경제 유튜버가 "곧 모든 앱이 AI로 대체되는 시대가 온다"며 배달 앱을 예로 든 적이 있었습니다. 배달 앱을 열어서 카테고리를 누르고, 가게를 고르고, 메뉴를 스크롤 하는 대신 --- AI에게 "오늘 매운 거 먹고 싶어"라고 말하면 메뉴 추천부터 주문까지 한 번에 처리해 주는 세상이 올 거라는 이야기였습니다. 배달 플랫폼을 개발하는 입장에서 그 영상을 보면서 든 생각은 솔직히 회의적이었습니다. 수만 개 레스토랑의 메뉴 정보, 실시간 배달 가능 여부, 라이더 매칭, 결제 처리 --- 이 모든 걸 AI가 어떻게 다 처리한다는 거지? 배달 플랫폼이 직접 데이터와 기능을 열어주지 않는 한 불가능할 텐데 라고 생각했습니다.

그런데 2025년 12월, ChatGPT Apps가 출시되면서 그 "불가능"이 현실이 되는 구조가 만들어졌습니다. OpenAI가 제공하는 MCP(Model Context Protocol)Apps SDK 를 통해, 외부 서비스가 자신의 데이터와 기능을 ChatGPT에 연결할 수 있는 표준 인터페이스가 열린 것입니다. AI가 혼자서 배달 서비스를 만드는 게 아니라, 배달 플랫폼이 MCP 서버를 통해 도구(Tool)를 제공하면 ChatGPT가 대화 맥락에 맞춰 그 도구를 호출하는 구조 --- 결국 플랫폼이 정보를 열어야 가능하다는 그때의 생각이 맞았고, MCP가 바로 그 "여는 방법"을 표준화한 프로토콜이었습니다.

여기서 주목할 점은 MCP 자체와 ChatGPT App의 차이 입니다. 기존 MCP는 REST API처럼 AI가 외부 시스템의 기능을 호출할 수 있도록 Tool을 확장하는 프로토콜입니다. AI가 Tool을 호출하면 JSON 형태의 데이터를 받아서 텍스트로 요약해 응답하는 구조 --- 결국 사용자가 보는 것은 텍스트뿐이었습니다. 그런데 ChatGPT Apps SDK는 여기에 위젯(Widget) 이라는 레이어를 추가했습니다. MCP 서버가 Tool 응답에 _meta.outputTemplate이라는 메타데이터를 포함하면, ChatGPT가 단순 텍스트 대신 HTML 기반의 인터랙티브 UI 를 대화 안에 인라인으로 렌더링합니다. 사용자는 이 위젯 안에서 지도를 드래그하고, 가게를 클릭하고, 메뉴를 스크롤 하고, 주문 버튼을 누를 수 있습니다. 텍스트 응답을 읽는 것이 아니라 서비스와 직접 상호작용하는 경험이 가능해진 것입니다.

이제 사용자는 "강남역 근처 치킨 배달 가능한 곳 알려줘"라고 대화하는 것만으로, ChatGPT가 요기요의 레스토랑 검색 Tool을 호출하고, 그 결과를 가게 리스트·상세 정보·메뉴·리뷰·지도 같은 위젯으로 바로 보여줄 수 있게 됩니다. 기존 요기요 앱/웹의 전체 기능을 그대로 옮기는 것이 아니라, 대화 흐름 속에서 필요한 데이터만 Tool로 가져오고, 그에 맞는 UI를 위젯으로 렌더링하는 구조 --- 이것이 이 프로젝트가 만들고자 한 경험이었습니다.

2. 개발 환경 세팅

ChatGPT App을 개발하고 테스트하려면 먼저 몇 가지 환경이 갖춰져야 합니다.

Registering a Dev version app to the workspace

Refresh MCP in Dev mode

3. 개발 타임라인

2025년 12월 초 ChatGPT Apps 서비스의 공개가 예정되어 있었기 때문에, 한정된 일정 안에 프로덕션 수준의 MCP 서버와 위젯을 완성해야 하는 상황이었습니다. 처음부터 모든 것을 구축하기보다는 OpenAI가 공개한 Apps SDK Examples 레포지토리의 레퍼런스 구현체(Pizzaz/ecommerce 예제)를 기반으로 빠르게 프로토타이핑한 뒤, 요기요 배달 도메인에 맞춰 확장해 나가는 전략을 택했습니다. 특히 프로토타이핑 단계에서는 AI 코딩 어시스턴트(Claude)를 적극 활용한 바이브 코딩(Vibe Coding) 방식으로 개발 속도를 끌어올렸습니다. 예제 코드의 구조를 파악하고, 요기요 API 연동 코드를 생성하고, 위젯 컴포넌트의 초기 스캐폴딩을 만드는 과정에서 AI와의 대화 기반 개발이 반복적인 보일러플레이트 작업을 크게 줄여주었습니다.

기술 스택 선정에서도 개발 속도를 우선시했습니다. OpenAI의 예제 레포는 MCP 서버를 Python( FastMCP**)** 과 Node.js( @modelcontextprotocol/sdk**)** 두 가지 스택으로 제공하고 있었는데, 이번 프로젝트는 프론트엔드 개발자가 위젯(React/TypeScript)과 MCP 서버를 모두 담당하는 구조였기 때문에, 클라이언트와 서버 간 언어 컨텍스트 스위칭 없이 TypeScript 단일 스택 으로 개발할 수 있는 Node.js 기반 MCP 서버를 선택했습니다. 이를 통해 위젯의 타입 정의(types.ts)를 서버와 공유하고, pnpm 워크스페이스로 모노레포를 구성하여 빌드·배포 파이프라인을 일원화할 수 있었습니다.

Phase 1 --- 도메인 전환 & API 연동

OpenAI Apps SDK Examples의 pizzaz_server_node 구현체를 fork하여, 요기요 배달 서비스에 맞게 재구성하는 것으로 시작했습니다.

Phase 2 --- 위젯 UI/UX 고도화

디자인팀과 협업하여 Figma 디자인 가이드를 위젯에 적용했습니다. OpenAI가 제공하는 App Design GuidelinesUI Guidelines를 기반으로, 인라인 카드·풀스크린 등 위젯 디스플레이 모드의 제약 사항을 반영하면서 요기요 브랜드 디자인을 적용했습니다.

Phase 3 --- 아키텍처 분리: 위젯 Static Hosting vs MCP Server

초기에는 MCP 서버(Node.js)가 Tool 요청 처리와 위젯 정적 파일(HTML/JS/CSS/이미지) 서빙을 모두 담당하는 모놀리식 구조였습니다. 운영 환경을 구성하면서 이 두 책임을 분리하기로 결정했습니다.

분리 결정의 근거:

이에 따라 MCP 서버 엔드포인트 URL과 위젯 정적 리소스(S3/CDN) URL을 환경변수로 분리하고, ReadResourceRequest 핸들러가 반환하는 위젯 HTML 내의 JS/CSS 참조 경로가 위젯 CDN 도메인을 바라보도록 빌드 파이프라인을 구성했습니다.

Phase 4 --- CSP 정책·Tool 메타데이터·UX 개선

ChatGPT의 sandbox iframe 환경에서 위젯이 외부 리소스에 접근하려면, MCP 서버가 _meta 응답에 허용 도메인을 명시적으로 선언해야 합니다.

Phase 5 --- Observability 구축 & 크로스 플랫폼 대응

프로덕션 운영을 위한 모니터링 체계를 구축하고, iOS/다크 모드 등 다양한 클라이언트 환경에 대응했습니다.

Phase 6 --- 안정화 & 유지보수

4. 전체 아키텍처

Service Architecture

MCP 서버 (Node.js / TypeScript)

위젯 (React + Vite)

배포

5. MCP 서버와 툴 설계

MCP 서버는 다음 세 가지를 구현합니다.

  1. List tools --- 사용 가능한 툴 목록과 입력 스키마, 그리고 어떤 위젯 HTML과 매핑할지 (_meta.openai/outputTemplate) 를 응답.
  2. Call tools --- ChatGPT가 선택한 툴과 인자를 받아, 요기요 API를 호출한 뒤 구조화된 결과 + 위젯 메타데이터를 반환.
  3. Read resources --- 위젯 HTML 템플릿을 제공. ChatGPT가 해당 위젯을 iframe으로 띄울 때 사용.

각 툴은 readOnlyHint: true 어노테이션으로 "읽기 전용 도구"임을 표시해, 불필요한 사용자 확인을 줄였습니다.

6. 위젯과 데이터 흐름

위젯은 ChatGPT의 sandbox iframe 안에서 동작합니다. 우리가 빌드한 HTML/JS/CSS가 connector_*.web-sandbox.oaiusercontent.com 같은 도메인의 iframe으로 로드되고, 그 안에서만 실행됩니다.

7. CSP: sandbox iframe 안에서의 보안 제약

ChatGPT Apps 위젯 개발에서 가장 많이 부딪힌 부분이 CSP(Content Security Policy)였습니다.

ChatGPT는 위젯을 sandbox iframe 안에서 실행하며, 기본적으로 모든 외부 네트워크 요청을 차단합니다. 우리 위젯처럼 외부 JS(Mapbox GL), 이미지 CDN, MCP 서버 호출이 필요한 경우, MCP 메타데이터의 openai/widgetCSP 필드에 허용 도메인을 명시적으로 선언해야 합니다.

"openai/widgetCSP": {
  connect_domains: [mcpUrl, widgetUrl, "https://api.mapbox.com", ...],  // fetch, callTool 등
  resource_domains: [widgetUrl, "https://rev-static.yogiyo.co.kr", "data:", ...],  // 이미지, CSS
  script_domains: [widgetUrl, "https://api.mapbox.com", ...],  // 외부 JS 로드
}

이 메타데이터에 없는 도메인으로의 요청은 브라우저가 조용히 차단합니다. 에러 팝업 없이 이미지가 안 뜨거나, 지도가 빈 화면이 되거나, API 호출이 실패합니다. 브라우저 콘솔의 Refused to... 로그가 유일한 단서이기 때문에, CSP 문제는 알고 있으면 5분, 모르면 반나절이 걸립니다.

개발 중 겪은 대표적인 CSP 이슈들:

8. 배포와 운영에서의 선택

URL 분리

배포 환경에서는 세 가지 URL을 분리하여 관리합니다.

빌드 시점에 위젯 CDN URL을 주입하여 HTML 안의 스크립트/스타일 경로를 만들고, MCP 서버의 CSP에도 이 도메인을 허용하도록 맞췄습니다.

서버에도 빌드가 필요한 이유

MCP의 ReadResource에서 위젯 HTML 템플릿을 내려줄 때, HTML 안에 <script src="..."> 로 링크된 JS 파일명에는 빌드 시 생성되는 콘텐츠 해시가 포함됩니다(예: shop-list.a3f2b1c.js). S3/CDN에 배포된 JS 파일과 이 해시가 일치해야 위젯이 정상 로딩되므로, 서버 배포 시에도 반드시 pnpm run build를 실행하여 최신 빌드 결과물의 해시를 맞춰야 합니다. 위젯 빌드 → S3 업로드 → MCP 서버 배포 순서가 어긋나면 해시 불일치로 위젯 로딩이 실패하기 때문에, CI/CD 파이프라인에서 이 순서를 명시적으로 보장하도록 구성했습니다.

로컬 개발

로컬에서는 MCP 서버와 위젯 정적 파일을 하나의 서버(포트 8000)로 함께 서빙한 뒤, ngrok 같은 터널링 서비스로 외부에 노출하고, ChatGPT의 개발자 워크스페이스에 개발 앱으로 등록하여 실제 ChatGPT 대화 안에서 E2E 테스트를 진행했습니다.

다만 위젯 UI를 수정할 때마다 ChatGPT를 거치면 반복 비용이 크기 때문에, 위젯을 독립적으로 로컬 브라우저에서 바로 띄워 테스트할 수 있는 환경도 구성했습니다. test_widget/ 디렉토리에 각 위젯별 테스트 HTML(test-widget-shop-list.html, test-widget-map.html 등)을 만들어, 브라우저에서 직접 열어 위젯을 확인할 수 있도록 했습니다.

핵심은 mock-openai.js입니다. ChatGPT sandbox 안에서만 주입되는 window.openai 객체를 로컬에서 모의(mock)로 구현한 파일로, toolOutput, callTool, setWidgetState, openExternal 등 위젯이 의존하는 API를 동일한 인터페이스로 제공합니다. 특히 callTool은 로컬 MCP 서버에 실제 JSON-RPC 요청을 보내고 SSE 스트림으로 응답을 받아오도록 구현했기 때문에, ChatGPT 없이도 위젯 → MCP 서버 → 요기요 API 전체 흐름을 로컬에서 테스트할 수 있습니다. 테스트 HTML에서 updateWidgetProps()로 샘플 데이터를 주입하면 위젯이 즉시 렌더링되고, 위젯 안에서의 callTool 호출도 실제 MCP 서버를 거쳐 동작합니다.

또한 위젯 코드 자체에서도 useWidgetProps 훅에 defaultState 폴백을 두어, window.openai.toolOutput이 없는 환경에서도 빈 화면 대신 기본 UI가 렌더링되도록 처리했습니다.

9. 모니터링: Datadog APM과 SSE

운영에서 Datadog APM 으로 트레이스와 성능을 보기로 했습니다. Node 서버에는 dd-trace를 쓰고, 호스트에 Datadog Agent가 떠 있으면 트레이스가 자동으로 수집됩니다.

여기서 두 가지를 특히 신경 썼습니다.

Tracer 초기화 순서

dd-trace는 다른 모듈(예: node:http**)이 로드되기 전에** init() 되어야, HTTP 등이 자동 계측됩니다. ESM에서는 모든 import가 먼저 평가된 뒤에 top-level 코드가 실행되므로, 같은 파일 안에서 순서를 바꿔도 소용이 없습니다.

그래서 tracer 전용 파일 (tracer.ts)을 두고, 진입점( server.ts**)의 맨 첫 번째 import**로 두어, node:http보다 먼저 tracer.init()이 실행되게 했습니다.

// server.ts — 반드시 첫 번째 import
import tracer from "./common/tracer.js";

import { createServer } from "node:http"; // 이 시점에서 이미 dd-trace가 패치 완료
// ...

SSE 트레이싱

MCP는 SSE로 한 연결이 오래 유지되기 때문에, "요청 하나 = 스팬 하나"인 일반 HTTP와 다르게 보입니다. 그래서 커스텀 스팬을 넣었습니다.

Datadog APM에서 "GET /mcp → mcp.sse_connection → mcp.sse_send 여러 개"처럼 한 트레이스 트리로 보이게 했고, 태그(mcp.transport: sse)로 SSE만 필터링해 볼 수 있습니다.

10. 앱 등록: 코드 완성이 끝이 아니었다

개발이 마무리되면 바로 서비스를 올릴 수 있을 거라 생각했지만, 실제로는 앱 등록 절차라는 예상치 못한 관문이 기다리고 있었습니다.

ChatGPT Apps의 SDK 문서와 MCP 개발 가이드는 사전에 충분히 검토할 수 있었지만, 정작 앱을 OpenAI에 등록(submit)하는 과정에서 요구하는 항목들은 등록 페이지가 열리기 전까지는 전혀 알 수 없었습니다. 12월 15일, 앱 등록이 공개되자마자 제출을 시도했는데, 다음과 같은 항목들이 필요했습니다.

코드 한 줄 건드리지 않는 작업이었지만, 서류 준비부터 영상 촬영·편집까지 꼬박 하루가 소요되었습니다. 특히 도메인 인증의 경우 MCP 서버에 .well-known 경로를 서빙하는 핸들러를 급히 추가해야 했고, 테스트 케이스는 "사용자가 치킨을 검색 → 가게 상세 확인 → 요기요 앱으로 주문"과 같은 End-to-End 시나리오를 플랫폼별로 작성해야 했습니다.

등록 제출 이후에는 OpenAI의 리뷰 프로세스를 기다려야 했습니다. 약 50일이 지난 뒤 앱이 Approved 상태가 되었고, 그제서야 ChatGPT 사용자들이 실제로 요기요 배달 앱을 사용할 수 있게 되었습니다. 개발 완료부터 실제 출시까지의 리드 타임이 생각보다 길었기 때문에, 앱 등록에 필요한 준비물을 개발 단계에서 미리 파악하고 병행하는 것이 중요하다는 교훈을 얻었습니다.

11. 개발하면서 만난 것들

CSP 위반은 소리 없이 실패한다

CSP 도메인을 하드코딩하면 환경 전환이 어렵다

ESM의 import 평가 순서

SSE가 APM에 안 보이는 것처럼 느껴졌던 점

위젯 버전과 MCP 서버

12. 마치며

ChatGPT Apps SDK와 MCP를 쓰면, "대화 + 구조화된 도구 호출 + 인라인 위젯"을 한 번에 설계할 수 있었습니다. 툴 스키마와 위젯 매핑을 잘 나누어 두면, 이후에 툴을 추가하거나 위젯만 바꾸는 확장도 수월했습니다.

특히 CSP는 ChatGPT sandbox 환경에서 위젯을 만들 때 피할 수 없는 주제입니다. "어떤 외부 리소스를 쓰는가"를 위젯 개발 초기부터 파악해두고, MCP 메타데이터에 빠짐없이 선언하는 것이 디버깅 시간을 크게 줄여줍니다. 조용히 실패하는 특성 때문에, CSP 관련 문제는 알고 있으면 5분, 모르면 반나절이 걸립니다.

앞으로는 더 많은 툴(예: 주문·결제 플로우), 위젯 UX 개선, 그리고 사용 지표 연동까지 이어갈 수 있을 것 같습니다.

이 글은 ChatGPT App(요기요 배달 연동) 프로젝트의 기술적 구현과 개발 (Vibe Code with AI)경험을 정리한 글입니다. 실제 서비스 정책이나 API 스펙은 OpenAI 문서를 참고해 주세요. 이 글의 초안 작성과 구성에는 AI가 활용되었으며, 개발자가 검토·보완하여 완성하였습니다.

참고 링크


ChatGPT에서 요기요 배달 쓰기 --- MCP + 위젯 연동 개발기 was originally published in YOGIYO Tech Blog - 요기요 기술블로그 on Medium, where people are continuing the conversation by highlighting and responding to this story.