-
디자인 토큰이 드디어 표준이 됐다 — W3C DTCG 2025.10 안정판이 정한 세 가지IT 2026. 8. 10. 23:00
브랜드 대표색 하나를 바꾼다고 가정하자. 파랑을 살짝 다른 파랑으로 바꾸는, 말로는 5분짜리 일이다. 그런데 실제로는 디자인 도구의 스타일 정의, 웹의 CSS 변수, Android 테마 상수, iOS 색상 정의를 각각 따로 고쳐야 한다. 같은 "브랜드 파랑"이라는 결정 하나가 네 곳에 네 가지 표기법으로 흩어져 있었기 때문이다. 한 군데라도 빠뜨리면 플랫폼마다 미묘하게 다른 파랑이 몇 달씩 방치된다.
이 문제를 푸는 개념이 디자인 토큰(design token)이다 — 색·간격·글꼴 크기 같은 디자인 결정에 이름을 붙여, 값이 아니라 이름을 참조하게 만드는 방식이다. 개념 자체는 10년 가까이 쓰였지만 문제가 하나 있었다. 토큰을 적어두는 파일 포맷이 도구마다 달랐다. 그러다 2025년 10월 28일, W3C 커뮤니티 그룹(정식 표준화 전 단계에서 업계 합의를 만드는 W3C 산하 개방형 그룹)인 Design Tokens Community Group(DTCG)이 디자인 토큰 명세의 첫 안정 버전(2025.10)을 발표했다. 수년간의 초안 상태가 끝났고, Adobe·Google·Microsoft·Meta·Figma·Salesforce 등에서 모인 20여 명의 편집자가 합의한 벤더 중립 포맷이 확정됐다. 이 글은 그 표준이 정확히 무엇을 정했는지를 하나의 예시로 풀어본다.
디자인 토큰 — 값이 아니라 결정에 이름을 붙인다
디자인 토큰은 프로그래밍의 상수 선언과 비슷한 발상이다. 코드에서
3.14159를 여기저기 직접 적지 않고PI라는 이름을 선언해 쓰듯, 화면 곳곳에#2563EB를 직접 적지 않고color.brand.primary라는 이름을 선언해 쓴다. 값이 바뀌면 선언 한 곳만 고치면 된다.여기까지는 어느 팀이나 이미 하고 있던 일이다. 문제는 그 선언을 어디에, 어떤 형식으로 적느냐였다. 디자인 도구는 자기 파일 안에, 웹 팀은 CSS 변수로, 모바일 팀은 각자의 테마 파일로 적었다. 선언이 네 벌이면 그건 사실상 상수가 아니라 복사본이다. 2025.10 표준이 정한 것이 바로 이 지점이다 — 모든 도구와 플랫폼이 함께 읽고 쓰는 단일한 토큰 파일 형식을 확정했다.
무엇이 표준이 됐나 — 세 개의 모듈
2025.10 안정판은 세 개의 모듈로 구성된다. 각 모듈이 맡은 질문이 다르다.
모듈 답하는 질문 핵심 내용 Format 토큰을 어떻게 적는가 JSON 기반 파일 구조, $type·$value속성, 토큰 간 참조(alias)Color 색을 어떻게 표현하는가 hex 문자열이 아닌 구조화된 객체 — OKLCH·Display P3 등 최신 색 공간 지원 Resolver 테마·모드를 어떻게 전환하는가 라이트/다크, 브랜드별 변형을 파일 복제 없이 조합하는 규칙 이 세 모듈이 함께 만드는 그림은 이렇다. 토큰 파일 하나가 유일한 원본(single source of truth)이 되고, 변환 도구가 그 파일에서 각 플랫폼의 코드를 뽑아낸다.
다이어그램 설명. "토큰 JSON 파일 하나"에서 출발한 정의가 "변환 도구"를 거쳐 웹·Android·iOS 세 갈래의 플랫폼별 코드로 갈라지는 구조다. 핵심은 갈라지는 지점이 사람 손이 아니라 도구라는 점이다 — 사람은 원본 파일 한 곳만 고치고, 네 벌의 복사본을 동기화하는 일은 변환 도구가 맡는다. 이 구조 자체는 Style Dictionary(하나의 토큰 정의를 여러 플랫폼 코드로 변환해 주는 오픈소스 빌드 도구) 같은 도구가 표준 이전부터 해 오던 일이지만, 입력 파일 포맷이 표준이 아니어서 도구를 바꾸면 토큰 파일도 다시 써야 했다. 표준 확정으로 이 입력 파일이 이식 가능해졌고, 실제로 Style Dictionary·Tokens Studio·Terrazzo가 참조 구현을 제공하며 Figma·Sketch·Penpot·Framer 등 10개 이상의 도구가 지원을 시작했다.
대표 예시 — 버튼 배경색 하나로 표준을 관통해 보기
추상 설명보다 파일 하나를 직접 보는 편이 빠르다. "버튼의 배경은 브랜드 대표색이다"라는 결정을 표준 포맷으로 적으면 이렇게 된다.
{ "color": { "brand": { "primary": { "$type": "color", "$description": "브랜드 대표색. 모든 플랫폼이 이 값 하나를 참조한다.", "$value": { "colorSpace": "oklch", "components": [0.55, 0.15, 250], "hex": "#2563EB" } } } }, "button": { "background": { "$type": "color", "$value": "{color.brand.primary}" } } }코드 설명. 평범한 JSON(중괄호·따옴표로 데이터를 적는 표준 텍스트 형식)이고, 표준이 정한 특별한 부분은
$로 시작하는 속성들이다.$type은 이 토큰이 무엇인지(색·치수·글꼴 등)를,$value는 실제 값을 담는다. 눈여겨볼 곳은 두 군데다. 첫째,color.brand.primary의 값이 hex 문자열이 아니라 색 공간과 성분을 명시한 객체다 — 이유는 다음 섹션에서 풀겠다. 둘째,button.background의 값은 색이 아니라{color.brand.primary}라는 참조(alias)다. 중괄호 경로 표기로 다른 토큰을 가리키면, 읽는 도구가 참조를 따라가 실제 값을 해석해야 한다고 표준이 못박고 있다(순환 참조는 금지된다). 이 참조 덕분에 "버튼은 브랜드색을 따른다"는 관계 자체가 파일에 남는다 — 나중에 브랜드색을 바꾸면 버튼은 자동으로 따라온다.왜 이게 중요한가? 참조가 표준의 일부가 되면서, 디자인 시스템의 계층 구조(원시 색상 → 의미 색상 → 컴포넌트 색상)를 어느 도구에서 열어도 같은 의미로 읽을 수 있게 됐다. 이전에는 이 관계가 각 도구의 독점 기능이었고, 도구를 옮기면 관계가 값으로 구워져(flatten) 사라지곤 했다.
색을 hex 문자열이 아니라 객체로 적는 이유
위 예시에서 색을
"#2563EB"한 줄로 적지 않고colorSpace·components·hex로 풀어 적은 것이 낯설 수 있다. 이는 hex 표기의 한계 때문이다. hex는 sRGB(1996년에 정의된 웹의 기본 색 공간)의 8비트 표현에 묶여 있다. 그런데 요즘 디스플레이는 sRGB보다 넓은 색을 표현한다 — 최신 스마트폰과 노트북 화면이 지원하는 Display P3는 sRGB보다 약 25% 넓은 색 영역을 갖는다. hex로는 이 영역의 색을 아예 적을 수가 없다.그래서 표준은 색을 "어떤 색 공간의, 어떤 성분값"이라는 구조화된 객체로 적게 했다. 예시의
oklch는 CSS Color Module 4(CSS의 최신 색 표준)에 포함된 색 공간으로, 밝기(L)·채도(C)·색상(H) 세 성분이 사람의 지각과 비례하도록 설계돼 있다 — 밝기 성분만 조절해 hover 색을 만들어도 색조가 틀어지지 않는 실용적 장점이 있다.hex필드는 구형 환경을 위한 대체값(fallback)으로 함께 적을 수 있다. 디자인 도구들이 이미 P3·OKLCH로 작업하는 시대에, 토큰 포맷이 sRGB에 갇혀 있으면 도구에서 고른 색과 파일에 적힌 색이 달라진다 — 표준이 최신 색 공간을 1급으로 지원하는 이유다.테마 전환 — 파일을 복제하지 않고 조합한다
남은 질문은 다크 모드다. 라이트/다크 두 벌의 색이 필요하면 토큰 파일을 통째로 두 벌 만들어야 할까? 여기에 브랜드가 2개, 접근성 고대비 모드까지 얹으면 2×2×2 = 8벌이 된다. 조합이 하나 늘 때마다 파일 수가 배로 느는 조합 폭발이다. 표준의 세 번째 모듈인 Resolver가 이 문제를 맡는다. 공통 토큰은
sets(기본 토큰 묶음)에 한 번만 두고, 상황에 따라 달라지는 부분만modifiers(조건별 덮어쓰기 묶음)로 분리한다.{ "$schema": "https://www.designtokens.org/schemas/2025.10/resolver.json", "version": "2025.10", "sets": { "foundation": { "sources": [{ "$ref": "base-tokens.json" }] } }, "modifiers": { "theme": { "contexts": { "light": [{ "$ref": "theme/light.json" }], "dark": [{ "$ref": "theme/dark.json" }] }, "default": "light" } }, "resolutionOrder": [ { "$ref": "#/sets/foundation" }, { "$ref": "#/modifiers/theme" } ] }코드 설명.
sets.foundation이 모든 테마가 공유하는 기본 토큰 파일을 가리키고,modifiers.theme이 "light면 이 파일, dark면 저 파일"이라는 조건 분기를 선언한다.resolutionOrder는 병합 순서다 — 배열에서 뒤에 오는 항목이 앞의 값을 덮어쓴다. 도구에{ "theme": "dark" }라는 입력을 주면, 기본 토큰 위에 다크 테마 파일이 덮여 최종 토큰 세트가 확정된다. CSS를 아는 독자라면 익숙한 패턴이다 — 기본 스타일 위에 미디어 쿼리로 일부만 덮어쓰는 cascade와 같은 발상이다.다이어그램 설명. "공통 토큰"과 "테마별 덮어쓰기", 그리고 "theme: dark"라는 입력이 resolver 병합 단계에서 만나 "최종 토큰 세트" 하나로 확정되는 흐름이다. 파일 수를 세어 보면 이 구조의 가치가 드러난다 — 브랜드 2개 × 테마 2개 × 고대비 여부 2가지면 복제 방식은 완성본 8벌을 유지해야 하지만, 조합 방식은 공통 1벌 + 차이만 담은 조각 6개(브랜드 2 + 테마 2 + 고대비 2)로 끝난다. 조합이 늘어도 파일은 곱셈이 아니라 덧셈으로 는다. 놓치기 쉬운 함정도 하나 있다 — 덮어쓰기 순서가 결과를 좌우하므로,
resolutionOrder를 어떤 순서로 두느냐 자체가 팀이 합의해야 할 설계 결정이다.정리 — 표준화된 것은 '표현'이고, 그것으로 충분한 출발이다
2025.10이 정한 것을 한 줄로 줄이면 이렇다. 디자인 결정이 특정 도구의 내부 데이터가 아니라, 모든 도구가 읽고 쓰는 이식 가능한 데이터가 됐다. 색을 어떻게 적을지(Format·Color), 테마를 어떻게 전환할지(Resolver)까지가 표준의 영역이다. 어떤 색이 우리 브랜드에 옳은지, 버튼이 브랜드색을 따라야 하는지 경고색을 따라야 하는지 — 그 판단은 여전히 표준 밖, 각 팀의 몫으로 남는다. 표준은 결정을 대신해 주지 않고, 결정을 한 번만 적어도 되게 해 준다.
개인 프로젝트에도 적용해 볼 만하다. 대시보드든 블로그든 화면이 두 개 이상이라면, 색과 간격을 CSS 곳곳에 직접 적는 대신 표준 포맷의 토큰 파일 하나로 뽑아 두는 것부터가 시작이다 — 다크 모드를 붙이고 싶어지는 날, 파일을 복제하는 대신 덮어쓰기 조각 하나를 추가하면 된다.
참고한 공개 자료:
- Design Tokens specification reaches first stable version (DTCG 공식 발표, 2025-10-28): https://www.w3.org/community/design-tokens/2025/10/28/design-tokens-specification-reaches-first-stable-version/
- Design Tokens Technical Reports 2025.10 (Format·Color·Resolver 모듈): https://www.designtokens.org/tr/2025.10/
- Design Tokens Format Module 2025.10: https://www.designtokens.org/tr/2025.10/format/
- Design Tokens Resolver Module 2025.10: https://www.designtokens.org/tr/2025.10/resolver/
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
디자인 토큰은 색상 상수 모음이 아니다 — 이름을 두 겹으로 두는 이유 (0) 2026.08.10 린트라는 이름은 건조기 필터에서 왔다 — 1978년 C 코드 검사기가 디자인 토큰까지 온 길 (0) 2026.08.10 6.86배 빨라졌다는데 왜 못 믿나 — 성능 최적화 자동화의 best-of-N 함정 (0) 2026.08.09 AI가 시안대로 UI를 짜도, 맞는지 자동으로 검증할 수가 없다 — 개발 자동화 루프의 진짜 병목 (0) 2026.08.09 남의 AI를 내 안에서 실행한다 — Claude Code를 붙이는 subprocess 하나와 두 개의 브리지 (0) 2026.08.08 격리된 내 AI에 Claude Code를 붙이는 네 갈래 — 그리고 'ACP'라는 이름이 둘이었다 (0) 2026.08.08 MCP를 전부 컨테이너에 가뒀다, 딱 하나만 빼고 — 샌드박스 담에 문을 내는 법과 그 대가 (0) 2026.08.07 샌드박스 정책에 반드시 들어가는 세 줄 — 그리고 각 줄이 막는 공격 (0) 2026.08.07 130개의 보안 경고를 가진 에이전트를 집에서 안전하게 돌리는 법 — OpenShell로 케이지에 넣기 (0) 2026.08.07 에이전트를 샌드박싱한다 — 그 한 문장이 숨기는 세 개의 경계 (0) 2026.08.07