Claude Code × Figma MCP × GitHub Actions: 디자인 토큰 CI/CD 자동화 실전 가이드
디자이너가 버튼 색상을 바꾼 지 사흘이 지났습니다. 슬랙에 공지도 없었고, 티켓도 없었습니다. 개발자 중 누구도 모릅니다. 그 사이 스테이징 환경의 컴포넌트는 Figma와 조금씩 달라져 있고, QA 단계에서야 "왜 색이 달라요?"라는 코멘트가 올라옵니다. 이 현상에는 이름이 있습니다. Design-Code Drift. 디자인 시스템을 운영하는 팀이라면 한 번쯤 겪어봤을 고통입니다.
Figma는 2025년 공식 MCP(Model Context Protocol) 서버를 출시하고, Anthropic과의 파트너십을 통해 Claude Code와의 깊은 통합을 제공하기 시작했습니다. 이 글에서는 Figma MCP 서버를 팀 레포지토리에 설정하고, Figma REST API와 GitHub Actions를 연결해 Figma Variables 변경이 자동으로 코드 PR로 이어지는 완전 자동화 디자인 토큰 파이프라인을 구축하는 전 과정을 다룹니다.
이 가이드를 따라가려면 다음이 필요합니다. Figma Pro 이상 플랜(Starter는 Rate Limit이 월 6회로 실무 사용이 어렵습니다), GitHub Actions 기본 경험, TypeScript와 Node.js 실행 환경. 설정 완료까지 약 2~3시간이 소요됩니다.
핵심 개념
Figma MCP 서버: 스크린샷과 무엇이 다른가
Figma MCP 서버는 Figma의 디자인 데이터(노드 트리, 자동 레이아웃, 디자인 토큰, 컴포넌트 속성 등)를 AI 코딩 도구에 컨텍스트로 직접 제공하는 브리지입니다. Claude Code, Cursor, VS Code Copilot, Windsurf 등 MCP를 지원하는 모든 에디터에서 사용할 수 있습니다.
MCP(Model Context Protocol)란? Anthropic이 제안한 오픈 표준으로, AI 에이전트가 외부 도구·데이터 소스와 표준화된 방식으로 통신할 수 있게 해주는 프로토콜입니다.
기존의 스크린샷 기반 접근법은 LLM이 이미지를 보고 색상값을 추정합니다. MCP를 사용하면 LLM이 Figma 내부 노드 구조에 직접 접근합니다. 특히 get_variable_defs 툴 호출로 선택한 프레임의 모든 변수를 추출하고, get_code_connect_data 툴로 실제 컴포넌트 임포트 경로까지 받아옵니다. 결과적으로 #3B82F6 같은 하드코딩 대신 --color-primary-500이라는 실제 토큰 이름이 코드에 그대로 사용됩니다.
전체 아키텍처: 3계층 구조
이 가이드에서 구축하는 시스템은 세 개의 계층으로 구성됩니다.
┌─────────────────────────────────────────────┐
│ 1. MCP Layer │
│ Claude Code ↔ Figma MCP Server │
│ (로컬 개발 시 실시간 디자인 컨텍스트 조회) │
├─────────────────────────────────────────────┤
│ 2. Token Sync Layer │
│ Figma Variables → Style Dictionary │
│ → 플랫폼별 토큰 파일 (CSS / iOS / Android) │
├─────────────────────────────────────────────┤
│ 3. CI/CD Layer │
│ GitHub Actions → 토큰 변경 감지 │
│ → 자동 PR 생성 → 리뷰 → 배포 │
└─────────────────────────────────────────────┘1계층(MCP)은 로컬 개발 전용이고, 3계층(CI/CD)은 Figma REST API를 직접 호출합니다. CI/CD 환경에서 MCP를 직접 사용할 수 없는 이유는 MCP가 사람이 에디터를 통해 인증하는 구조이기 때문입니다. 자동화 파이프라인은 서비스 토큰으로 REST API를 호출하는 별도 경로를 사용합니다.
Style Dictionary v4와 W3C 표준
디자인 토큰(Design Token)이란? 색상, 스페이싱, 타이포그래피 등 디자인 결정값을 플랫폼 독립적인 변수로 표현한 것입니다.
--color-primary-500: #3B82F6처럼 이름을 가진 값으로, 웹·iOS·Android 전체에서 디자인 일관성을 유지하는 핵심 수단입니다.
Style Dictionary는 Amazon이 오픈소스로 공개한 토큰 변환 도구입니다. 단일 JSON 소스에서 CSS Variables, Swift, Kotlin 등 다양한 플랫폼 코드를 생성합니다. 2025년을 기점으로 W3C Design Token Community Group의 표준 포맷이 Style Dictionary v4에 통합되어 Figma Variables와의 호환성이 크게 향상됐습니다.
// tokens/color.json (W3C 표준 포맷 — Style Dictionary v4 기준)
{
"color": {
"primary": {
"500": {
"$value": "#3B82F6",
"$type": "color",
"$description": "Primary brand color"
}
}
}
}Style Dictionary v3 → v4 마이그레이션 주의: v4는 W3C 표준 포맷(
$value,$type) 도입과 함께 설정 파일 구조에 Breaking Change가 있습니다. 기존 v3 프로젝트를 이 가이드 설정과 혼용하면 오류가 발생할 수 있으므로, 신규 프로젝트라면 v4로 시작하고, 기존 프로젝트라면 공식 마이그레이션 가이드를 먼저 확인하는 것을 권장합니다.
실전 적용
아래 세 단계는 순서대로 따라가도록 설계되어 있습니다. 1단계(MCP 설정)가 완료된 뒤 2단계(토큰 추출 스크립트), 그 다음 3단계(GitHub Actions 연결) 순으로 진행하시면 됩니다.
팀 공유 MCP 설정: .mcp.json을 Git에 커밋하기
팀 전체가 동일한 MCP 서버 설정을 공유하려면 .mcp.json을 프로젝트 루트에 커밋합니다. 이렇게 하면 새 팀원이 합류할 때 환경변수 하나만 설정하면 바로 동일한 환경에서 작업할 수 있습니다.
// .mcp.json (프로젝트 루트에 커밋)
{
"mcpServers": {
"figma": {
"type": "http",
"url": "https://mcp.figma.com/mcp",
"headers": {
"Authorization": "Bearer ${FIGMA_PERSONAL_ACCESS_TOKEN}"
}
}
}
}${FIGMA_PERSONAL_ACCESS_TOKEN}은 환경변수 참조 형식입니다. 실제 토큰 값을 이 파일에 직접 작성하면 Git에 시크릿이 노출되므로 반드시 환경변수 형식을 유지해야 합니다.
각 팀원은 로컬 쉘 설정(~/.zshrc 또는 ~/.bashrc)에 개인 토큰을 추가합니다.
# ~/.zshrc
export FIGMA_PERSONAL_ACCESS_TOKEN="figd_your_personal_access_token"Claude Code CLI에서 직접 등록하는 방법도 있습니다.
# 프로젝트 범위 MCP 등록
# --scope project: 현재 프로젝트에만 적용
# --transport http: HTTP 기반 Remote 서버 방식
claude mcp add --scope project --transport http figma https://mcp.figma.com/mcp
# 사용자 전역 범위 등록
# --scope user: 이 사용자의 모든 프로젝트에 적용
claude mcp add --scope user --transport http figma https://mcp.figma.com/mcp| 설정 방식 | 적용 범위 | 권장 용도 |
|---|---|---|
.mcp.json 커밋 |
프로젝트 전체 팀원 | 팀 공유 표준 설정 |
--scope project |
현재 프로젝트 | 개인이 프로젝트별 추가 설정 |
--scope user |
사용자 전체 환경 | 개인 전역 설정 |
Figma 디자인을 컴포넌트로 직접 변환: MCP가 토큰 이름을 전달하는 과정
MCP 서버가 연결된 상태에서 Claude Code에 Figma 링크를 전달하면 내부적으로 다음이 일어납니다.
- Claude Code가
get_variable_defs툴을 호출해 해당 프레임의 모든 Variables를 추출합니다. get_code_connect_data툴로 연결된 코드 컴포넌트의 임포트 경로와 Props 인터페이스를 가져옵니다.- 이 정보가 LLM 컨텍스트에 포함된 채로 코드 생성이 시작됩니다.
# Claude Code 세션에서 실행
claude "Figma 파일 https://figma.com/file/ABC123 의 Button 컴포넌트를
우리 디자인 시스템 토큰을 사용해 React 컴포넌트로 구현해줘"MCP를 통해 LLM이 --color-primary-500, --spacing-2, --border-radius-md 같은 실제 토큰 이름을 컨텍스트로 받았기 때문에, 결과 코드는 하드코딩 없이 토큰 변수명을 그대로 사용합니다.
// MCP 컨텍스트로 생성된 Button 컴포넌트
// --color-primary-500, --spacing-* 등 토큰 이름은 get_variable_defs 응답에서 왔습니다
import styles from './Button.module.css';
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
}
export function Button({ variant = 'primary', size = 'md', children }: ButtonProps) {
return (
<button className={`${styles.button} ${styles[variant]} ${styles[size]}`}>
{children}
</button>
);
}/* Button.module.css — 토큰 변수명이 하드코딩 없이 그대로 사용됨 */
.button {
background-color: var(--color-primary-500);
color: var(--color-neutral-0);
padding: var(--spacing-2) var(--spacing-4);
border-radius: var(--border-radius-md);
font-size: var(--font-size-sm);
}Code Connect란? Figma 컴포넌트와 실제 코드 컴포넌트를 연결해, MCP가 LLM에게 컴포넌트의 실제 임포트 경로와 Props 인터페이스를 전달할 수 있게 해주는 Figma 기능입니다. Organization/Enterprise 플랜에서 사용 가능합니다. 소규모 팀이라면 Code Connect 없이 Variables 추출만으로도 토큰 동기화 파이프라인을 구축할 수 있습니다.
GitHub Actions로 디자인 토큰 CI/CD 파이프라인 구축하기
전체 자동화 흐름은 다음과 같습니다.
Figma Variables 변경
↓
Figma REST API (/v1/files/:file_id/variables/local)
↓
tokens/figma-variables.json → Git Push
↓
GitHub Actions 트리거
↓
Style Dictionary 변환 (CSS / iOS / Android)
↓
자동 PR 생성 → 리뷰 → Merge → 배포토큰 추출 스크립트부터 시작합니다. Figma REST API에는 두 가지 Variables 엔드포인트가 있습니다. /v1/files/:file_id/variables는 모든 변수(퍼블리싱된 라이브러리 포함)를 반환하고, /v1/files/:file_id/variables/local은 해당 파일에 정의된 로컬 변수만 반환합니다. 디자인 시스템 토큰 추출 목적이라면 /local 엔드포인트를 사용하는 것이 의도에 맞습니다.
// scripts/export-tokens.ts
import fs from 'fs/promises';
interface FigmaVariable {
id: string;
name: string;
resolvedType: string;
valuesByMode: Record<string, unknown>;
}
interface FigmaVariableCollection {
id: string;
name: string;
modes: Array<{ modeId: string; name: string }>;
defaultModeId: string;
}
function transformVariablesToTokens(
variables: Record<string, FigmaVariable>,
collections: Record<string, FigmaVariableCollection>
): Record<string, unknown> {
const tokens: Record<string, unknown> = {};
for (const variable of Object.values(variables)) {
const collection = collections[
Object.keys(collections).find(id =>
collections[id].name === variable.name.split('/')[0]
) ?? ''
];
const defaultModeId = collection?.defaultModeId;
const value = defaultModeId ? variable.valuesByMode[defaultModeId] : undefined;
// "color/primary/500" → { color: { primary: { "500": { $value, $type } } } }
const parts = variable.name.split('/');
let cursor = tokens;
for (let i = 0; i < parts.length - 1; i++) {
if (!cursor[parts[i]]) cursor[parts[i]] = {};
cursor = cursor[parts[i]] as Record<string, unknown>;
}
cursor[parts[parts.length - 1]] = {
$value: value,
$type: variable.resolvedType.toLowerCase(),
};
}
return tokens;
}
async function main() {
const fileId = process.env.FIGMA_FILE_ID;
const token = process.env.FIGMA_TOKEN;
if (!fileId || !token) {
throw new Error('FIGMA_FILE_ID와 FIGMA_TOKEN 환경변수가 필요합니다');
}
// /local 엔드포인트: 이 파일에 정의된 로컬 변수만 반환
const response = await fetch(
`https://api.figma.com/v1/files/${fileId}/variables/local`,
{ headers: { 'X-Figma-Token': token } }
);
if (!response.ok) {
throw new Error(`Figma API 오류: ${response.status} ${response.statusText}`);
}
const { meta } = await response.json();
const tokens = transformVariablesToTokens(
meta.variables,
meta.variableCollections
);
await fs.mkdir('tokens', { recursive: true });
await fs.writeFile('tokens/figma-variables.json', JSON.stringify(tokens, null, 2));
// GitHub Actions output 설정
const changedCount = Object.keys(meta.variables).length;
const outputFile = process.env.GITHUB_OUTPUT;
if (outputFile) {
await fs.appendFile(outputFile, `changed_count=${changedCount}\n`);
}
console.log(`토큰 추출 완료: ${changedCount}개`);
}
main().catch(err => {
console.error(err);
process.exit(1);
});Style Dictionary 설정 파일입니다. 아래 예시는 ES Module(export default) 기준입니다. CommonJS 환경이라면 export default 대신 module.exports =로 교체하면 됩니다.
// style-dictionary.config.js (ES Module 기준)
// CommonJS 환경이라면: module.exports = { ... }
export default {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'src/styles/tokens/',
files: [{ destination: 'variables.css', format: 'css/variables' }],
},
ios: {
transformGroup: 'ios-swift',
buildPath: 'ios/DesignTokens/',
files: [{ destination: 'DesignTokens.swift', format: 'ios-swift/class.swift' }],
},
android: {
transformGroup: 'android',
buildPath: 'android/src/main/res/values/',
files: [{ destination: 'design_tokens.xml', format: 'android/resources' }],
},
},
};GitHub Actions 워크플로우 전체입니다.
# .github/workflows/sync-design-tokens.yml
name: Sync Design Tokens
on:
push:
paths:
- 'tokens/**'
workflow_dispatch:
inputs:
figma_file_id:
description: 'Figma File ID'
required: true
jobs:
sync-tokens:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 9
- name: Install dependencies
run: pnpm install
- name: Export Figma Variables
id: export
env:
FIGMA_TOKEN: ${{ secrets.FIGMA_PERSONAL_ACCESS_TOKEN }}
FIGMA_FILE_ID: ${{ github.event.inputs.figma_file_id || vars.FIGMA_FILE_ID }}
run: pnpm tokens:export
- name: Build Tokens
run: pnpm tokens:build
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: sync design tokens from Figma'
title: '[Design System] Figma 토큰 자동 동기화'
branch: 'chore/sync-design-tokens'
body: |
Figma Variables에서 자동으로 추출된 토큰 변경사항입니다.
- 변경된 토큰: ${{ steps.export.outputs.changed_count }}개
변경 전 리뷰 후 Merge해 주시기 바랍니다.steps.export.outputs.changed_count는 위 export-tokens.ts 스크립트에서 GITHUB_OUTPUT에 기록한 값이 자동으로 연결됩니다.
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 공식 지원 | Figma + Anthropic 공식 파트너십으로 안정적인 업데이트가 보장됩니다 |
| 내부 데이터 직접 접근 | 노드 구조에 직접 접근해 스크린샷 기반 대비 정확도가 높습니다 |
| Code Connect 연동 | 실제 컴포넌트 임포트 경로와 Props 인터페이스를 LLM에 전달합니다 |
| 설정 간소화 | Remote 서버 방식으로 데스크톱 앱 설치가 불필요하고, OAuth 인증을 5분 내에 완료할 수 있습니다 |
| 버전 관리 통합 | .mcp.json을 Git에 커밋해 팀 전체가 동일한 설정을 공유할 수 있습니다 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| Rate Limit | Starter: 월 6회, Pro: 일 제한, Enterprise: 일 600회 | 팀 운영 시 Pro 이상 플랜을 검토해 보시기 바랍니다 |
| CI/CD 직접 실행 불가 | GitHub Actions 등 서버리스 환경에서 MCP 인증이 불가합니다 | 토큰 추출은 Figma REST API를 별도로 사용합니다 |
| 베타 안정성 | 베타 단계로 간헐적 연결 오류가 보고됩니다 | 생성된 코드는 반드시 리뷰 후 사용하는 것을 권장합니다 |
| 초기 도입 비용 | Code Connect 완전 구성까지 40~80시간이 필요합니다 | 토큰 동기화부터 단계적으로 도입해 보시면 좋습니다 |
| Enterprise 요건 | Code Connect 등 핵심 기능은 Organization/Enterprise 플랜이 필요합니다 | 소규모 팀은 Figma REST API 직접 호출로 대체할 수 있습니다 |
실무에서 가장 흔한 실수
-
.mcp.json에 토큰을 하드코딩하는 경우:FIGMA_PERSONAL_ACCESS_TOKEN실제 값을 파일에 직접 작성하면 Git에 시크릿이 노출됩니다. 반드시 환경변수 참조(${FIGMA_PERSONAL_ACCESS_TOKEN}) 형식을 사용하고,.env파일은.gitignore에 추가해 두는 것을 권장합니다. -
SSOT(단일 진실 공급원) 전략 없이 시작하는 경우: Figma Variables를 진실 공급원으로 할지, Git 내
tokens.json을 기준으로 할지 팀 합의 없이 시작하면 양쪽이 충돌하는 상황이 발생합니다. 파이프라인 구축 전에 이 결정을 먼저 내리는 것을 권장합니다. -
토큰 네이밍 컨벤션을 사후에 정하는 경우: Figma Variable 이름이 곧 코드 토큰 이름(
--color-primary-500)이 됩니다. 초기 도입 시점에color/primary/500형식의 네이밍 규칙을 확립하지 않으면, 나중에 전체 토큰 이름을 마이그레이션해야 하는 상황이 생길 수 있습니다.
트러블슈팅
파이프라인 설정 과정에서 자주 마주치는 문제들입니다.
MCP 연결 실패 시: Claude Code에서 claude mcp list로 등록 상태를 확인합니다. FIGMA_PERSONAL_ACCESS_TOKEN 환경변수가 현재 셸 세션에 로드되어 있는지(echo $FIGMA_PERSONAL_ACCESS_TOKEN), Figma에서 토큰이 만료되지 않았는지 확인합니다.
tokens:export 실패 시: Figma API 응답 코드를 먼저 확인합니다. 403이면 토큰 권한 문제(파일 접근 권한 확인), 404이면 FIGMA_FILE_ID가 잘못된 경우입니다. Rate Limit에 걸렸다면 429 응답과 함께 Retry-After 헤더가 반환됩니다.
Style Dictionary 변환 실패 시: v3와 v4의 설정 포맷이 다릅니다. $value / $type 키는 v4 전용이며, v3에서는 value / type을 사용합니다. package.json에서 설치된 버전을 먼저 확인하는 것을 권장합니다.
마치며
Figma MCP 서버와 GitHub Actions를 조합하면, 디자이너의 Variables 변경이 자동으로 코드 PR로 이어지는 완전 자동화 디자인 시스템 파이프라인을 구축할 수 있습니다.
지금 당장 시작할 수 있는 가장 작은 첫 행동은 .mcp.json 파일 하나를 만들어 팀 레포지토리에 PR을 올려보는 것입니다. 설정 전체를 한 번에 도입할 필요는 없습니다.
-
MCP 연결 설정: 프로젝트 루트에
.mcp.json파일을 생성하고https://mcp.figma.com/mcp엔드포인트를 등록합니다. 각 팀원이FIGMA_PERSONAL_ACCESS_TOKEN환경변수를 로컬에 설정하면 팀 공유 MCP 환경이 준비됩니다. -
토큰 추출 스크립트 작성: Figma REST API
/v1/files/:file_id/variables/local을 호출하는scripts/export-tokens.ts를 작성하고, Style Dictionary v4 설정 파일을 추가합니다.pnpm tokens:export && pnpm tokens:build로 로컬에서 먼저 동작을 확인해 보시기 바랍니다. -
GitHub Actions 파이프라인 연결:
.github/workflows/sync-design-tokens.yml에 워크플로우를 추가하고, 레포지토리 Secrets에FIGMA_PERSONAL_ACCESS_TOKEN과 Variables에FIGMA_FILE_ID를 등록하면tokens/폴더 변경 시 자동으로 PR이 생성되는 파이프라인이 완성됩니다.
다음 글: Style Dictionary v4의 W3C 표준 포맷을 활용해 웹·iOS·Android 세 플랫폼에 동시 배포하는 크로스플랫폼 디자인 토큰 전략을 다룰 예정입니다.
참고 자료
공식 문서
- Guide to the Figma MCP server | Figma Help
- Remote Server Installation | Figma Developer Docs
- Plans, access, and permissions | Figma Developer Docs
- Connect Claude Code to tools via MCP | Claude Code Docs
블로그 & 사례
- Claude Code + Figma MCP Server | Builder.io
- Introducing Claude Code to Figma | Figma Blog
- Design Systems And AI: Why MCP Servers Are The Unlock | Figma Blog
- Building a Figma-Driven MCP Production Pipeline | Francesca Tabor
- Syncing Figma Variables and Style Dictionary with GitHub Actions | James Ives
도구 레포