디자인 토큰 & Variables 운영 전략 — Figma부터 코드 매핑까지 완전 가이드
디자이너와 개발자 사이에 이런 대화, 다들 한 번쯤 겪어보셨을 거예요. "Primary 색상 조금 바꿨어요~" 한 마디에 개발자가 파일 열다섯 개를 열어서 색 코드를 하나씩 찾아 바꾸는 상황. 혹은 반대로, 개발자가 이미 코드에서 색을 바꿨는데 Figma 파일은 아직 옛날 색인 상황. 😅 이게 한두 번이면 모르겠는데, 프로젝트가 커질수록 이 문제는 기하급수적으로 커져요.
디자인 토큰은 바로 이 문제를 해결하기 위해 나온 개념이에요. "색, 간격, 폰트 크기 같은 디자인 결정 사항을 하나의 이름 붙인 변수로 관리하자"는 아이디어인데요, 말은 쉽지만 실제로 Figma Variables와 코드를 동기화해서 운영하는 건 생각보다 손이 많이 가요. 오늘은 그 전체 흐름을 처음부터 끝까지 정리해 드릴게요!
디자인 토큰이란 무엇인가요? 🔤
디자인 토큰(Design Token)은 UI를 구성하는 시각적 속성값을 이름 붙인 변수로 추상화한 것이에요. 색상, 타이포그래피, 간격, 그림자, 모션 등 디자인 시스템에서 반복적으로 사용되는 값들이 모두 토큰의 대상이 될 수 있어요.
예를 들어 버튼의 배경색을 #6a1b9a라는 HEX 값으로 직접 쓰는 대신, color.primary.500이라는 토큰 이름으로 참조하는 거예요. 나중에 브랜드 색상이 바뀌어도 토큰 값 하나만 수정하면 그 토큰을 참조하는 모든 컴포넌트가 한꺼번에 업데이트되죠.
토큰의 3단계 계층 구조
실무에서 많이 쓰는 3-tier 구조를 알아두면 토큰 설계가 훨씬 명확해져요.
| 계층 | 이름 | 역할 | 예시 |
|---|---|---|---|
| 1계층 | Primitive (원시 토큰) | 브랜드 팔레트 전체 정의 | purple.500 = #6a1b9a |
| 2계층 | Semantic (의미 토큰) | 용도별 역할 부여 | color.action.primary → purple.500 |
| 3계층 | Component (컴포넌트 토큰) | 특정 컴포넌트에 매핑 | button.bg.default → color.action.primary |
컴포넌트가 Primitive 토큰을 직접 참조하면 안 돼요. 반드시 Semantic 토큰을 거쳐야 해요. 그래야 다크모드 전환이나 테마 교체 시 Semantic 토큰 값만 바꿔도 모든 컴포넌트가 일관되게 업데이트돼요.
Figma Variables로 세팅하는 법 🎨
Figma는 2023년 Variables 기능을 정식 출시하면서 디자인 토큰을 Figma 안에서 직접 관리할 수 있게 됐어요. Color, Number, String, Boolean 네 가지 타입을 지원하고, 컬렉션(Collection)과 모드(Mode)로 라이트/다크, 브랜드 테마 전환을 한 파일 안에서 처리할 수 있어요.
📋 Figma Variables 세팅 단계
- 컬렉션 생성: 우측 패널 Variables → "+" 클릭 → 컬렉션 이름 지정. Primitives, Semantics, Components 세 컬렉션을 각각 만드는 걸 추천해요.
- Primitives 먼저 채우기: 브랜드 팔레트 전체를
purple/100 ~ purple/900식으로 입력해요. 이 단계에선 실제 HEX 값이 들어가요. - Semantics에서 Primitives 참조: Semantic 토큰 값을 입력할 때 HEX 직접 입력 대신 Variable 아이콘을 클릭해 Primitives 토큰을 연결해요.
- Mode 추가 (라이트/다크): Semantics 컬렉션 우측 상단 "Edit Modes"에서 Dark 모드를 추가하고, 같은 토큰 이름에 다른 값(예: 다크모드용 Primitive)을 매핑해요.
- 컴포넌트에 적용: 프레임·텍스트·아이콘의 Fill/Stroke/Gap 등을 Color Picker 대신 Variable로 연결하면 모드 전환 시 자동으로 값이 바뀌어요.
Figma Variables의 Number 타입은 spacing, border-radius, font-size에 사용할 수 있어요. 단, 현재(2025년 기준) Figma Variables는 그림자(Box Shadow) 전체를 하나의 변수로 관리하는 건 지원하지 않아요. 그림자 토큰은 별도 Styles로 관리하거나 플러그인을 활용하는 편이에요.
코드로 매핑하기 — CSS Variables & Theme 객체 🖥️
Figma에서 잘 정리한 토큰을 코드로 옮길 때는 두 가지 방식이 주로 쓰여요. CSS Custom Properties(CSS 변수)와 JS/TS Theme 객체예요. 프로젝트 스택에 따라 선택하거나 둘 다 함께 쓰기도 해요.
① CSS Custom Properties
가장 범용적인 방식이에요. 프레임워크에 종속되지 않고, 런타임에서도 값을 바꿀 수 있어서 다크모드 구현에 특히 유리해요.
📝 Primitive → Semantic → Component 레이어 예시
:root {
--purple-500: #6a1b9a;
--purple-200: #ce93d8;
--gray-100: #f5f5f5;
--gray-900: #212121;
}
/* 2. Semantics — 역할 부여 (라이트 기본) */
:root {
--color-action-primary: var(--purple-500);
--color-bg-surface: var(--gray-100);
--color-text-default: var(--gray-900);
}
/* 다크모드 — Semantic 값만 교체 */
[data-theme="dark"] {
--color-action-primary: var(--purple-200);
--color-bg-surface: var(--gray-900);
--color-text-default: var(--gray-100);
}
/* 3. Component — Semantic 참조 */
.btn-primary {
background-color: var(--color-action-primary);
color: var(--color-bg-surface);
}
② JS/TS Theme 객체 (React 예시)
Styled-components, Emotion, MUI 같은 CSS-in-JS 환경이나 Tailwind의 theme extend를 쓸 때는 JS 객체로 토큰을 관리해요. 타입 안전성이 생기고, 자동완성이 돼서 개발 경험(DX)이 좋아요.
export const primitives = {
purple: { 200: '#ce93d8', 500: '#6a1b9a' },
gray: { 100: '#f5f5f5', 900: '#212121' },
};
// tokens/themes.ts
import { primitives as p } from './primitives';
export const lightTheme = {
color: {
action: { primary: p.purple[500] },
bg: { surface: p.gray[100] },
text: { default: p.gray[900] },
},
};
export const darkTheme = {
...lightTheme,
color: {
action: { primary: p.purple[200] },
bg: { surface: p.gray[900] },
text: { default: p.gray[100] },
},
};
CSS Variables는 런타임 교체가 쉽고 프레임워크 무관. 다크모드를
data-theme 속성 하나로 처리하고 싶을 때 최적이에요. Theme 객체는 TypeScript 타입 안전성·자동완성이 필요하고, CSS-in-JS 환경에서 컴포넌트 단위로 토큰을 소비할 때 유리해요. 둘을 함께 쓰는 것도 가능해요 — Theme 객체를 CSS Variables로 변환해주는 유틸 함수를 만들면 돼요.
실무에서 동기화가 깨지는 순간들 — 그리고 해결법 ⚠️
이론은 깔끔한데 실무에서는 Figma와 코드 사이 동기화가 생각보다 자주 어긋나요. 제가 직접 겪었거나 팀에서 자주 보던 패턴 네 가지와 각각의 해결법을 솔직하게 정리해 드릴게요.
💥 시나리오 1 — "Figma에서 토큰 이름 바꿨는데 코드는 그대로"
원인: 디자이너가 Figma Variables 이름을 변경했지만 코드의 CSS 변수명은 수동으로 업데이트하지 않은 경우.
해결: 토큰 이름 변경은 반드시 코드 리뷰 없이 단독으로 하지 않는다는 팀 규칙을 만들고, Style Dictionary나 Token Studio 같은 도구로 Figma → 코드 변환을 자동화해요. 이름 변경 = PR 필수.
💥 시나리오 2 — "개발자가 하드코딩으로 급하게 패치"
원인: 긴급 수정 상황에서 토큰 참조 없이 HEX 값을 직접 입력. Figma는 여전히 토큰 참조 상태지만 코드만 다른 값.
해결: ESLint custom rule이나 Stylelint 규칙으로 하드코딩 색상값 사용 시 경고를 띄우도록 설정해요. no-hardcoded-colors 같은 커스텀 룰이 효과적이에요.
💥 시나리오 3 — "Figma Variables 업데이트가 코드에 자동 반영 안 됨"
원인: 토큰을 수동으로 양쪽에서 관리하다 보니 어느 순간부터 파일 두 벌이 독립적으로 진화하는 현상.
해결: 단일 진실의 원천(Single Source of Truth)을 정해요. 코드의 토큰 JSON 파일을 원본으로 두고, Figma에 역으로 임포트하는 방식(Tokens Studio 플러그인의 GitHub 동기화 기능)이 실무에서 가장 안정적이에요.
💥 시나리오 4 — "새 디자이너가 Primitive 토큰을 컴포넌트에 직접 연결"
원인: 계층 구조 원칙을 모르는 팀원이 편의상 purple.500을 버튼에 직접 연결. 다크모드 추가 시 해당 버튼만 테마 전환이 안 됨.
해결: 토큰 사용 가이드를 Figma 내 커버 페이지나 Notion 위키에 명문화하고, Primitive 컬렉션의 Publish 설정에서 외부 공개를 제한해 Semantic을 통해서만 쓸 수 있도록 강제해요.
핵심 내용 정리 📝
오늘 다룬 내용을 한 번 더 정리해 드릴게요!
- 디자인 토큰: 색상·간격·폰트 등 디자인 결정값을 이름 붙인 변수로 추상화. 변경 비용을 극적으로 낮춰줘요.
- 3계층 구조: Primitive → Semantic → Component 순으로, 컴포넌트는 반드시 Semantic을 통해 값을 참조해야 해요.
- Figma Variables: 컬렉션과 Mode로 라이트/다크 테마를 한 파일에서 관리. Semantic이 Primitive를 참조하도록 연결하는 게 핵심.
- 코드 매핑: CSS Custom Properties는 런타임 테마 전환에 유리, Theme 객체는 타입 안전성과 DX에 유리. 프로젝트 스택에 맞게 선택하거나 함께 써요.
- 동기화 전략: 단일 진실의 원천 확보(코드 토큰 JSON → Figma 임포트), 하드코딩 린트 룰, 이름 변경 PR 필수화 — 이 세 가지가 동기화 붕괴를 막는 핵심이에요.
디자인 토큰 운영 핵심 요약 카드
자주 묻는 질문 ❓
data-theme="brand-a" 속성만 바꿔도 전체 브랜드가 전환되는 구조를 만들 수 있어요.[카테고리].[역할].[상태].[스케일] 패턴을 많이 써요. 예: color.feedback.error.default. 중요한 건 팀 안에서 규칙이 일관되는 거예요. W3C의 Design Tokens Community Group(DTCG)에서 발표한 토큰 표준 스펙을 참고하면 업계 표준에 가까운 네이밍을 잡을 수 있어요.처음 디자인 토큰 개념을 접하면 "이렇게까지 해야 해?" 싶을 수 있어요. 근데 프로젝트가 커지고 팀원이 늘어나면, 토큰 없이 작업했을 때의 그 끔찍한 파편화를 경험하고 나서야 "왜 진작에 안 했지?" 하게 돼요. 😄 처음엔 Semantic 토큰 몇 개부터 시작해보는 것도 충분해요. 작은 것부터 시작해서 점진적으로 3계층을 완성해 나가는 게 현실적인 방법이에요. 궁금한 점이나 팀에서 겪은 동기화 고민이 있으면 댓글로 편하게 공유해 주세요! 🙌