
OpenAI Apps SDK 해설: ChatGPT 앱은 위젯보다 MCP 도구 계약·권한·UI 경계를 먼저 설계해야 하는 이유
OpenAI Apps SDK를 ChatGPT 위젯 제작 도구가 아니라 MCP 서버, 도구 descriptor, 권한, structuredContent, iframe UI 경계로 설계해야 하는 이유를 실무 관점에서 정리합니다.

1. 한 줄 문제 정의
핵심 요약: Apps SDK의 본질은 ChatGPT 안에 예쁜 화면을 넣는 일이 아니라, 모델이 호출할 수 있는 도구와 사용자가 믿고 조작할 수 있는 UI 사이의 계약을 만드는 일입니다.
OpenAI Apps SDK는 ChatGPT 안에서 동작하는 앱을 만들기 위한 프레임워크입니다. 공식 문서 기준으로 앱은 MCP 서버가 제공하는 도구와, 선택적으로 ChatGPT iframe 안에서 렌더링되는 UI 컴포넌트로 구성됩니다. 따라서 이 글의 대상 독자는 ChatGPT 앱, 사내 업무 커넥터, 고객 지원 보조 도구, 데이터 조회/수정형 어시스턴트를 만들려는 개발자입니다.
적용 범위는 모델이 외부 시스템의 데이터를 읽거나 쓰고, 그 결과를 ChatGPT 안의 위젯으로 보여주는 제품입니다. 반대로 단순 질의응답, 문서 요약, 내부 FAQ처럼 외부 도구 호출이나 사용자 조작 UI가 필요 없는 경우에는 Apps SDK가 과한 선택일 수 있습니다. 이 글은 “앱을 어떻게 등록하나”보다 “등록 전에 어떤 경계를 설계해야 사고가 줄어드나”에 집중합니다.
2. 먼저 결론
핵심 요약: Apps SDK를 바로 도입해도 되는 팀은 MCP 도구 계약, 권한 검증, UI 상태 복구를 코드로 책임질 준비가 된 팀입니다.
저는 Apps SDK를 “ChatGPT용 프론트엔드 SDK”가 아니라 “대화형 MCP 제품 표면”으로 보는 편이 맞다고 봅니다. 서버는 도구 이름, 입력 스키마, 출력 스키마, 읽기/쓰기 힌트, 인증 범위, UI 리소스 위치를 선언합니다. ChatGPT 모델은 이 정보를 보고 언제 어떤 도구를 호출할지 판단합니다. 위젯은 그 결과를 사용자가 조작할 수 있는 형태로 보여줍니다.
도입을 추천하는 경우는 명확합니다. 이미 API가 있고, 사용자별 권한 모델이 있으며, 읽기 도구와 쓰기 도구를 구분할 수 있고, 실패 로그를 남길 수 있는 팀입니다. 아직 데이터 모델도 불안정하고 “일단 ChatGPT에 앱처럼 보여주자”가 목표라면 먼저 MCP 서버와 도구 스키마만 작게 실험하는 편이 낫습니다. 화면은 나중에 붙여도 되지만, 잘못 설계된 도구 계약은 앱 심사, 보안, 사용자 신뢰를 동시에 흔듭니다.
3. 핵심 구조 분해
핵심 요약: Apps SDK 앱은 MCP 서버, 도구 descriptor, structuredContent, widget iframe, MCP Apps bridge 다섯 부분으로 나눠 보면 이해가 쉽습니다.
첫 번째 계층은 MCP 서버입니다. 서버는 /mcp 엔드포인트를 통해 도구 목록과 호출 결과를 제공합니다. 공식 빠른 시작 문서는 ChatGPT 앱을 만들려면 MCP 서버가 필수이고, UI 컴포넌트는 선택이라고 설명합니다. 이 말은 곧 “앱의 중심은 화면이 아니라 서버가 제공하는 능력”이라는 뜻입니다.
두 번째 계층은 도구 descriptor입니다. 도구 이름, 설명, 입력 스키마, 출력 스키마, 인증 정보, 읽기/쓰기 힌트, UI 리소스 URI가 여기에 들어갑니다. 이 descriptor는 모델이 읽는 사용 설명서와 같습니다. 이름이 모호하거나 description이 과장되면 모델이 엉뚱한 순간에 도구를 호출하고, 사용자에게도 왜 권한 요청이 뜨는지 설명하기 어려워집니다.
세 번째 계층은 도구 응답입니다. 응답은 보통 structuredContent, content, _meta로 나뉩니다. structuredContent와 content는 모델이 읽을 수 있으므로 작고 명확해야 합니다. _meta는 위젯 전용 데이터로 쓰이며 모델에게 보이지 않는다는 점이 중요합니다. 큰 원본 데이터, 내부 ID 맵, UI 렌더링용 상세 정보는 이쪽에 두는 편이 안전합니다.
네 번째 계층은 UI 리소스입니다. ChatGPT는 MCP Apps UI 표준을 구현하고, 위젯은 iframe 안에서 실행됩니다. 다섯 번째 계층은 iframe과 ChatGPT host 사이의 bridge입니다. 공식 레퍼런스는 JSON-RPC 2.0 over postMessage와 ui/* 메서드/알림, tools/call 호출을 기본 구조로 설명합니다. 쉽게 말해 위젯은 독립 웹앱처럼 보이지만, 실제로는 ChatGPT host와 약속된 메시지 프로토콜로 대화합니다.
4. 설계 의도 해설
핵심 요약: OpenAI가 Apps SDK를 MCP 위에 올린 이유는 앱 기능을 모델, 서버, UI 중 한 곳에 몰아넣지 않기 위해서입니다.
전통적인 플러그인형 구조에서는 서버 API가 모든 일을 설명하고, 챗봇은 API 호출 결과를 텍스트로 요약하는 경우가 많았습니다. Apps SDK는 여기에 UI 계층을 더합니다. 하지만 UI가 서버의 책임을 대신하지는 않습니다. 서버는 여전히 권한을 검사하고, 도구 호출을 검증하고, side effect를 실행하며, 감사 로그를 남겨야 합니다.
이 구조가 얻는 장점은 역할 분리입니다. 모델은 어떤 도구가 필요한지 판단합니다. 서버는 실제 데이터를 처리합니다. 위젯은 조작 가능한 화면을 제공합니다. 대신 비용도 있습니다. 개발자는 API, 대화, iframe UI, MCP bridge, 인증, 심사 가이드라인을 동시에 고려해야 합니다. 그래서 단순 웹앱을 ChatGPT 안으로 “그냥 임베드”하는 접근은 위험합니다.
제가 보는 핵심 설계 의도는 재사용성입니다. 공식 레퍼런스는 ChatGPT가 MCP Apps 표준을 구현하고, MCP Apps 표준 필드와 ui/* bridge를 기본으로 쓰라고 권장합니다. 이는 ChatGPT 전용 API에만 기대지 말고, 가능한 한 MCP Apps 표준 위에 앱을 올리라는 신호입니다. ChatGPT 전용 window.openai 확장은 파일 선택, 모달, 결제 같은 특수 기능이 정말 필요할 때만 붙이는 것이 운영상 유리합니다.
5. 근거 및 비교
핵심 요약: Apps SDK의 경쟁 상대는 “일반 웹앱”, “텍스트-only MCP 커넥터”, “사내 챗봇 UI”이며, 선택 기준은 UI 필요성보다 권한과 side effect 관리입니다.
| 접근 | 맞는 상황 | 장점 | 비용/한계 | 판단 기준 |
|---|---|---|---|---|
| Apps SDK + MCP UI | ChatGPT 대화 안에서 조회, 수정, 선택, 승인 화면이 필요할 때 | 모델 호출과 위젯 조작을 한 흐름으로 연결할 수 있음 | MCP 서버, UI bridge, CSP, 심사 기준까지 설계해야 함 | 사용자가 ChatGPT 안에서 조작해야 업무 시간이 줄어드는가 |
| 텍스트-only MCP 서버 | 읽기 중심 도구, 간단한 상태 조회, 내부 자동화 | 구현이 작고 검증이 쉬움 | 복잡한 선택/비교/편집 UI는 불편함 | 결과가 표나 짧은 요약만으로 충분한가 |
| 기존 웹앱 + 링크 | 이미 완성된 화면과 로그인/권한 체계가 있을 때 | 기존 UX, 분석, 배포 체계를 그대로 사용 | 대화 맥락과 도구 호출이 분리됨 | ChatGPT 안에서 끝내야 할 이유가 약한가 |
| 자체 챗봇 UI | 브랜드 경험, 세밀한 권한 흐름, 자체 고객 채널이 중요할 때 | 전체 UI와 정책을 직접 통제 | 모델 선택, 도구 호출, 사용자 인증, UI를 모두 직접 운영 | ChatGPT 배포보다 자체 채널 통제가 더 중요한가 |
공식 문서의 중요한 근거는 세 가지입니다. 첫째, Apps SDK 앱은 MCP 서버가 필수이고 UI는 선택입니다. 둘째, 도구 응답은 모델이 읽는 데이터와 위젯만 받는 데이터를 구분합니다. 셋째, 보안 문서는 최소 권한, 명시적 동의, 서버 측 입력 검증, 감사 로그, PII redaction을 반복해서 강조합니다. 이 세 가지를 합치면 결론은 분명합니다. Apps SDK의 품질은 위젯의 화려함이 아니라 도구 계약의 정확도에서 결정됩니다.
6. 실제 동작 흐름 / 단계별 실행 방법
핵심 요약: 처음부터 완성 앱을 만들지 말고, 읽기 도구 하나와 최소 위젯 하나로 계약을 검증한 뒤 쓰기 도구를 붙이는 순서가 안전합니다.
- 업무 의도 하나를 고릅니다. 예를 들어
list_customer_tickets처럼 읽기 도구부터 시작합니다. “고객 지원 전체 관리”처럼 큰 이름은 피합니다. - 입력 스키마를 좁힙니다.
customer_id,status,limit처럼 실제 처리에 필요한 필드만 받습니다. 전체 대화 기록, 자유형 context, 불필요한 위치 정보는 받지 않습니다. - 출력 스키마를 먼저 고정합니다. 위젯과 모델이 함께 읽을
structuredContent는 작게 유지합니다. 예: 티켓 5개 요약, 상태, 마지막 업데이트 시각. - 위젯 전용 상세 데이터는
_meta로 분리합니다. 내부 ID 맵, 페이지네이션 커서, UI 렌더링용 전체 배열은 모델에게 보여줄 필요가 없습니다. - UI 리소스 URI를 버전 관리합니다. 문서가 권장하듯 깨지는 변경이 있으면
ui://widget/tickets-v2.html처럼 새 URI를 씁니다. URI는 사실상 캐시 키입니다. - MCP Inspector로 먼저 호출합니다. ChatGPT에 연결하기 전
npx @modelcontextprotocol/inspector@latest --server-url localhost:8787/mcp --transport http처럼 도구 목록, 스키마, 응답을 확인합니다. 실제 실행 시에는 서버 URL 앞에 필요한 프로토콜을 붙입니다. - Developer Mode에서 connector를 붙입니다. 로컬이면 ngrok 같은 터널로 HTTPS MCP URL을 만들고, ChatGPT 설정에서 connector를 등록합니다.
- 쓰기 도구는 마지막에 추가합니다.
update_ticket_status처럼 외부 상태를 바꾸는 도구는 approval, idempotency key, 감사 로그, 롤백 정책을 붙인 뒤 공개합니다.
registerAppTool(server, "list_customer_tickets", {
title: "List customer tickets",
description: "Returns recent support tickets for a customer without modifying them.",
inputSchema: { customerId: z.string(), limit: z.number().min(1).max(10) },
outputSchema: {
tickets: z.array(z.object({
id: z.string(),
title: z.string(),
status: z.string(),
updatedAt: z.string()
}))
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false
},
_meta: { ui: { resourceUri: "ui://widget/tickets-v1.html" } }
}, async ({ customerId, limit }) => {
const rows = await db.tickets.list({ customerId, limit });
return {
structuredContent: {
tickets: rows.map(({ id, title, status, updatedAt }) => ({ id, title, status, updatedAt }))
},
content: [{ type: "text", text: `Found ${rows.length} recent tickets.` }],
_meta: { rowsById: Object.fromEntries(rows.map((row) => [row.id, row])) }
};
});
7. 실수/함정
핵심 요약: Apps SDK에서 자주 터지는 문제는 대부분 모델을 너무 믿거나, 위젯을 너무 믿거나, descriptor를 대충 쓰는 데서 나옵니다.
함정 1: description을 마케팅 문구처럼 쓰는 경우
도구 설명에 “가장 좋은”, “무조건 사용”, “모든 상황에서” 같은 문구를 넣으면 모델 선택을 왜곡하고 심사에서도 불리합니다. 예방책은 동사형 이름과 실제 행동만 적는 것입니다. 복구는 간단합니다. 도구별로 “무엇을 읽는가/무엇을 바꾸는가/언제 쓰면 안 되는가”를 한 문장으로 다시 씁니다.
함정 2: structuredContent에 너무 많은 데이터를 넣는 경우
모델이 읽는 영역에 원본 레코드, 개인정보, 내부 토큰, 긴 HTML을 넣으면 비용과 보안 위험이 함께 커집니다. 예방책은 모델이 판단에 필요한 요약만 structuredContent에 두고, 위젯 렌더링용 상세 데이터는 _meta에 두는 것입니다. 이미 섞였다면 응답 객체를 “모델용/위젯용/로그용” 세 그룹으로 다시 나눕니다.
함정 3: 쓰기 도구를 retry-safe하게 만들지 않는 경우
공식 문서는 모델이 도구 호출을 재시도할 수 있으니 handler를 idempotent하게 설계하라고 말합니다. 주문 생성, 메시지 발송, 결제 링크 생성 같은 도구가 재시도될 때 중복 실행되면 사용자 피해가 생깁니다. 예방책은 idempotency key, 서버 측 중복 감지, 명시적 승인, 실행 후 상태 조회를 넣는 것입니다.
함정 4: 위젯 iframe 보안을 일반 웹앱처럼 착각하는 경우
위젯은 sandboxed iframe에서 실행되고 CSP 제약을 받습니다. clipboard, prompt, confirm 같은 일부 privileged browser API를 기대하면 동작하지 않을 수 있습니다. 예방책은 공식 CSP metadata에 필요한 connect/resource/frame domain만 선언하고, 외부 링크나 파일 처리는 지원되는 host API를 feature-detect해서 쓰는 것입니다.
8. 강점과 한계
핵심 요약: Apps SDK의 강점은 대화와 조작 UI를 합치는 것이고, 한계는 앱 품질 책임이 ChatGPT가 아니라 개발자 서버로 넘어온다는 점입니다.
강점은 분명합니다. 사용자는 ChatGPT와 대화하다가 같은 화면에서 표, 카드, 폼, 목록을 조작할 수 있습니다. 모델은 도구 결과를 읽고 설명하며, 위젯은 복잡한 선택과 수정 작업을 맡습니다. 특히 고객 지원, 예약, 사내 운영, 데이터 조회, 카탈로그 탐색처럼 대화와 구조화 UI가 함께 필요한 업무에 잘 맞습니다.
한계도 명확합니다. Apps SDK는 권한 문제를 자동으로 해결해 주지 않습니다. 보안 문서는 서버가 모든 입력을 검증하고, 토큰 scope를 매 호출마다 확인하고, PII를 로그에서 지우고, 삭제 요청을 존중해야 한다고 말합니다. 즉 “ChatGPT 안에서 실행되니 안전하겠지”가 아니라 “ChatGPT가 호출해도 우리 서버가 최종 방어선”이라는 태도가 필요합니다.
또 하나의 한계는 리뷰와 배포 비용입니다. 앱 이름, 설명, 도구 annotation, 최소 입력, side effect 설명, 인증 흐름이 모두 심사 대상입니다. 내부 실험용 connector라면 빠르게 돌릴 수 있지만, 공개 배포를 목표로 한다면 제품 문서, 개인정보 처리 방침, 테스트 계정, 오류 처리까지 준비해야 합니다.
9. 더 깊게 공부할 포인트
핵심 요약: 학습 순서는 Apps SDK Quickstart, MCP server guide, Reference, Security & Privacy, Submission guidelines 순서가 가장 덜 헷갈립니다.
- OpenAI Apps SDK Quickstart - MCP 서버와 선택적 UI 컴포넌트가 어떻게 연결되는지 먼저 확인합니다. 확인일: 2026-06-23.
- Build your MCP server - Apps SDK - 도구 descriptor, UI resource,
structuredContent,_meta경계를 깊게 봅니다. 확인일: 2026-06-23. - Apps SDK Reference - MCP Apps 표준 필드,
ui/*bridge,window.openai확장, annotation을 확인합니다. 확인일: 2026-06-23. - Security & Privacy - Apps SDK - 최소 권한, 명시적 동의, prompt injection 대응, 로그 redaction을 점검합니다. 확인일: 2026-06-23.
- App submission guidelines - Apps SDK - 공개 배포 전 도구 이름, 설명, annotation, 인증, 정책 기준을 확인합니다. 확인일: 2026-06-23.
- OpenAI Apps SDK examples repository - React 위젯과 MCP 서버 예제를 실제 코드로 따라봅니다. 확인일: 2026-06-23.
10. 실행 체크리스트 + 작성자 관점
핵심 요약: 저는 Apps SDK 도입 여부를 “화면을 만들 수 있나”가 아니라 “도구 호출을 감사 가능하게 운영할 수 있나”로 판단합니다.
- 앱의 첫 번째 도구가 하나의 명확한 사용자 의도와 연결되어 있는가?
- 읽기 도구와 쓰기 도구가 이름, description, annotation에서 분명히 구분되는가?
structuredContent에는 모델이 읽어도 되는 최소 데이터만 들어가는가?- 위젯 전용 상세 데이터는
_meta에 있고, 토큰이나 비밀값은 포함하지 않는가? - 쓰기 도구에는 idempotency key, 승인 흐름, 감사 로그, 실패 복구 기준이 있는가?
- OAuth scope와 서버 측 권한 검증이 도구 호출마다 적용되는가?
- 위젯 CSP의 connect/resource/frame domain이 필요한 범위로만 제한되어 있는가?
- MCP Inspector와 ChatGPT Developer Mode에서 대표 입력, 빈 입력, 권한 없음, 네트워크 실패를 모두 테스트했는가?
- 공개 배포라면 앱 이름, 설명, 스크린샷, 테스트 계정, privacy policy, submission guideline 대응이 준비되어 있는가?
Definition of Done: 읽기 도구 1개와 쓰기 도구 1개가 MCP Inspector와 ChatGPT Developer Mode에서 같은 스키마로 통과하고, 쓰기 도구의 승인/중복 방지/감사 로그가 실제 서버 로그로 확인되면 첫 내부 베타 준비가 된 상태로 봅니다.
제 추천은 단계적입니다. 1주차에는 텍스트-only MCP 도구로 descriptor와 권한을 검증합니다. 2주차에는 최소 위젯을 붙여 structuredContent와 _meta 분리를 검증합니다. 3주차에야 쓰기 도구를 붙입니다. Apps SDK는 빠르게 데모를 만들 수 있지만, 오래 가는 앱은 위젯보다 도구 계약이 먼저 탄탄합니다.
공유하기
관련 글

GitHub 외부 코딩 에이전트 보안 검증 해설: Claude·Codex가 만든 PR은 모델보다 CodeQL·의존성·시크릿 게이트를 먼저 설계해야 하는 이유
GitHub의 외부 코딩 에이전트 보안 검증 GA를 개발팀 운영 관점으로 해설합니다. Claude·Codex 같은 에이전트가 만든 코드를 PR에 올릴 때 CodeQL, 의존성 검사, 시크릿 스캔을 어떻게 승인 게이트로 묶어야 하는지 정리했습니다.

TypeScript 7.0 RC 해설: Go 네이티브 컴파일러 전환은 속도보다 마이그레이션 경계와 도구 호환성을 먼저 봐야 하는 이유
TypeScript 7.0 RC는 Go 기반 네이티브 컴파일러로 전환되는 큰 변화입니다. 10배 빠른 빌드보다 중요한 것은 tsc 6.0과의 병행, 프로그램 API 공백, CI 검증 경계를 먼저 설계하는 일입니다.

OpenAI 자율 AI 화학자 해설: AI 연구 자동화는 모델 성능보다 실험실 연결·사람 검증·재현 루프를 먼저 설계해야 하는 이유
AI타임스가 전한 OpenAI·Molecule.one의 자율 AI 화학자 프로젝트를 개발자와 AI 도입 담당자 관점에서 해설합니다. GPT-5.4가 Maria Lab과 연결돼 1만80건의 실험을 수행한 사례를 통해, 연구 자동화 시스템에서 모델·실험실·사람 검증 경계를 어떻게 나눠야 하는지 정리했습니다.
AQ 테스트 해보기
지금 내 AI 활용 능력이 어느 수준인지 3분 안에 확인해보세요. 인지력, 활용력, 검증력, 통합력, 윤리감을 한 번에 진단하고 맞춤형 인사이트를 받아볼 수 있습니다.
무료 AQ 테스트 시작하기