YouMind
로그인

ChatGPT Intelligent UI 역공학: 실제 작동 원리 공개

@rabi_guha
영어2026년 10월 08일
206K
2.0K
146
66
3.5K

TL;DR

본 기사는 ChatGPT에서 최근 출시한 Intelligent UI의 기술적 아키텍처를 상세히 다루며, 맞춤형 추론 언어(DIL) 사용, JavaScript 및 JSON으로의 서버 측 컴파일, 그리고 샌드박스 워커를 통한 안전한 클라이언트 측 실행을 설명합니다.

ChatGPT 가 드디어 Generative UI 를 출시했고, 하룻밤 사이에 해당 분야의 명칭을 Intelligent UI 로 바꿔버렸습니다. 우리는 이들이 이를 어떻게 구현했는지 직접 확인해 봐야 했습니다.

https://x.com/OpenAI/status/2107894997538525580

이 글은 OpenAI 가 자사의 핵심 기능인 Intelligent UI 를 어떻게 구현했는지, 그리고 웹과 모바일 전반에서 레이어, 포맷, 네이티브 렌더링을 어떻게 처리하는지 심층적으로 분석한 내용입니다.

구성 요소

ChatGPT 의 구현 방식은 작업을 모델, 백엔드 서버, 클라이언트로 나눕니다.

  • 추론 포맷: 모델은 Markdown 에 JSX 스타일 태그와 JavaScript 를 결합한 DIL 언어로 인터페이스를 작성합니다.
  • 서버 측 컴파일: 서버는 각 부분 응답을 JavaScript 프로그램과 텍스트 및 데이터로 구성된 JSON 문서로 변환합니다.
  • 클라이언트 런타임: 샌드박스 환경의 런타임이 프로그램을 실행하고 UI 연산을 생성합니다.
  • 렌더링: ChatGPT 는 자체 네이티브 컴포넌트에 해당 연산을 적용합니다.
  • 디자인 시스템 및 카탈로그: 모델이 사용할 수 있는 컴포넌트, 속성, 디자인 토큰을 의미합니다.
Rabi Shanker Guha - inline image

ChatGPT Intelligent UI 아키텍처 다이어그램

추론 포맷

이는 모델이 작성하는 결과물입니다. ChatGPT 에서 이는 OpenAI 가 DIL 이라고 부르는 언어로, 산문용 Markdown, 컴포넌트용 JSX 스타일 태그, 상태 및 로직용 JavaScript 로 구성됩니다. 하나의 짧은 응답 예시를 통해 모든 레이어를 살펴보겠습니다.

text
1## 팀 플랜 예상 비용
2슬라이더를 드래그하여 팀의 **월간 요금**을 확인하세요.
3{@body const [seats,setSeats] = DIL.useState(8)}
4{@body const price = seats*29}
5<box border padding={3} gap={2}>
6 <slider min={1} max={50} value={seats} onChange={setSeats}/>
7 <title size="xl">${price}/mo</title>
8</box>

제목과 단락은 일반적인 Markdown 입니다. 태그는 ChatGPT 카탈로그에 있는 컴포넌트입니다. 두 개의 {@body …} 줄은 JavaScript 로, 첫 번째 줄은 seats 라는 상태를 선언하고 두 번째 줄은 이를 기반으로 price 를 도출합니다. 슬라이더는 seats 에 바인딩되어 있으므로, 슬라이더를 움직이면 요금이 업데이트됩니다.

모델이 인터페이스를 토큰 단위로 작성하기 때문에 전용 포맷이 필요합니다.

  • 안정적으로 작성하기 쉬워야 합니다. 따라서 모델이 이미 잘 알고 있는 표기법으로 구성됩니다.
  • 절반만 작성된 상태에서도 사용 가능해야 합니다. 구문은 각각 독립된 줄에 위치하며, 열려 있는 모든 요소는 자동으로 닫힐 수 있습니다. 덕분에 서버는 부분 응답을 마지막 완성된 구조에서 끊어내더라도 여전히 컴파일할 수 있습니다.

서버 측 컴파일

클라이언트는 모델의 출력물을 작성된 그대로 절대 실행하지 않습니다. OpenAI 의 서버는 이를 JavaScript 프로그램과 JSON 문서로 컴파일하며, 이들은 메시지와 함께(model_dil_v2 로) 저장됩니다. 해당 응답은 다음과 같이 컴파일됩니다(가독성을 위해 포맷팅함).

javascript
1function __dilSafe(evaluate, failureValue) {
2 try { return evaluate(); } catch { return failureValue; }
3}
4
5DIL.render(__dil.jsx(() => {
6 const __dilConstants = DIL.useConstants();
7 const __dilModelDataBindings = DIL.useAppData((appData) => appData.opGenui?.modelDataBindings ?? {});
8 const [seats, setSeats] = DIL.useState(8, { key: "seats" });
9 const price = __dilSafe(() => seats * 29, undefined);
10 return __dil.jsx(__dil.Fragment, null,
11 __dil.jsx("title", { size: "lg" }, __dilConstants["0"]),
12 __dil.jsx("text", null, __dilConstants["1"], __dil.jsx("bold", null, __dilConstants["2"]), __dilConstants["3"]),
13 __dil.jsx("box", { border: true, padding: 3, gap: 2 },
14 __dilSafe(() => __dil.jsx("slider", { min: 1, max: 50, value: seats, onChange: setSeats }), null),
15 __dil.jsx("title", { size: "xl" }, __dilConstants["4"], __dilSafe(() => price, null), __dilConstants["5"])));
16}, { key: "body:2" }));
json
1{
2 "constants": {
3 "0": "팀 플랜 예상 비용",
4 "1": "슬라이더를 드래그하여 팀의 ",
5 "2": "월간 요금",
6 "3": "을 확인하세요.",
7 "4": "$",
8 "5": "/월"
9 },
10 "appData": { "opGenui": { "componentResults": {}, "modelDataBindings": {} } }
11}

Markdown 은 컴포넌트와 동일한 트리로 컴파일됩니다. 제목은 title 이 되고, 단락은 내부에 bold 가 포함된 text 가 되며, 단어들은 constants 테이블로 이동합니다.

컴파일 과정에서는 모든 클라이언트가 반복해서 수행해야 할 작업을 대신 처리합니다.

  • 일반 함수 호출. 마크업은 __dil.jsx 호출로 변환되므로, JavaScript 런타임이 DIL 파서 없이도 프로그램을 평가할 수 있습니다.
  • 오류 격리. 표현식은 __dilSafe 로 래핑되므로, 오류를 발생시키는 표현식이 있어도 전체 렌더링이 중단되지 않고 해당 요소 하나만 제거됩니다.
  • 별도의 테이블에 텍스트 저장. 정적 텍스트는 constants 테이블로 이동하므로, 응답이 스트리밍될 때 늘어나는 텍스트가 프로그램이 아닌 데이터만 변경하게 됩니다.
  • 안정적인 상태 키. 각 상태 조각에는 키({ key: "seats" })가 부여되어, 재컴파일이 이루어져도 값이 유지됩니다.
  • 복구 및 유효성 검사. 불완전한 구문과 태그는 삭제되고, 닫히지 않은 요소는 자동으로 닫히며, 카탈로그 기준 유효성 검사에 실패한 속성은 제거되고 진단 정보로 기록됩니다.

JSON 문서는 텍스트 상수와 이미지 검색 결과처럼 서버가 응답을 위해 처리하는 모든 데이터를 담고 있습니다(데이터 참조).

클라이언트 런타임

클라이언트는 컴파일된 프로그램과 JSON 문서를 수신합니다. 작업은 프로그램을 실행하는 런타임과 결과를 그리는 렌더러로 나뉩니다.

프로그램은 모델이 작성한 코드이므로 ChatGPT 페이지에서 직접 실행되지 않습니다. ChatGPT 는 allow-scripts 와 default-src 'none' 콘텐츠 보안 정책으로 샌드박스 처리된 숨겨진 iframe(runner.html)을 로드하며, 이 iframe 이 Web Worker 를 시작합니다.

  • 잠금(Lockdown). 프로그램을 평가하기 전에 워커는 전역 스코프에서 네트워크 접근, 타이머, 메시징, 동적 코드 평가를 제거하고 나머지 전역 변수를 동결합니다.
  • 평가(Evaluation). 그런 다음 new Function 으로 프로그램을 평가합니다. 런타임 객체(DIL, __dil, GenUI)와 카탈로그의 복합 컴포넌트가 매개변수로 전달됩니다.
  • 워치독(Watchdog). 시간 제한 내에 응답하지 않는 프로그램은 격리되고 워커가 재시작됩니다.

런타임은 React 스타일의 작은 리콘실러(reconciler)입니다. 컴포넌트를 렌더링하고 훅 상태를 키가 지정된 슬롯에 유지합니다. 이후 결과 트리를 이전 트리와 비교하여 차이점을 연산 목록으로 인코딩합니다. 화면에 직접 그리는 작업은 하지 않습니다.

다음 예시는 첫 렌더링에서 생성되는 연산을 보여주며, 한 줄이 하나의 노드를 나타냅니다. 각 요소의 속성 이름을 나열하는 항목은 생략했습니다.

text
1CREATE #1 title SET size = "lg" PLACE under root at 0
2CREATE #2 text "팀 플랜 예상 비용" PLACE under #1 at 0
3CREATE #3 text PLACE under root at 1
4CREATE #4 text "슬라이더를 드래그하여 팀의 " PLACE under #3 at 0
5CREATE #5 bold PLACE under #3 at 1
6CREATE #6 text "월간 요금" PLACE under #5 at 0
7CREATE #7 text "을 확인하세요." PLACE under #3 at 2
8CREATE #8 box SET border = true, padding = 3, gap = 2 PLACE under root at 2
9CREATE #9 slider SET min = 1, max = 50, value = 8, onChange = fn#1 PLACE under #8 at 0
10CREATE #10 title SET size = "xl" PLACE under #8 at 1
11CREATE #11 text "$" PLACE under #10 at 0
12CREATE #12 text "232" PLACE under #10 at 1
13CREATE #13 text "/월" PLACE under #10 at 2

함수는 워커 밖으로 절대 나가지 않으며, 슬라이더의 핸들러는 식별자(fn#1)로만 전송됩니다. 네트워크 전송 시 연산은 정수의 바이너리 시퀀스로 인코딩되고, 문자열은 별도의 테이블에 보관됩니다.

렌더링

ChatGPT 페이지는 자체 컴포넌트 트리에 연산을 적용합니다. 각 CREATE 는 ChatGPT 디자인 시스템의 네이티브 컴포넌트를 인스턴스화하며, 페이지는 변경 사항이 도착하는 대로 애니메이션을 적용합니다. 페이지는 알려진 컴포넌트 유형에 대한 연산만 허용하므로, 모델 출력이 임의의 마크업이나 스타일을 삽입할 수 없습니다. 예외는 일부 속성이 허용하는 원시 CSS 값(디자인 시스템 및 카탈로그 참조)과 iframe 에서 실행되는 AppBlock 앱(이스케이프 해치 참조)입니다.

상호작용은 반대 방향으로 진행됩니다. 사용자가 슬라이더를 9 로 드래그하면, 페이지는 핸들러의 식별자와 인수를 워커로 보냅니다. 워커는 setSeats(9) 를 호출하고 다시 렌더링한 뒤 업데이트 연산을 반환합니다. 이때 모델 호출은 발생하지 않습니다.

디자인 시스템 및 카탈로그

카탈로그는 모델이 요청할 수 있는 범위를 정의합니다. 모델이 원시 레이아웃과 스타일링 규칙만으로 인터페이스를 구축하지 않기 때문에 이 카탈로그가 필요합니다. 모델은 ChatGPT 가 이미 그리는 방법을 아는 컴포넌트 중에서 선택하고, padding={3} 같은 디자인 토큰으로 스타일을 지정합니다. 픽셀 너비나 16 진수 색상 같은 원시 CSS 값도 일부 속성에서 허용되지만, 디자인 토큰 사용이 권장됩니다. 그 결과 다음과 같은 이점이 생깁니다.

  • 생성된 인터페이스가 모든 플랫폼에서 ChatGPT 의 나머지 부분과 자연스럽게 어울립니다.
  • 컴파일러가 출력물을 검증할 스키마를 갖게 됩니다. 컴포넌트에 존재하지 않는 속성이나 잘못된 유형의 리터럴은 컴파일 중에 제거되고 진단 정보로 기록됩니다.

우리가 캡처한 한 응답에서 컴파일러는 두 가지 속성을 제거했습니다. icon 의 fill(unknown_prop 진단)과 box 의 gap="1"(invalid_literal 진단)입니다.

카탈로그는 세 부분으로 구성됩니다.

  • 네이티브 컴포넌트. 약 70 개의 컴포넌트가 ChatGPT 클라이언트 코드의 컴포넌트 레지스트리에 정의되어 있으며, 우리가 캡처한 응답 중 39 개가 등장했습니다.
  • 간격, 반경, 색상, 크기를 위한 디자인 토큰.
  • image 및 product 컴포넌트처럼 OpenAI 가 DIL 로 작성하여 사전 빌드된 상태로 샌드박스에 보내는 복합 컴포넌트. 우리가 캡처한 사례에서 모델은 이러한 컴포넌트를 사용했지만, 자체적으로 정의하지는 않았습니다.

스트리밍

텍스트 스트리밍은 간단합니다. 새로운 토큰이 화면에 이미 표시된 내용에 계속 추가되기 때문입니다. 하지만 인터페이스 스트리밍은 세 가지 이유로 훨씬 어렵습니다.

  • 출력물은 대개 아직 실행 가능한 상태가 아닙니다. 대부분의 순간 태그나 표현식이 열린 채로 남아 있는 불완전한 프로그램이므로, 작성된 그대로 실행할 수 없습니다.
  • 인터페이스가 확장되는 동안에도 계속 작동해야 합니다. 사용자가 이미 조작한 컴포넌트는 상태를 유지해야 합니다.
  • 일부 콘텐츠는 별도로 도착합니다. 이미지 같은 데이터는 텍스트가 아닌 서버에서 제공됩니다.

서버 측 스트리밍

단순히 토큰을 이어 붙이는 스트림으로는 이를 표현할 수 없습니다. 대신 ChatGPT 는 원시 텍스트, 컴파일된 프로그램, 데이터를 나란히 담고 있는 구조화된 메시지에 패치를 스트리밍합니다.

응답은 서버 전송 이벤트 스트림(POST /backend-api/f/conversation)을 통해 브라우저에 도달합니다. 각 이벤트는 생성 중인 메시지에 대한 JSON-Patch 스타일 업데이트입니다. 단일 이벤트는 보통 원시 DIL 텍스트와 그 컴파일 형태를 함께 업데이트합니다. 다음은 캡처된 응답의 업데이트 중 하나를 축약한 것입니다.

json
1{"o": "patch", "v": [
2 {"p": "/message/content/parts/0", "o": "append", "v": " 친구들과 함께하는 일요일 양고기 로스트 — 푸짐한 음식, …"},
3 {"p": "/message/metadata/model_dil_v2/code", "o": "replace", "v": "DIL.render(__dil.jsx(()=>{…"},
4 {"p": "/message/metadata/model_dil_v2/constants", "o": "append", "v": {"0": "친구들과 제대로 된 일요일 양고기 로스트를 즐기기 위한 계획이에요 — …"}},
5 {"p": "/message/metadata/model_dil_v2/constants", "o": "append", "v": {"1": "당신이"}},
6 {"p": "/message/metadata/model_dil_v2/fallbackMarkdown", "o": "append", "v": " 친구들과 함께하는 일요일 양고기 로스트 — …"}
7]}

서버는 점진적으로 컴파일하지 않습니다. 수백 밀리초마다, 아마도 모델 출력의 새로운 청크가 생길 때마다, 모델이 지금까지 작성한 모든 것을 다시 컴파일하고 결과를 전송합니다. 컴파일은 어떤 태그도 나타나기 전인 첫 번째 토큰부터 시작됩니다.

컴파일러는 다음 사항을 처리해야 합니다.

  • 절반만 작성된 응답 컴파일
  • 텍스트 업데이트
  • UI 업데이트

타임라인은 대략 다음과 같습니다.

Rabi Shanker Guha - inline image

GIF

클라이언트 측 스트리밍

페이지는 새로운 업데이트를 모두 샌드박스 워커로 전달합니다. 워커는 이를 평가하고 기존 상태로 다시 렌더링한 뒤, 업데이트 연산을 페이지로 보냅니다. 컴파일 중에 추가된 키 덕분에 상태는 재컴파일이 이루어져도 값을 유지합니다. 새 프로그램이 평가나 렌더링에 실패하면, 워커는 마지막으로 정상 작동했던 프로그램을 유지합니다.

그런 다음 페이지는 각 변경 사항에 애니메이션을 적용합니다.

  • 텍스트는 0.7 초에 걸쳐 페이드인됩니다.
  • 새 행과 그리드 항목은 0.42 초에 걸쳐 슬라이드인됩니다.
  • 차트는 1.8 초에 걸쳐 그려집니다.
  • 컨테이너 높이는 갑자기 변하지 않고 부드럽게 전환됩니다.

이스케이프 해치: iframe 내 앱인 AppBlock

Rabi Shanker Guha - inline image

ChatGPT 가 생성한 인라인 앱

Web Audio 로 소리를 합성하는 드럼 머신처럼 네이티브 컴포넌트로 처리할 수 없는 요청도 있습니다. 이 경우 모델은 AppBlock 을 작성할 수 있습니다. AppBlock 은 HTML, CSS, JavaScript 로 구성된 독립형 웹 앱으로 응답에 포함됩니다. 다음은 그중 하나를 축약한 시작 부분입니다.

text
1<AppBlock title="Drum Lab" icon="app-chatgpt" variant="inline" app_block_id="drum-lab-01">
2<div id="dl" class="w-full min-w-0 space-y-4 text-base">
3 <style>
4 #dl{color:var(--viz-text)}#dl button{touch-action:manipulation}#dl .panel{background:var(--viz-panel);border:1px solid var(--viz-border);border-radius:15px}…
5 </style>
6 …
7 <button id="dl-play" class="btn" style="background:var(--viz-text);color:var(--viz-card);min-width:100px">▶ Play</button>
8 …
9</div>
10<script>
11(function(){
12const root=document.getElementById('dl');if(root.dataset.init)return;root.dataset.init="yes";
13…
14function audioInit(){if(!audio){const C=window.AudioContext||window.webkitAudioContext; if(!C)return false;audio=new C();…
15…
16})();
17</script>
18</AppBlock>

AppBlock 은 Intelligent UI 컴포넌트와는 다른 방식으로 렌더링됩니다.

모든 것의 통합

사용자가 프롬프트를 입력합니다. 모델이 인터페이스 작성을 시작하면, 서버는 이를 ChatGPT 가 실행할 수 있는 형태로 바꾸고, 페이지는 응답이 스트리밍되는 대로 조각조각 이를 조립합니다. 인터페이스가 완성되면 슬라이더를 움직이거나 체크박스를 선택하는 동작은 모델에 다시 요청하지 않고 로컬에서 즉시 반영됩니다.

모델 전용 언어, 서버 컴파일 단계, 네이티브 렌더러, 그리고 탄탄한 디자인 시스템이 이 모든 과정을 하나로 엮어줍니다. 각 부분은 핵심적인 역할을 수행하며, 이들이 모여 전 세계 수십억 사용자에게 차세대 AI 네이티브 인터페이스를 제공합니다. 정말 놀라운 시대입니다!

Rabi Shanker Guha - inline image

ChatGPT Intelligent UI 스크린샷

조사 방법론

모든 관찰 결과는 저희의 ChatGPT 계정, ChatGPT 웹 앱이 생성하는 트래픽, 그리고 chatgpt.com 이 공개적으로 제공하는 JavaScript 를 바탕으로 도출되었습니다. 2026 년 10 월에 GPT-6 및 GPT-6 Thinking 모델을 사용해 진행했습니다.

분석은 Codex 와 Claude 의 도움을 받아 수행되었습니다. 작성은 Codex, 시각화는 Claude 를 활용했습니다.

(더 자세한 버전은 https://www.openui.com/blog/how-chatgpt-intelligent-ui-works 에서 확인할 수 있습니다.)

원클릭 저장

YouMind로 바이럴 글을 AI 심층 읽기

소스를 저장하고, 핵심 질문을 던지고, 주장을 요약해 바이럴 글을 다시 활용할 수 있는 노트로 바꾸세요. 하나의 AI 워크스페이스에서 모두 할 수 있습니다.

YouMind 둘러보기
크리에이터를 위해

당신의 Markdown을 깔끔한 𝕏 글로

직접 쓴 장문을 올릴 때 이미지, 표, 코드 블록을 𝕏에 맞게 정리하는 일은 번거롭습니다. YouMind는 전체 Markdown 초안을 깔끔하고 바로 게시할 수 있는 𝕏 글로 바꿔 줍니다.

Markdown → 𝕏 사용해 보기

분석할 패턴 더 보기

최근 바이럴 아티클

더 많은 바이럴 아티클 보기