Figma → 코드 자동화 퍼블리싱 전략
"디자인 시안이 오면, 코드가 알아서 나온다"
들어가며
프론트엔드 개발자로 일하다 보면 이런 순간이 반복됩니다.
"Figma 링크 공유드렸어요. 이번 주 안에 퍼블리싱 부탁드립니다."
그러면 개발자는 Figma를 열고, 색상을 눈으로 확인하고, 폰트 사이즈를 하나씩 읽고, 아이콘을 export하고, 이미지를 다운받고... 이 과정을 수작업으로 반복합니다.
현재 진행 중인 외주 프로젝트에서 이 반복이 너무 잦았다. 관리자 대시보드 페이지만 수십 개인데, 매번 같은 수작업을 하고 있으니 "이걸 자동화할 수 있지 않을까?" 하는 생각이 들었고, 실제로 만들어서 적용해보고 있다.
이 글은 그 자동화 파이프라인의 설계와 실제 적용 경험을 기록한다. Claude(AI), Python 스크립트, Figma API를 조합해서 Figma URL 하나를 넣으면 코드가 나오는 시스템을 구성했고, 외주 프로젝트의 어드민 페이지 퍼블리싱에 실전 투입 중이다.
Python도, 쉘도, Linux도 몰라도 됩니다. 이 글은 그 개념부터 설명합니다.
목차
- 사용한 도구들 — 개념 설명
- 전체 파이프라인 한눈에 보기
- PRE — 작업 시작 전 청소
- PHASE 0 — Figma 분석 (AI 없이)
- PHASE 1 — 자산 추출 (아이콘 + 이미지)
- PHASE 1.5 — 섹션 분할 전략
- PHASE 2 — AI가 코드를 쓴다
- PHASE 3 — 타입 검사 + 조립 확인
- POST — 정리
- 완료 후 — QA 파이프라인
- 핵심 설계 원칙 정리
1. 사용한 도구들 — 개념 설명
Claude (AI)
Claude는 Anthropic이 만든 AI입니다. 우리가 흔히 쓰는 ChatGPT와 비슷하지만, 터미널(명령줄)에서 코드처럼 실행할 수 있는 Claude Code라는 도구가 있습니다.
claude -p "이 디자인 스펙을 보고 React 코드를 작성해줘"이렇게 명령어 하나로 AI에게 작업을 시킬 수 있습니다.
주니어를 위한 비유: Claude Code는 "AI 직원"입니다. -p 뒤에 지시사항을 주면 그걸 읽고 코드를 작성합니다.
Skill (스킬)
Claude Code에는 스킬이라는 개념이 있습니다. 자주 하는 작업의 절차를 파일로 저장해두면, Claude가 그 절차를 그대로 따릅니다.
~/.claude/skills/figma-publish-tailwind/SKILL.md이 파일 안에 "STEP 1은 이렇게, STEP 2는 이렇게..."라고 적어두면, /figma-publish-tailwind라고 입력하는 것만으로 Claude가 그 절차를 실행합니다.
주니어를 위한 비유: 레시피 책입니다. 요리사(Claude)에게 "레시피 3번 따라해줘"라고 하면 그대로 실행하는 것.
Python 스크립트
Python은 프로그래밍 언어입니다. 이 파이프라인에서 Python은 AI를 쓰지 않아도 되는 단순하고 반복적인 작업을 담당합니다.
- Figma API에서 데이터 가져오기
- JSON 파일 파싱
- 아이콘/이미지 파일 다운로드
- 섹션 분할 계산
주니어를 위한 비유: Python은 AI가 필요 없는 "단순 노동"을 담당하는 로봇입니다. 정해진 규칙대로만 일하지만 빠르고 실수가 없습니다.
/tmp 디렉토리
/tmp는 Linux/macOS에 기본으로 있는 임시 파일 보관함입니다.
/tmp/figma-analysis.json
/tmp/figma-spec-shared.json이 파이프라인에서 각 단계(PHASE)가 결과물을 여기에 저장하고, 다음 단계가 이 파일을 읽어서 작업합니다. 파이프라인의 단계 간 통신 수단입니다.
주니어를 위한 비유: 공장의 컨베이어 벨트 위에 놓인 중간 작업물입니다. 1번 공정이 반제품을 벨트에 올리면, 2번 공정이 집어서 작업하는 구조.
재시작(PRE/POST)하면 삭제됩니다. 시스템 재부팅 시에도 자동 삭제됩니다.
MCP (Model Context Protocol)
MCP는 Claude가 외부 서비스에 직접 접근할 수 있게 해주는 프로토콜입니다. 이 파이프라인에서는 Figma MCP를 사용합니다.
Claude → MCP → Figma API → 노드 데이터Claude가 Figma URL을 받으면, MCP를 통해 Figma 서버에서 직접 디자인 데이터를 가져올 수 있습니다.
주니어를 위한 비유: AI에게 Figma 계정을 대신 로그인해서 쓸 수 있게 권한을 준 것입니다.
MCP가 제공하는 주요 Figma 도구:
| 도구 | 역할 |
|---|---|
get_design_context | 노드의 전체 디자인 스펙 + 스크린샷 반환 |
get_metadata | 노드 트리 구조 (이름, 크기, 위치) |
get_variable_defs | 디자인 토큰 변수 값 반환 |
pnpm tokript
tokript는 외주 프로젝트에 구성한 전용 CLI 도구다. Figma 토큰과 자산을 코드에서 쓸 수 있는 형태로 변환합니다.
pnpm tokript gen:icon # 아이콘 → MY_ICONS 객체로
pnpm tokript gen:img # 이미지 → MY_IMAGES 객체로코드에서 이렇게 사용: MY_ICONS['C-arrow'], MY_IMAGES['hero-banner']
2. 전체 파이프라인 한눈에 보기
사용자 입력: /figma-to-code-tailwind <Figma URL> --page src/app/dashboard
│
▼
figma_publish.py 실행
│
┌─────────────┼──────────────────────┐
│ │ │
PRE PHASE 0 PHASE 1
/tmp 청소 Figma 분석 자산 추출
마커 저장 (Python, AI 없음) (Python, AI 없음)
│ │
└──────────┬───────────┘
│
PHASE 1.5
섹션 분할
(Python, AI 없음)
│
PHASE 2
섹션별 코드 생성
(claude -p × N번)
│
PHASE 3
type-check
page.tsx 확인
│
POST
/tmp 청소
│
▼
/publish-qa 권장핵심 원칙: AI(LLM)는 코드 생성(PHASE 2)에만 사용합니다. 나머지는 Python이 처리해서 비용과 시간을 최소화합니다.
3. PRE — 작업 시작 전 청소
무엇을 하나?
이전 작업의 임시 파일이 남아 있으면 새 작업에 오염될 수 있습니다. PRE 단계는 관련된 /tmp 파일을 모두 삭제합니다.
삭제되는 파일들
/tmp/figma-spec-cache.md ← 이전 디자인 스펙 캐시
/tmp/figma-spec-shared.json ← 이전 공유 컨텍스트
/tmp/figma-result-section-*.md ← 이전 섹션별 결과
/tmp/figma-spec-section-*.json ← 이전 섹션별 스펙
/tmp/.current-filekey ← 이전 Figma 파일키
/tmp/.current-nodeid ← 이전 Figma 노드 ID
/tmp/.current-page ← 이전 페이지 경로
/tmp/.current-name ← 이전 컴포넌트 이름
/tmp/.active-skill ← 이전 활성 스킬 이름마커 파일 저장
청소 후, 현재 작업의 정보를 마커 파일로 저장합니다.
/tmp/.current-filekey → "abc123def456" (Figma URL에서 추출)
/tmp/.current-nodeid → "123:456" (node-id 파라미터에서 추출)
/tmp/.current-page → "src/app/dashboard"
/tmp/.current-name → "DashboardSection"
/tmp/.active-skill → "figma-to-code-tailwind"왜 마커 파일인가? figma_publish.py가 실행하는 claude -p 서브프로세스들은 별도의 독립 프로세스입니다. 원래 명령의 인자(--page, --name)를 직접 받을 수 없기 때문에, 파일 시스템을 통해 공유합니다.
4. PHASE 0 — Figma 분석 (AI 없이)
무엇을 하나?
figma_analyze.py가 Figma URL을 받아 전체 디자인 스펙을 JSON으로 변환합니다. LLM(AI)을 전혀 사용하지 않습니다.
python3 figma_analyze.py "https://figma.com/design/..." --app admin --cache-spec어떻게 작동하나?
- URL에서 fileKey와 nodeId를 파싱
- Figma REST API에 직접 요청 → 전체 노드 트리 수신
- 프로젝트의
token.json을 읽어 역색인(inverted index) 빌드- 역색인 예시:
"#1A1A2E"→"color-primary-900","#F5F5F5"→"color-grey-50"
- 역색인 예시:
- 노드 트리를 순회하면서 색상, 텍스트스타일을 토큰 이름으로 자동 매핑
- 결과를
/tmp/figma-analysis.json에 저장
역색인이 왜 중요한가?
AI에게 "이 색상 #1A1A2E는 무슨 토큰이야?"라고 물어보면 AI는 모릅니다. 추측하거나 틀립니다.
역색인은 hex 값 → 토큰 이름을 미리 계산해둔 사전입니다. AI가 추측할 필요 없이 정확한 토큰 이름을 바로 씁니다.
출력: /tmp/figma-analysis.json
{
"meta": {
"appName": "admin",
"fileKey": "abc123",
"nodeId": "123:456"
},
"nodeTree": {
"id": "123:456",
"name": "Dashboard Page",
"children": [ "..." ]
},
"tokenIndexFull": {
"#1A1A2E": "color-primary-900",
"#F5F5F5": "color-grey-50"
},
"summaries": {
"texts": ["제목 텍스트", "설명 텍스트"],
"icons": [
{ "name": "C-arrow-right", "nodeId": "111:222" }
],
"images": [
{ "name": "hero-banner", "nodeId": "333:444" }
],
"unresolvedColors": ["#FF0000"]
}
}출력: /tmp/figma-spec-cache.md
같은 내용을 사람이 읽기 쉬운 마크다운으로도 저장합니다. 서브에이전트가 빠르게 전체 컨텍스트를 파악할 때 사용합니다.
5. PHASE 1 — 자산 추출 (아이콘 + 이미지)
무엇을 하나?
PHASE 0에서 발견된 아이콘과 이미지를 실제 파일로 다운로드합니다. 이것도 Python이 처리합니다.
아이콘 추출 (figma_export_icons.py)
python3 figma_export_icons.py --app admin분류 규칙:
| 노드 이름 | 처리 방식 |
|---|---|
C-로 시작 (예: C-arrow-right) | SVG로 export → public/icons/ 저장 |
| Phosphor, Lucide 등 라이브러리 아이콘 | 스킵 (코드에서 npm 패키지 import) |
SVG 처리: 추출한 SVG의 fill과 stroke 색상을 모두 currentColor로 치환합니다. 그래야 CSS로 색상을 자유롭게 제어할 수 있습니다.
추출 후 자동 실행:
pnpm tokript gen:icon
# → MY_ICONS 객체 갱신
# → 코드에서: import { MY_ICONS } from '@/constants/icons'
# <img src={MY_ICONS['C-arrow-right']} />중요한 규칙: 아이콘의 배경(컨테이너 FRAME)은 추출하지 않습니다. 아이콘 자체 SVG 노드만 추출하고, 배경은 코드에서 Box 컴포넌트 + 색상 토큰으로 구현합니다.
이미지 추출 (figma_export_images.py)
python3 figma_export_images.py --app admin1x / 2x 동시 요청 (레티나 디스플레이 대응):
public/images/hero-banner.png ← 1x (일반 화면)
public/images/hero-banner@2x.png ← 2x (레티나 화면)추출 후 자동 실행:
pnpm tokript gen:img
# → MY_IMAGES 객체 갱신
# → 코드에서: import { MY_IMAGES } from '@/constants/images'
# <ImageAsNext src={MY_IMAGES['hero-banner']} />왜 <img> 태그를 안 쓰나? Next.js의 <Image> 컴포넌트(ImageAsNext)를 사용해야 자동 최적화(WebP 변환, lazy loading, 크기 최적화)가 적용됩니다.
6. PHASE 1.5 — 섹션 분할 전략
무엇을 하나?
하나의 Figma 페이지를 여러 섹션으로 나눠서, 각 섹션을 독립적으로 구현할 수 있게 준비합니다.
분할 전략
자식 노드 개수 → 분할 방식
─────────────────────────────────────────
≤ 3개 → 단일 에이전트 1개 (전체 처리)
4 ~ 7개 → 2그룹으로 분할
8개 이상 → 섹션별 개별 분할예시 (8개 자식 노드인 경우):
Dashboard Page
├── Header → section-1-header.json
├── KPI Cards → section-2-kpi-cards.json
├── Chart Area → section-3-chart-area.json
├── Table → section-4-table.json
└── ...공유 컨텍스트 생성: /tmp/figma-spec-shared.json
섹션 분할과 동시에 모든 에이전트가 공통으로 참조할 파일을 생성합니다.
{
"framework": "tailwind",
"pagePath": "src/app/dashboard",
"compName": "",
"meta": { "appName": "admin" },
"tokenIndex": {
"#1A1A2E": "color-primary-900"
},
"assets": {
"icons": ["C-arrow-right", "C-close", "C-search"],
"images": ["hero-banner", "product-thumb"]
},
"sections": [
{ "index": 1, "name": "header" },
{ "index": 2, "name": "kpi-cards" }
],
"sectionResults": {}
}이 파일이 중요한 이유: 각 섹션 에이전트는 독립적으로 실행되는 별도 프로세스입니다. 이 파일이 없으면 "사용 가능한 아이콘 이름"이나 "토큰 매핑"을 각자 다시 계산해야 합니다.
섹션별 스펙 파일: /tmp/figma-spec-section-N-name.json
{
"sectionIndex": 1,
"sectionName": "header",
"specCache": "...전체 스펙 마크다운 요약...",
"children": [
{ "id": "111:222", "name": "Logo" },
{ "id": "333:444", "name": "NavMenu" }
]
}7. PHASE 2 — AI가 코드를 쓴다
무엇을 하나?
준비된 스펙 파일들을 읽고, claude -p 명령으로 섹션별 코드를 순차 생성합니다.
왜 순차(병렬 아님)인가?
섹션 1 완료 → shared.json에 결과 병합
섹션 2 시작 → shared.json에서 섹션 1의 interface 읽음
섹션 3 시작 → 섹션 1, 2의 interface를 모두 알고 import 가능예를 들어 섹션1이 HeaderSection 컴포넌트를 만들면, 섹션2가 page.tsx에서 이를 import할 때 정확한 컴포넌트 이름을 알 수 있습니다. 병렬로 실행하면 섹션2가 섹션1의 결과를 모릅니다.
서브에이전트에게 전달되는 프롬프트
Figma 디자인의 **섹션 1: header**을 Next.js + Tailwind CSS + shadcn/ui 코드로 구현한다.
## 사전 준비된 데이터 (반드시 Read)
1. /tmp/figma-spec-shared.json — 공유 컨텍스트 (토큰 매핑, 자산 목록)
2. /tmp/figma-spec-section-1-header.json — 이 섹션의 디자인 스펙
3. /tmp/figma-spec-cache.md — 전체 디자인 스펙 마크다운 요약
## 실행
/figma-publish-tailwind 스킬을 호출하여 구현한다.서브에이전트의 작업 절차 (figma-publish-tailwind 스킬)
STEP 0: get_metadata로 섹션 자식 노드 목록 파악
STEP 1: get_design_context를 섹션별 개별 호출 (스킵 금지)
STEP 2: boundVariables → get_variable_defs → 토큰 이름 확정
STEP 3: 아이콘 data-name 속성 확인, 이미지 MY_IMAGES 참조
STEP 4: shadcn/ui 컴포넌트 매핑 + 컴포넌트 네스팅 설계
STEP 5: TSX 코드 작성 (cn() + Tailwind 클래스)
STEP 6: 완료 보고4대 누락 방지 원칙
| 항목 | 확인 방법 |
|---|---|
| 색상 | boundVariables → get_variable_defs로 토큰 이름 확인 |
| 아이콘 | Figma 노드의 data-name 속성 직접 확인 |
| 레이아웃 | layoutMode, padding, gap 값 직접 읽기 |
| 요소 위치 | x, y, width, height 직접 읽기 |
opacity 처리 규칙
fill opacity가 1.0 미만이면 시맨틱 토큰 대신 rgba() 직접 사용.
// ❌ opacity가 0.2인데 토큰을 그냥 쓰면 색이 다름
bg-color-yellow-500
// ✅ rgba로 직접 표현
style={{ backgroundColor: 'rgba(251, 192, 55, 0.2)' }}결과 파일: /tmp/figma-result-section-N.md
서브에이전트는 작업 완료 후 반드시 이 파일을 생성합니다.
## 섹션 1 구현 결과
### 생성된 파일
- src/components/dashboard/HeaderSection.tsx: export HeaderSection
- src/components/dashboard/NavMenu.tsx: export NavMenu
### 주요 interface
interface HeaderSectionProps {
title: string
userName: string
}오케스트레이터는 이 파일이 50바이트 초과인지 검증하고, 통과하면 shared.json의 sectionResults에 병합합니다.
8. PHASE 3 — 타입 검사 + 조립 확인
무엇을 하나?
page.tsx파일이 존재하는지 확인- TypeScript 타입 검사 실행
타입 검사 자동 감지
package.json을 읽어서 프로젝트에 맞는 명령을 자동 선택합니다.
if 'type-check' in scripts:
cmd = ['pnpm', 'type-check']
elif 'typecheck' in scripts:
cmd = ['pnpm', 'typecheck']
else:
cmd = ['pnpm', 'tsc', '--noEmit'] # 폴백타입 오류가 있으면 파이프라인이 중단됩니다. 코드를 배포하기 전에 타입 안정성을 강제하는 장치입니다.
9. POST — 정리
작업이 완료되면 /tmp의 모든 임시 파일을 삭제합니다.
- 삭제 대상: PRE에서 만든 모든 파일
- 이유: 다음 퍼블리싱 작업이 깨끗한 상태에서 시작하도록
10. 완료 후 — QA 파이프라인
/publish-qa <Figma URL> <source 디렉토리>내부적으로 publish_qa.py가 실행되며 4단계를 순차 처리합니다.
STEP 1: design-qa
Figma 시안 vs 생성된 코드 픽셀 수준 대조
불일치 항목 리스트업
→ /tmp/publish-qa-step1.md
STEP 2: publish-refactor
프로젝트 컨벤션 정합성 검사
import 방식, 파일 구조, 타입 정의, 상수 패턴 등
→ /tmp/publish-qa-step2.md
STEP 3: edge-case-test
재사용 컴포넌트의 엣지케이스 분석 + 자동 수정
(재사용 컴포넌트가 없으면 스킵)
→ /tmp/publish-qa-step3.md
STEP 4: pnpm build
최종 빌드 확인
빌드 실패 시 전체 QA 실패QA 완료 기준:
품질 검증 완료 — commit 준비 완료
디자인 QA: 완료
리팩토링: 완료
엣지케이스: 완료 / 미해당
빌드 체크: PASS11. 핵심 설계 원칙 정리
AI는 코드 생성에만 쓴다
| 단계 | 담당 | LLM 사용 |
|---|---|---|
| PHASE 0 (분석) | Python | X |
| PHASE 1 (자산) | Python | X |
| PHASE 1.5 (분할) | Python | X |
| PHASE 2 (코드) | Claude -p | O |
| PHASE 3 (검사) | pnpm | X |
| QA | Claude -p | O |
색상 매핑, 아이콘 분류, 이미지 다운로드처럼 규칙이 명확한 작업은 Python이 합니다. AI는 규칙이 명확하지 않은 **"코드 설계와 구현"**에만 투입됩니다.
추측 코딩을 구조적으로 차단한다
AI의 가장 큰 위험은 추측입니다.
- 색상:
#1A1A2E가 어떤 토큰인지 추측 → 역색인으로 차단 - 아이콘: 이름을 추측 →
data-name속성 직접 확인 강제 - 이미지: 경로를 하드코딩 →
MY_IMAGES객체만 허용 - 스펙: 서브레이어를 스킵 →
get_design_context개별 호출 강제
단계 간 통신은 파일로 한다
Python 스크립트 → /tmp/figma-analysis.json → Python 스크립트
→ claude -p 서브프로세스각 단계는 독립적인 프로세스입니다. 메모리를 공유하지 않습니다. /tmp 파일이 이들을 연결하는 유일한 통신 수단입니다. 이렇게 하면 어떤 단계가 실패해도 이전 단계의 결과가 남아 있어 디버깅이 쉽습니다.
파이프라인이 깨지면 수동 대체 없이 파이프라인을 고친다
- X:
figma-icons스킬 실패 → 수동으로 SVG 다운로드 - O:
figma-icons스킬 실패 → 스킬 자체를 수정
수동 대체는 한 번이면 괜찮지만, 반복되면 기술 부채가 됩니다. 파이프라인의 신뢰성을 유지하는 것이 더 중요합니다.
마치며
이 파이프라인의 핵심은 "AI가 잘하는 것"과 "Python이 잘하는 것"을 분리한 것이다.
- Python: 반복적이고 규칙 기반인 작업 (빠르고, 비용 없음)
- Claude: 창의적 판단이 필요한 코드 설계 및 구현 (느리지만 정확)
그리고 단계 간 통신을 /tmp 파일로 명시적으로 관리해서, 어디서 문제가 생겼는지 항상 추적할 수 있게 했다.
현재 외주 프로젝트에서 어드민 대시보드 페이지들을 이 파이프라인으로 찍어내고 있다. 페이지 하나당 수작업으로 반나절 걸리던 퍼블리싱이, Figma URL 입력 후 타입 검사 통과까지 대부분 자동으로 진행된다. 물론 QA 단계에서 수동 확인이 필요한 부분은 있지만, 반복 노동의 대부분은 사라졌다.
Figma URL 하나를 입력하면 타입 검사까지 통과한 코드가 나오는 것, 그게 이 시스템의 목표이고, 실제로 그렇게 돌아가고 있다.