Tokens Studio로 멀티브랜드 × 다크모드 디자인 토큰을 하나의 파이프라인에서 자동화하기 — Style Dictionary와 permutateThemes 완전 활용 가이드
전제 조건: Figma에서 Tokens Studio 플러그인을 설치한 경험이 있거나, Style Dictionary의 기본 개념(토큰 JSON → CSS 변환)을 알고 있다면 더 빠르게 따라올 수 있습니다. 예상 읽기 시간은 약 15분입니다.
디자인 시스템을 운영하다 보면 어느 순간 비슷한 고통을 경험하게 됩니다. 라이트 모드와 다크 모드를 각각 유지하면서, 동시에 여러 브랜드의 스타일 변형까지 커버해야 하는 상황입니다. 처음에는 CSS 파일 몇 개로 시작했다가, 어느새 button-light-brandA.css, button-dark-brandB.css 같은 파일들이 폴더를 가득 채우고, 디자인 변경 하나가 수십 곳의 수동 작업으로 번집니다. 이 현상은 "모드"와 "브랜드"라는 두 개의 독립 차원이 서로 맞물리면서, 파일 구조가 두 차원의 곱(N×M)만큼 폭발적으로 증가하기 때문에 발생합니다.
이 글을 다 읽고 나면 브랜드가 추가되거나 다크 모드가 요청되더라도 CSS 파일을 직접 만질 필요가 없어집니다. 핵심은 디자인 토큰을 계층화된 Set으로 설계하고, Themes로 조합을 자동화해 단 하나의 빌드 파이프라인에서 N×M 개의 CSS를 동시에 생성하는 것입니다. Tokens Studio(Figma 플러그인 및 독립 플랫폼)의 Sets·Themes 기능과, @tokens-studio/sd-transforms 패키지의 permutateThemes 함수를 조합하면 이것이 가능합니다. Set 계층 설계부터 $themes.json 구성, Style Dictionary 빌드 스크립트, 런타임 테마 전환, 그리고 CI/CD 연동까지 단계별로 살펴보겠습니다.
핵심 개념
Tokens Studio란?
Tokens Studio는 Figma 플러그인 및 독립 웹 플랫폼(Studio)으로, 디자인 토큰을 JSON 파일로 중앙 관리하고 GitHub 등 저장소에 동기화한 뒤 코드로 내보낼 수 있게 해주는 도구입니다. Style Dictionary는 이 토큰 JSON을 받아 CSS 변수, iOS Swift, Android XML 등 다양한 플랫폼 코드로 변환해주는 오픈소스 빌드 엔진입니다. sd-transforms는 Tokens Studio JSON 형식을 Style Dictionary가 이해할 수 있도록 변환하는 공식 플러그인 패키지입니다.
Token Sets — 토큰의 3계층 구조
Token Set은 토큰들의 논리적 묶음으로, 하나의 JSON 파일에 대응합니다. 다차원 테마 관리를 위해 세 계층으로 나누어 설계하는 것을 권장합니다.
| 계층 | 역할 | 예시 |
|---|---|---|
| Global/Core | 팔레트, 기본 수치 등 원시 값 | color.blue.500 = #3B82F6 |
| Semantic/Alias | 의미 있는 역할 참조 | color.background.primary = {color.blue.500} |
| Component | 컴포넌트 수준 스코프 | button.bg = {color.background.primary} |
토큰 참조(Reference): 중괄호
{}문법으로 다른 토큰을 참조합니다.{color.blue.500}처럼 작성하면 원시 값이 바뀌어도 참조하는 모든 시맨틱 토큰이 자동으로 갱신됩니다.
실제 Set 폴더 구조는 다음과 같이 설계할 수 있습니다.
sets/
├── global.json # 팔레트, 타이포그래피 원시 값 (항상 source)
├── semantic.json # 역할 별칭 토큰 (항상 source)
├── mode/
│ ├── light.json # light 모드 시맨틱 오버라이드
│ └── dark.json # dark 모드 시맨틱 오버라이드
└── brand/
├── brandA.json # BrandA 고유 색상/폰트
└── brandB.json # BrandB 고유 색상/폰트global과 semantic은 항상 참조 값 공급원(source)으로 활성화되고, mode/*와 brand/*는 테마에 따라 선택적으로 활성화(enabled)되어 시맨틱 값을 덮어씁니다.
Themes — 다차원 조합의 자동화
Theme는 "어떤 Set 조합을 함께 적용할 것인가"를 정의한 설정입니다. Theme Group(차원)을 여러 개 만들고, 각 그룹 안에 Theme Option(값)을 넣어 다차원 매트릭스를 구성합니다.
Theme Group: mode → Options: light, dark
Theme Group: brand → Options: brandA, brandB두 그룹이 교차하면 light_brandA, light_brandB, dark_brandA, dark_brandB 네 가지 조합이 자동으로 생성됩니다. permutateThemes 함수는 내부적으로 이 Cartesian product(두 그룹의 모든 경우의 수 교차 조합)를 계산해 반환합니다. 모드 3개 × 브랜드 4개라면 12개 조합이 파이프라인 변경 없이 자동으로 빌드됩니다.
Multi-Dimensional Theming: 독립적인 Theme Group들이 교차 조합되어, 각 차원의 값이 독립적으로 적용되는 방식입니다.
Tokens Studio가 자동 생성하는 $themes.json 파일의 구조는 아래와 같습니다.
[
{
"id": "light", "name": "light", "group": "mode",
"selectedTokenSets": {
"global": "source", // 항상 병합 (참조 값 공급, 출력에 직접 포함 안 됨)
"semantic": "source", // 항상 병합 (참조 값 공급, 출력에 직접 포함 안 됨)
"mode/light": "enabled" // 이 테마에서만 활성화 (실제 출력에 포함)
}
},
{
"id": "dark", "name": "dark", "group": "mode",
"selectedTokenSets": {
"global": "source",
"semantic": "source",
"mode/dark": "enabled"
}
},
{
"id": "brandA", "name": "brandA", "group": "brand",
"selectedTokenSets": { "brand/brandA": "enabled" }
},
{
"id": "brandB", "name": "brandB", "group": "brand",
"selectedTokenSets": { "brand/brandB": "enabled" }
}
]source로 설정된 Set은 참조 값의 공급원이 되지만 출력에 직접 포함되지는 않고, enabled로 설정된 Set은 해당 테마에서 실제 출력 값에 포함되어 오버라이드를 수행합니다. disabled 상태도 존재하며, 이 경우 해당 Set은 참조에도 출력에도 포함되지 않습니다.
실전 적용
빌드 파이프라인 구성 — sd-transforms로 모든 조합 CSS 동시 빌드
@tokens-studio/sd-transforms 패키지의 permutateThemes 함수는 $themes.json을 읽어 가능한 모든 Theme Group 교차 조합을 생성해 줍니다.
Node.js 버전 주의: 아래 코드의
assert { type: 'json' }import assertion은 Node.js 22 이상에서 안정적으로 동작합니다. 그 이하 버전을 사용한다면fs.readFileSync+JSON.parse방식으로 대체하는 것을 권장합니다.
// build-tokens.ts (Node.js 22+)
import StyleDictionary from 'style-dictionary';
import { permutateThemes, registerTransforms } from '@tokens-studio/sd-transforms';
import themes from './$themes.json' assert { type: 'json' };
await registerTransforms(StyleDictionary);
// permutateThemes 반환 형태: { light_brandA: [...sets], light_brandB: [...sets], ... }
// 배열 원소 예시: ['global', 'semantic', 'mode/light', 'brand/brandA']
// tokenSets 배열의 순서가 병합 우선순위를 결정합니다 — 후위 파일이 전위 파일을 덮어씁니다
const permutations = permutateThemes(themes, { separator: '_' });
for (const [themeName, tokenSets] of Object.entries(permutations)) {
const sd = new StyleDictionary({
// tokenSets 배열 순서대로 병합 — 뒤에 오는 파일이 앞 파일의 동일 키를 덮어씁니다
source: tokenSets.map(s => `tokens/${s}.json`),
platforms: {
css: {
// tokens-studio 변환 그룹: Figma의 rgba() → CSS hex, px 단위 처리 등을 자동 변환
transformGroup: 'tokens-studio',
prefix: 'ts',
buildPath: 'dist/',
files: [
{
destination: `${themeName}.css`,
format: 'css/variables',
},
],
},
},
});
await sd.buildAllPlatforms();
}| 코드 포인트 | 역할 |
|---|---|
registerTransforms |
Tokens Studio 전용 변환 규칙을 Style Dictionary에 등록 |
permutateThemes |
Theme Group 간 Cartesian product 조합을 열거해 객체로 반환 |
tokenSets 배열 순서 |
병합 우선순위를 결정 — 뒤에 오는 파일이 앞 파일을 덮어씁니다 |
transformGroup: 'tokens-studio' |
Figma의 rgba() → CSS hex 변환, px 단위 처리 등 Tokens Studio 전용 변환 적용 |
package.json에 다음과 같이 빌드 스크립트를 등록해 두면 편리합니다.
{
"scripts": {
"build:tokens": "node --loader ts-node/esm build-tokens.ts"
}
}빌드 결과물로 dist/light_brandA.css, dist/light_brandB.css, dist/dark_brandA.css, dist/dark_brandB.css 네 개의 파일이 생성됩니다. 브랜드가 하나 더 추가된다면 Set 파일을 추가하고 $themes.json에 Theme Option을 등록하는 것만으로 빌드 파일이 자동으로 늘어납니다.
런타임 테마 전환 구현
빌드된 CSS 파일을 브라우저에서 동적으로 교체하는 방식으로 런타임 테마 전환을 구현할 수 있습니다.
FOUC(Flash of Unstyled Content): 스타일이 적용되기 전 화면이 잠깐 깜빡이는 현상입니다.
<link>태그를<head>최상단에 위치시키면 방지할 수 있습니다.
<!-- 초기 로드: 기본 테마 CSS를 링크 태그로 참조 -->
<link id="theme-css" rel="stylesheet" href="/tokens/light_brandA.css" />type Mode = 'light' | 'dark';
type Brand = 'brandA' | 'brandB';
function switchTheme(mode: Mode, brand: Brand): void {
const link = document.getElementById('theme-css') as HTMLLinkElement | null;
if (!link) return; // SSR 환경이나 스크립트 실행 시점에 요소가 없을 때 대비
link.setAttribute('href', `/tokens/${mode}_${brand}.css`);
}
// 사용 예시: 다크 모드로 전환
switchTheme('dark', 'brandA');이 접근 방식은 구현이 단순하고 CSS 변수 전체가 교체되므로, 컴포넌트 코드를 전혀 수정하지 않아도 테마가 즉시 반영됩니다.
CI/CD 자동화 — GitHub Actions로 토큰 변경 시 자동 빌드
Tokens Studio는 GitHub 저장소와 직접 동기화되므로, 디자이너가 토큰을 수정하면 PR이 자동으로 생성되고 병합 시 빌드가 트리거되는 파이프라인을 구성할 수 있습니다.
# .github/workflows/build-tokens.yml
name: Build Design Tokens
on:
push:
paths:
- 'tokens/**'
- '$themes.json'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Build all theme permutations
run: pnpm build:tokens
- name: Upload CSS artifacts
uses: actions/upload-artifact@v4
with:
name: theme-css
path: dist/*.csstokens/** 경로 변경이 감지될 때만 워크플로가 실행되므로, 불필요한 빌드를 줄이면서도 토큰 변경 사항이 자동으로 코드에 반영됩니다.
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 단일 진실 공급원 | Figma 디자인과 코드가 동일한 토큰 JSON을 공유해 디자인-개발 간 불일치를 제거합니다 |
| 자동 조합 생성 | permutateThemes로 N×M 조합을 수동 작업 없이 빌드할 수 있습니다 |
| 플랫폼 무관 | CSS 변수, iOS Swift, Android XML, Tailwind 등 다양한 출력 포맷을 지원합니다 |
| Git 동기화 | GitHub/GitLab/Bitbucket과 직접 연동해 PR 기반 토큰 변경 이력을 관리할 수 있습니다 |
| 확장성 | 브랜드나 모드가 추가돼도 Set 하나 추가로 파이프라인 변경 없이 확장됩니다 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| Pro 라이선스 필요 | Themes 기능은 Figma 플러그인 유료 플랜 전용입니다 | 오픈소스 프로젝트라면 $themes.json을 수동으로 작성하는 방법도 있습니다 |
| Figma Variable 모드 수 제한 | Figma Variable의 Mode 수는 Free 플랜 1개, Professional 플랜 4개로 제한됩니다 (Tokens Studio Themes 수와는 별개입니다) | 핵심 조합 우선 적용 또는 독립형 Studio 플랫폼을 활용할 수 있습니다 |
| 학습 곡선 | Set 계층 설계와 $themes.json 구조 이해에 초기 학습 비용이 발생합니다 |
공식 Community File(Figma)로 구조를 먼저 파악하는 것을 권장합니다 |
| 빌드 파일 수 증가 | 조합이 많아지면 CSS 파일 수가 기하급수적으로 늘어납니다 | CDN 캐싱 전략을 활용하거나 CSS @layer 방식으로 전환을 고려할 수 있습니다 |
| JSON 충돌 가능성 | 여러 디자이너가 동시에 수정하면 tokens.json 머지 충돌이 발생할 수 있습니다 |
Set 파일을 담당자별로 분리하고 PR 리뷰 프로세스를 도입하는 것을 권장합니다 |
오버라이드(Override): 상위 Set에서 정의된 토큰을 하위 Set에서 동일한 이름으로 재정의하는 것입니다. Style Dictionary는
source배열에서 후위 파일이 전위 파일을 덮어쓰는 방식으로 동작하기 때문에,tokenSets배열의 순서가 곧 오버라이드 우선순위가 됩니다.
함정과 해결책 — 실무에서 가장 흔한 실수
-
Global Set에 시맨틱 토큰을 섞어 정의하는 경우: Global은 원시 팔레트만, Semantic은 역할 참조만 담아야 계층 오버라이드가 예측 가능하게 동작합니다. 두 계층이 섞이면 특정 모드에서 예상치 못한 값이 노출될 수 있습니다.
-
Set 간 토큰 이름을 다르게 작성하는 경우:
mode/light.json의color.surface.default와mode/dark.json의color.surface.base처럼 이름이 다르면 오버라이드가 동작하지 않고 두 값이 공존하게 됩니다. 모든 Set에서 동일한 토큰 이름 규칙을 유지하는 것이 중요합니다. -
$themes.json의sourcevsenabled구분을 놓치는 경우:source는 참조 값 공급(계산에만 사용, 출력에 직접 포함 안 됨),enabled는 실제 출력에 포함되는 값입니다. Global과 Semantic은source로, 오버라이드 Set은enabled로 설정해야 올바른 CSS가 생성됩니다. 반대로 설정하면 오버라이드 값이 출력에서 누락되거나 중복 포함될 수 있습니다.
마치며
Tokens Studio의 Sets와 Themes를 3계층으로 설계하고 permutateThemes로 빌드 파이프라인을 자동화하면, 디자이너가 토큰을 수정하는 순간부터 모든 브랜드·모드 조합의 CSS가 자동으로 생성되고 배포되는 완전 자동화 디자인 시스템을 운영할 수 있게 됩니다.
지금 바로 시작해 볼 수 있는 3단계:
- Tokens Studio 플러그인 설치 및 Community File 탐색: Figma에서 Tokens Studio 플러그인을 설치한 뒤, Multi-Dimensional Themes Community File을 복제해
global.json,semantic.json,mode/*.json,brand/*.json의 실제 구조를 직접 살펴보시면 좋습니다. GitHub Sync 설정으로 저장소와 연결해 토큰 JSON이 저장소에 동기화되는 흐름도 확인해볼 수 있습니다. - 로컬 빌드 파이프라인 설정:
pnpm add -D @tokens-studio/sd-transforms style-dictionary로 의존성을 설치한 뒤, 위의 빌드 스크립트를 참고해build:tokens스크립트를package.json에 추가하고pnpm build:tokens로 첫 번째 CSS 생성을 확인해볼 수 있습니다. - GitHub Actions로 CI/CD 연동: 위의 YAML을
.github/workflows/build-tokens.yml로 추가하면, 디자이너가 Figma에서 토큰을 저장할 때 자동으로 PR이 생성되고 병합 시 CSS가 빌드되는 코드 리뷰 기반 토큰 변경 이력 관리를 시작할 수 있습니다.
다음 글: Style Dictionary v4의
@layer기반 CSS 출력과 Tailwind v4 연동으로 유틸리티 클래스와 디자인 토큰을 통합하는 방법
참고 자료
공식 문서
- Themes (pro) — Tokens Studio for Figma 공식 문서
- Token Sets — Tokens Studio for Figma 공식 문서
- Themes that switch — Simple Switch Guide
- Style Dictionary + SD Transforms 통합 가이드
- Theme Groups and Theme Options — Studio 플랫폼 문서
실습 자료
- sd-transforms GitHub 레포지터리
- @tokens-studio/sd-transforms npm 패키지
- Tokens Studio: Multi-Dimensional Themes Community File (Figma)
심화 학습