← 미리디 아카이브

Warp 데이터 포맷 2026-08-28 원문 대조

Warp IR — 디자인 문서 중간 표현

Figma·HTML 같은 서로 다른 디자인 포맷을 한 스키마로 정규화하는 포맷 중립 문서. 코드상 타입명은 DesignDoc 이다. 담는 것은 레이아웃 의도(layout-intent)이고, 렌더링 방법 — 이를테면 차트를 어떻게 그리는가 — 은 담지 않는다.

스키마 0.10.0 노드 10종 표기 JSON · XML v1 · v2 정본 @warp/core 타입
§1

세 층 — 문서 → 페이지 → 노드

문서 하나가 파일 하나다. 페이지는 Figma 프레임 · HTML 페이지와 1:1 이고, 그 안은 노드 트리 하나다.

DesignDoc                     문서 하나 = 파일 하나
├─ version                    IR 스키마 버전 (현재 0.10.0)
├─ tokens?                    문서 공통 색 · 글자 크기
├─ components[]               재사용 정의 — instance 가 가리킨다
└─ pages[]                    페이지 = Figma 프레임 = HTML 페이지 (1:1)
   ├─ size {w, h?}
   ├─ style?                  페이지 자체 칠 (fills · opacity 만 의미가 있다)
   ├─ background?             순서를 바꿀 수 없는 배경 슬롯 (노드 하나)
   └─ root                    노드 트리의 뿌리 — 보통 layout
      └─ children[]           자식을 갖는 것은 layout · diagram 뿐
JSON 과 XML 은 같은 문서의 두 표기다
.ir.json.ir.xml 은 서로 다른 포맷이 아니라 같은 값의 다른 적기다. 같은지는 표기가 아니라 정규 직렬화가 판정한다.
serialize(fromXml(toXml(doc))) === serialize(doc)
background 가 root 의 첫 자식이 아닌 이유
자식은 재정렬될 수 있다. 재정렬되지 않는 것이 배경을 배경으로 만들기 때문에 페이지의 별도 슬롯으로 뺐다.

문서 레벨 키

versionIR 스키마 버전. 새 필드는 전부 optional 로 들어오므로 옛 문서는 계속 유효하다. 반대로 새 문서를 옛 코드가 읽으면 모르는 필드가 _unknown 에 남아 적합성에서 걸린다 — 버전 스큐가 드러나는 자리문자열 (현재 0.10.0)
pages페이지 배열. 비어 있으면 문서가 아니다Page[]
components재사용 정의. instance.def 가 이 id 를 가리킨다{id, name, root}[]
tokens문서 공통 값. colors 는 이름→#hex, fontSizes 는 이름→px{colors?, fontSizes?}
_unknown읽을 때 몰랐던 필드를 그대로 담아 두는 자리. 쓸 때 다시 펼쳐진다. 값이 있으면 적합성 검사가 WARP_UNKNOWN_FIELDS 로 신고한다 — 손실을 막는 장치이지 정상 상태가 아니다객체

페이지 키

id · name문서 안에서 유일한 식별자 · 사람이 읽는 이름문자열
size캔버스 크기. h 는 생략 가능 — 세로가 내용에 따라 늘어나는 페이지{w, h?} px
root노드 트리의 뿌리Node
style페이지 자신의 칠. fillsopacity 의미가 있다 (페이지에는 테두리·곡률·그림자가 없다)Style
background순서를 바꿀 수 없는 배경 슬롯의 노드Node
§2

이 파일이 IR 인가 — 판별법

XML 표기는 스키마를 모른다. 그래서 IR 이 아닌 문서도 똑같이 생긴 XML 로 적힌다 — 가장 헷갈리는 상대가 적재용 미리캔버스 엔진 데이터다. 파일이 무엇인지 먼저 본다.

Warp IR 문서
루트가 <ir codec="1"> 또는 codec="2"
그 아래 version · pages 가 있다
style.fills[{ kind:"solid", color:"#00000000" }]
색은 #rrggbbaa 문자열
미리캔버스 엔진 (적재용)
designId · nodes 가 보인다
디자인 2.0 JSON — id·designId·rootNodeIds·data·nodes[]
props.shape.fill.renderer / .color
색은 {hex, colorType} 객체
같은 표기, 다른 스키마. 값 매핑은 스키마를 모르기 때문에 둘 다 똑같은 XML 로 적힌다.

⚠ “IR JSON” ≠ “디자인 2.0 JSON”

디자인 2.0 JSON(id/designId/rootNodeIds/data/nodes[])은 미리캔버스 엔진 고유의 템플릿·디자인 저장 포맷이고 IR 과는 별개 스키마다. 엔진은 아직 Warp IR 파이프라인으로 이관되지 않아 엔진 JSON 과 IR JSON 이 별개로 공존한다 (§8adapter-miricanvas 가 IR 을 거치지 않는 예외인 것과 같은 이야기다). 엔진 쪽 구조 해부는 sheetJson 해부도.

§3

노드 10종

node 값이 종류 판별자다 — 이 값이 어느 키 표를 읽을지 정한다. 컨테이너는 layout 하나뿐이고, diagram 만 예외적으로 자식을 함께 나른다.

node무엇고유 키
layout 유일한 컨테이너. 표는 layout:"grid" + semantic.tag:"table" 에 자식이 행 우선 셀(th/td)인 것 layout(stack 세로·row 가로·grid·overlay 절대) · gap · padding · align(교차축) · justify(주축) · grid · children
text 순서 있는 런의 묶음. 런마다 스타일이 다를 수 있고 링크도 런이 갖는다. 평범한 한 줄은 런 하나 runs[{text, style?, href?}] · textStyle(블록 기본값 — 런이 덮어쓴다)
image 이미 그려진 그림을 그대로 그린다 src(포맷 중립 URL) · resource · fit · crop · alt
vector 문서가 칠하는 윤곽. 자산의 자기 색이 아니라 style.fills 로 칠한다 — 이미지와 갈라 둔 이유가 이것이다 path(SVG d) · svg(인라인 마크업) · src셋 중 하나
line 두 점이 정체다. 커넥터는 끝을 다른 노드에 매단다 — 가리키는 node 가 없으면 적합성 위반 start·end · thickness(0 이면 위반) · cap · dash · startMarker·endMarker · startAttachment·endAttachment{node, anchor}
media 재생은 상태다. embed 는 외부 플레이어라 poster/muted 가 의미 없다 kind(video·audio·embed) · src · fit · poster · autoplay·loop·muted
barcode 무엇을 인코딩하는가 symbology(7종) · value(비면 위반) · logo{kind, ref}
chart 종류 + 데이터. data다 — 첫 행이 시리즈 이름, 첫 열이 카테고리 이름 chartType(15종) · data(비면 위반) · palette · styleRef · scale{min?,max?} · recipe
diagram 컨테이너이면서 저작 소스를 함께 나른다. diagramType 은 열거하지 않는 라벨 — 어휘가 서버 것이라(예: hub_and_spoke) 아는 쪽만 이해하고 나머지는 children 을 그린다 diagramType(비면 위반) · source(실무에서는 마크다운) · children(렌더된 진짜 노드) · recipe
instance components[] 의 정의를 가리킨다 def(없는 id 면 위반) · overrides(대상 노드 id → 덮어쓸 값)

렌더 방법은 왜 IR 에 없나

차트의 렌더 트리나 프리셋 키를 IR 이 들고 있지 않은 이유는, 그것이 “차트가 무엇인가” 가 아니라 “한 제품이 차트를 어떻게 그리는가” 이기 때문이다. 담아야 할 것만 recipe{schema?, data}해석하지 않고 불투명하게 나른다. diagram.recipe · adjust.extra 도 같은 대우다.

리소스 키는 어디 사나 — 두 자리다

문서 안의 resource{provider, key, env?, type?}(생산자 자산 id)와, 문서 밖 키 저장소(src→키, 환경별 사이드카). 앞의 것이 왕복을 닫고, 사이드카는 아직 문서에 없는 신규 업로드 키를 주입한다. 소비 우선순위는 주입 키 → 문서의 resource(발급자·환경 일치) → 주소에서 추출 → 없음(거부).

키가 src대체하지는 않는다 — 키는 다른 포맷이 해석하지 못하고 발급 환경 밖에서 조회되지 않으며(프로덕션 키는 staging 에서 안 나온다) 키→URL 복원 규칙도 없다. 그래서 둘 다 싣는다. 붙는 자리는 다섯 — image · vector · media · mask · 이미지 칠 fills[].

§4

모든 노드가 갖는 공통 키

종류와 무관하게 붙는 자리다. 코드에서는 SERIALIZED_KEYS.common() 이 정본이다.

SERIALIZED_KEYS.common() = id name node sizing position cell mask clip adjust transform style semantic
값 · 단위조건
sizing크기 의도 — 값이 아니라 규칙이다{w, h} 각각 {kind:"hug"}(내용만큼) · {kind:"fill"}(부모를 채운다) · {kind:"fixed", px}필수
position부모 좌상단 기준 절대 위치{x, y} pxoverlay 자식만. 다른 곳에 있으면 위반이고, overlay 자식에 없어도 위반
cell격자 칸 배정. 0-기반{row, column, rowSpan?, colSpan?}grid 자식만. 없으면 문서 순서로 채운다
transform회전·반전. 자신과 자손에 함께 걸리고 곱해진다{rotation? 도·시계방향, flipH?, flipV?}
style칠·테두리·곡률·그림자·불투명도fills[](아래에서 위로 쌓는다) · stroke{color,width} · radius · shadow[] · opacity
semanticHTML 의미 태그. Figma 에는 없지만 어느 방향으로도 보존한다{tag, role?} — 태그 29종
mask실루엣. 이 모양으로 노드의 칠과 자식을 오려낸다{src}
clip렌더 결과에서 보이는 사각형{left, top, width, height} — 자기 박스의 0–1 정규image.crop 과 다르다 ↓
adjust색·블러 조정. 종류와 무관하게 아무 노드에나 붙는다brightness·contrast·saturation·temperature·tint -1…1 · blur px · sharpen·vignette 0…1 · overlay{color, strength} · extra
crop — 원본 선택
원본의 어느 부분을 쓸지
원본 전체를 0–1 로 본 좌표. angle 은 크롭 사각형의 회전
clip — 결과 가리기
결과에서 보이는 사각형
자기 박스 기준 0–1. 한 노드가 둘 다 가질 수 있다

adjust 값은 배수가 아니라 슬라이더 양이다

“슬라이더를 얼마나 올렸나” 를 -1…1 로 적는다. 실제 곡선은 렌더러마다 다르고, 왕복하는 것은 이 양이다. blur 만 물리량(가우시안 시그마)이라 px 이고, sharpen 은 블러의 반대 연산이지 음수 블러가 아니다.

§5

단위 규약 — 가장 자주 틀리는 자리

엔진(미리캔버스) 쪽 단위는 IR 단위가 아니다. 환산은 어댑터의 일이다. 그대로 넘기면 조용히 틀린 값이 된다.

IR 단위흔한 오해 · 엔진 쪽
letterSpacingpx미리캔버스는 폰트 크기 대비 % — 그대로 넘기면 24px 글자에서 정답의 24%
lineHeightpx엔진 lineSpacing비율(1.4 = 140%)
style.radiuspx엔진 cornerRadius0–1 비율 (r = ratio × min(w,h)/2)
adjust.* (blur 제외)-1…1엔진 displayFilter 는 -100…100
adjust.blurpx (가우시안 시그마)엔진은 노드 크기에 상대적
clip · crop0–1 정규px 로 넣으면 전부 오른쪽 아래로 잘린다
transform.rotation · gradient angle도, 시계방향
opacity · stops[].at0–1엔진 shadow.alpha 는 0–100
fontSize · gap · padding · position · sizepx

TextStyle 의 나머지 — fontFamily · fontWeight(100–900) · color(#hex) · align(left·center·right·justify). 런은 {text, style?, href?} 이고 href 가 있으면 그 런이 링크다.

§6

값 어휘 — 프로필이 허용하는 것

@warp/profile 이 좁힌 목록이다. 여기 없는 값은 적합성 검사에서 걸린다.

레이아웃 · 크기 · 정렬
stack · row · grid · overlay
hug · fill · fixed
align: start · center · end · stretch
justify: start · center · end · between · around · evenly
차트 종류 15
bar · column · line · area · pie · donut · scatter · radar · area-line · combo · gauge-circle · gauge-semicircle · gauge-arc · gauge-bar · gauge-column
bar 는 가로, column 은 세로
바코드 규격 7
qr · ean13 · ean8 · code128 · code39 · upc-a · itf
시맨틱 태그 29
section · article · nav · header · footer · main · aside
h1–h6 · p · span · button · a · ul · ol · li · div · img
table · thead · tbody · tfoot · tr · th · td
매체 · 이미지 맞춤
video · audio · embed
cover · contain · fill · none
cap: butt · round · square
marker: none · arrow · chevron · circle · square · diamond
anchor 9점: center · left·right·top·bottom · top-left·top-right·bottom-left·bottom-right
/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i
#rgb · #rrggbb · #rrggbbaa
출처는 @warp/profilePROFILEprofile.ts 안의 CHART_TYPES/SYMBOLOGIES/MEDIA_KINDS. Fill 은 다섯 갈래 — solid · linear-gradient · radial-gradient · conic-gradient · image.
§7

같은 노드, 세 표기

XML 에는 타입도 배열도 없다. <x>1</x> 이 숫자 1 인지 문자열 "1" 인지 알 수 없으므로 추측하는 표기는 왕복하지 못한다. 그래서 두 판 모두 타입을 어딘가에 선언한다 — v1 은 요소의 t 속성에, v2 는 값의 모양에. 키의 뜻은 판과 무관하다. 읽기는 루트의 codec 으로 자동 판별하므로 두 판이 섞여 있어도 된다.

v1 (codec="1")v2 (codec="2" — 쓰기 기본)
스칼라자기 요소 + t속성, 타입은 렉시컬 규칙
구조 객체요소요소 (단축되는 것은 속성 한 개)
실측 크기1,919줄 · 54KB · 속성 1,020개(t 1,013)524줄 · 21KB · 속성 690개(t 114)
JSON — 키 순서는 정규 직렬화가 사전순으로 정렬한 결과다 (diff 를 위한 것, 의미 없음)
{ "id": "p1-body", "node": "text",
  "runs": [ { "text": "런 단위 스타일과 " },
            { "href": "https://example.com/ir?a=1&b=2", "style": { "color": "#2c6bed" }, "text": "링크" } ],
  "semantic": { "tag": "p" },
  "sizing": { "h": { "kind": "hug" }, "w": { "kind": "fill" } },
  "textStyle": { "align": "justify", "color": "#444444", "fontSize": 16, "lineHeight": 26 } }
XML v2 — 스칼라가 속성으로 오르고, sizing 이 한 줄로 접힌다. 키·값이 CSS 어휘로 바뀐다
<i id="p1-body" node="text">
  <runs t="a">
    <i text="런 단위 스타일과 "/>
    <i href="https://example.com/ir?a=1&amp;b=2" text="링크"><style color="#2c6bed"/></i>
  </runs>
  <semantic tag="p"/>
  <sizing height="fit-content" width="stretch"/>
  <textStyle align="justify" color="#444444" font-size="16px" line-height="26px"/>
</i>
XML v1 — 모든 값이 자기 요소를 갖는다. 속성은 codec·t·key·key64 넷뿐
<i>
  <id t="s">p1-body</id>
  <node t="s">text</node>
  <runs t="a">
    <i><text t="s">런 단위 스타일과 </text></i>
    <i>
      <href t="s">https://example.com/ir?a=1&amp;b=2</href>
      <style><color t="s">#2c6bed</color></style>
      <text t="s">링크</text>
    </i>
  </runs>
  <sizing><h><kind t="s">hug</kind></h><w><kind t="s">fill</kind></w></sizing>
</i>

v2 를 읽는 규칙은 여섯 줄뿐

모양읽는 값
"…" 로 시작문자열 (JSON 인용 해제)text="&quot;8&quot;""8"
null / true / falsenull / booleanvisible="true"
숫자 리터럴numberopacity="0.5"
숫자 + pxnumber (단위를 벗긴다)gap="8px"8
[ … ]스칼라 배열palette="[#deff58 #343434]"
그 밖문자열 (값 어휘를 되돌린다)display="flex-column""stack"

그래서 그 규칙으로 읽힐 문자열만 인용한다"8"·"true"·"null"·"10px"·"[a b]"·"". 속성에 담을 수 없는 값(제어문자가 든 문자열·배열)은 v1 의 t 형태로 떨어진다 — v2 는 v1 을 대체하는 표기가 아니라 그 위에 빠른 길을 얹은 것이다. 타입 태그 t 는 두 판 공통: a 배열 · s 문자열 · s64 base64 · n 숫자 · b 불린 · z 널 · l 스칼라 배열(v2 전용).

없는 것도 설계다

이름공간 없음(접두사가 붙으면 “요소 이름 = 필드 이름” 이 깨진다) · XSD/DTD 없음(검증 책임은 @warp/profile 이 갖는다) · 정책·옵션 흔적 없음(같은 문서를 다른 옵션으로 변환해도 문서 값은 같다) · 리소스 키 없음(문서에는 src 주소만, 키는 사이드카에).

§8

Warp 생태계 — 패키지와 어댑터

등록된 정식 IR(layout-intent)이 있고 어댑터들이 그 IR 을 대상으로 한다. 그런데 adapter-miricanvas 만 그 바깥에 있다.

adapter-figma
Adapter(FigmaFile, DesignDoc)
adapter-html
Adapter(string, DesignDoc)
adapter-miricanvas
Adapter 미구현 · ir 없음
자체 모델 McNode 직접 생성
layout-intent IR
@warp/core — DesignDoc 구조·직렬화
@warp/profile — 허용 어휘·적합성 검사
@warp/registry
IR·adapter 등록 · IR↔IR bridge 경로
adapter-xml
JSON ↔ XML(v1/v2) 상호 변환 · 왕복성 검증
figmahtml같은 IR 을 대상으로 같은 문서 타입(DesignDoc)을 만든다. adapter-miricanvascore·profile·ir-layout-intent 참조가 0건이고, Warp 저장소 어디에도 이 어댑터를 registry 에 등록하는 코드가 없다.
패키지역할메모
@warp/coreIR 구조·직렬화의 source of truthtypes.ts(구조) · serialize.ts(직렬화 키 집합 SERIALIZED_KEYS)
@warp/profile허용 어휘 · 적합성 검사profile.ts(어휘) · check.ts·violations.ts(무엇이 위반인가)
@warp/registry어댑터·IR 등록, IR↔IR bridge 경로 관리축이 둘 — format(어댑터) · bridge(IR 버전)
adapter-figmaFigma → IRRegistry 등록됨 · ir: layout-intent
adapter-htmlHTML → IR (정적 파싱, 측정 없음)Registry 등록됨 · ir: layout-intent
adapter-xmlJSON ↔ XML(v1/v2) 상호 변환 · 왕복성 검증xml-compact.ts 가 v2 어휘표의 출처
adapter-miricanvasIR 을 거치지 않고 McNode 를 직접 만든다Registry 미등록 · 예외 상태. Adapter 계약이 요구하는 serialize(엔진 노드 → HTML) 방향이 없어 지금은 등록이 성립하지 않는다

web-2 안의 Warp — vendor 가 두 자리다

mainhtml-import (PoC 브랜치)
위치packages/engine/vendor/warp/packages/engine/editor/src/html-import/vendor/warp-mc/
가져온 패키지packages/registry (원본 4파일)packages/adapter-miricanvas (원본 25파일)
쓰는 곳framework — 노드 props 마이그레이션editor — HTML 가져오기 (DEV 전용)
registry 사용bridge 축만. format 축은 항등 어댑터로 의도적으로 막아 뒀다0건 — 직접 함수 파이프라인

같은 Warp 저장소에서 왔지만 가져온 부분이 겹치지 않는다 — 파일 내용 SHA-256 교차 0개, 파일명 교집합 0개. HTML 가져오기가 지금은 adapter-html(정적 파싱)이 아니라 브라우저 실측 → adapter-miricanvas 경로를 쓰는 것도 이 때문이다. 기준: main 병합 커밋 6ae349c9765 · Warp cf87a75(mirror) / 3964a2c(main vendor) · 2026-08-12 확인.

§9

출처

정본은 타입이다

이 페이지는 Confluence 원문을 옮긴 것이고, 그 원문도 코드를 옮긴 것이다. 어긋나면 타입이 이긴다packages/core/src/types.ts(구조) · serialize.ts(직렬화 키 집합) · packages/profile/src/profile.ts(허용 어휘). 키 집합은 SERIALIZED_KEYS.common()/.kinds()/.extra(k) 로 직접 뽑아 대조할 수 있다.

무엇어디
IR 스키마 참조: JSON 키와 XML 표기 설명 — 이 페이지 §1–§7 의 원문Confluence 2814312600
main 브랜치의 vendor 병합 — §8 의 원문 (Warp 저장소 구조 · registry 축 · vendor 두 자리)Confluence 2777154274
figma-import: JSON → 엔진 요소 변환 기준adapter-figma 실질 설계문서Confluence 2703820539
AIP HTML importadapter-html 실질 설계문서Confluence 2772238832
디자인 2.0 JSON 구조 — §2 에서 갈라 둔 엔진 쪽 포맷Confluence 1066862669

Warp 저장소 안의 근거 문서 — 왜 XML 이 이렇게 생겼나: records/decisions/DR-041-ir-xml-form.md(v1) · DR-059-xml-ir-v2-compact.md(v2 축약) · DR-042(URL 대체 정책) · DR-043·DR-045(리소스 키) · 엔진 단위·한계는 docs/wiki/pages/concept-miricanvas-engine-contracts.md. 모든 종류를 담은 실물 표본은 examples/artifacts/all-node-kinds.ir.{json,xml}.