Claude Code 스킬로 Figma-to-Code 퍼블리싱 파이프라인 만들기
개요
Figma 디자인 시안을 Next.js 코드로 변환하는 작업을 Claude Code 스킬 시스템으로 자동화한 경험을 정리한다. 단순한 단일 스킬이 아니라, 오케스트레이터 → 실행 스킬 → 서브에이전트 구조로 설계하여 대형 디자인도 컨텍스트 오버플로우 없이 처리할 수 있도록 했다.
왜 스킬을 만들었나
Figma → 코드 퍼블리싱은 반복적이면서도 실수가 잦은 작업이다:
- 토큰 매핑을 추측하면 100% 틀린다 (grey.6 vs grey.7)
- 아이콘을 수동 다운로드하면 currentColor 치환을 빼먹는다
- 대형 시안을 한 번에 처리하면 컨텍스트가 넘쳐서 뒤쪽 섹션을 추측한다
- QA를 빼먹으면 strokeDashes(dashed/solid) 같은 세부사항이 누락된다
이런 실수를 구조적으로 방지하려면, 절차를 스킬로 코드화하고 강제하는 게 맞다.
스킬 아키텍처
3계층 구조
┌─────────────────────────────────────────────┐
│ 오케스트레이터 (사용자가 직접 호출) │
│ figma-to-code-tailwind, publish-qa 등 │
│ → 다른 스킬을 순차 호출, /tmp 캐싱 관리 │
├─────────────────────────────────────────────┤
│ 실행 스킬 (오케스트레이터가 호출) │
│ figma-fetch, figma-publish-tailwind 등 │
│ → 실제 작업 수행 (API 호출, 코드 생성) │
├─────────────────────────────────────────────┤
│ 초기 세팅 스킬 (프로젝트 시작 시 1회) │
│ token-to-tailwind, token-to-chakra │
│ → 토큰 변환, 독립 실행 │
└─────────────────────────────────────────────┘전체 스킬 맵 (16개)
| 분류 | 스킬 | 역할 |
|---|---|---|
| 오케스트레이터 | figma-to-code-tailwind | 단일 시안 → Tailwind 퍼블리싱 |
figma-to-code-chakra | 단일 시안 → ChakraUI 퍼블리싱 | |
responsive-publish-tailwind | 반응형 → Tailwind 퍼블리싱 | |
responsive-publish-chakra | 반응형 → ChakraUI 퍼블리싱 | |
publish-qa | 품질 검증 (commit 전) | |
| 실행: 분석 | figma-fetch | Figma REST API 노드 분석 + 스펙 구조화 |
| 실행: 자산 | figma-icons | 아이콘 SVG 추출 |
figma-images | 이미지 추출 (1x/2x) | |
| 실행: 구현 | figma-publish-tailwind | Tailwind + shadcn/ui 코드 구현 |
figma-publish-chakra | ChakraUI 코드 구현 | |
| 실행: QA | design-qa | Figma ↔ 코드 정밀 대조 |
publish-refactor | 프로젝트 컨벤션 리팩토링 | |
edge-case-test | 훅/유틸 엣지케이스 검증 | |
a11y-seo | 접근성 + SEO 검사 | |
| 초기 세팅 | token-to-tailwind | token.json → Tailwind config |
token-to-chakra | token.json → Chakra 테마 |
핵심 설계 결정
1. 서브에이전트 위임으로 컨텍스트 오버플로우 방지
문제: 대형 Figma 시안(섹션 8개+)을 메인 컨텍스트에서 처리하면, 뒤쪽 섹션에서 앞쪽 스펙을 "까먹고" 추측하기 시작한다.
해결: 오케스트레이터는 직접 구현하지 않고, 서브에이전트에 위임한다. 각 에이전트는 독립 컨텍스트를 가지므로 오버플로우가 발생하지 않는다.
오케스트레이터 (메인 컨텍스트)
├── PHASE 0: figma-fetch → /tmp/figma-spec-cache.md 저장
├── PHASE 1: figma-icons, figma-images
├── PHASE 1.5: 섹션 분할 → /tmp/figma-spec-section-*.md
└── PHASE 2: 섹션별 서브에이전트 spawn (독립 컨텍스트)
├── Agent 1: Header 섹션 (자기 스펙만 로드)
├── Agent 2: Content 섹션
└── Agent 3: Footer 섹션각 에이전트의 컨텍스트 배분:
[공유 컨텍스트 ~15%] + [섹션 스펙 ~15%] + [스킬 규칙 ~20%] + [구현 ~50%]
= 충분한 여유2. /tmp 캐싱으로 API 재호출 방지
Figma REST API 호출은 비용이 크다. PHASE 간 데이터를 /tmp에 저장하여 재사용한다.
# PHASE 0에서 저장
/tmp/figma-spec-cache.md # 전체 디자인 스펙
# PHASE 1.5에서 분할
/tmp/figma-spec-shared.md # 공유 컨텍스트 (토큰, 자산, 구조)
/tmp/figma-spec-section-1-header.md
/tmp/figma-spec-section-2-content.md
/tmp/figma-spec-section-3-footer.md
# publish-qa에서 저장
/tmp/publish-qa-step1.md # 디자인 QA 결과
/tmp/publish-qa-step2.md # 리팩토링 결과
/tmp/publish-qa-step3.md # 엣지케이스 결과PRE/POST에서 캐시를 정리하여 이전 세션 오염을 방지한다:
# PRE: 작업 시작 전
rm -f /tmp/figma-spec-cache.md /tmp/figma-spec-section-*.md
# POST: 작업 완료 후
rm -f /tmp/figma-spec-cache.md /tmp/figma-spec-section-*.md3. 섹션 분할 전략 (대형 디자인 대응)
| 디자인 규모 | 루트 직계 자식 수 | 전략 |
|---|---|---|
| 소형 | 1~3개 | 단일 에이전트 |
| 중형 | 4~7개 | 2~3개 그룹 분할 |
| 대형 | 8개+ | 섹션별 개별 에이전트 |
병렬 spawn은 파일 충돌 위험이 있으므로, 섹션 에이전트는 순차 실행한다.
4. publish-qa도 서브에이전트 기반
처음에는 publish-qa가 메인 컨텍스트에서 4개 STEP을 모두 실행했다. 하지만 design-qa(Figma API + 토큰 역색인 + 대조 테이블)와 publish-refactor(12개 검사 항목 + 반복 수정)가 각각 무거워서 컨텍스트가 부족했다.
Before: 메인에서 모두 실행 → 뒤쪽 STEP에서 품질 저하
After: STEP별 독립 서브에이전트 + /tmp 리포트 캐싱
publish-qa (오케스트레이터)
├── PRE: /tmp/publish-qa-step*.md 캐시 정리
├── STEP 1: 서브에이전트 → /design-qa → /tmp/publish-qa-step1.md
├── STEP 2: 서브에이전트 → /publish-refactor → /tmp/publish-qa-step2.md
├── STEP 3: 서브에이전트 → /edge-case-test → /tmp/publish-qa-step3.md (조건부)
├── STEP 4: 메인 → pnpm build (가벼움)
└── POST: 캐시 정리 + 최종 보고STEP 4(빌드 체크)만 메인에서 실행하는 이유: pnpm build는 가벼운 명령이고, 에러 발생 시 즉시 수정해야 하므로 메인 컨텍스트가 적합하다.
오케스트레이터 워크플로우 상세
figma-to-code-tailwind (단일 시안 퍼블리싱)
PRE: /tmp 캐시 정리
PHASE 0: /figma-fetch → 노드 분석 + 스펙 → /tmp/figma-spec-cache.md
PHASE 1: /figma-icons, /figma-images → 자산 처리 (조건부)
PHASE 1.5: 섹션 분할 판단
PHASE 2: 서브에이전트 → /figma-publish-tailwind
PHASE 3: 통합 검증 (페이지 조립 + type-check)
POST: 캐시 정리ChakraUI 버전(figma-to-code-chakra)에는 **PHASE 0.5(MCP 힌트 검증)**이 추가로 있다. MCP get_design_context가 반환하는 Tailwind 근사값을 REST API로 교차 검증하는 단계다.
responsive-publish-tailwind (반응형 퍼블리싱)
단일 서브에이전트에 전체 위임:
STEP 0: URL 파싱 + 프로젝트 구조 확인
STEP 1: 브레이크포인트별 get_design_context 개별 조회
STEP 2: 섹션별 브레이크포인트 차이 비교 테이블
STEP 3: 토큰 역색인 빌드
STEP 4: 자산 처리
STEP 5: 반응형 구현 설계서
STEP 6: Tailwind 반응형 코드 구현 (md:, lg: 접두사)
STEP 7: pnpm type-check입력 형식:
/responsive-publish-tailwind <설명> URL([base, md|null, lg|null]) --page src/app/path핵심: 각 브레이크포인트 URL을 개별 조회한다. 하나만 조회하고 나머지를 추측하면 반응형 차이점을 놓친다.
토큰 매핑: 추측을 구조적으로 차단
퍼블리싱에서 가장 흔한 실수는 토큰 추측이다.
❌ "grey.6 정도 되겠지" → 실제는 grey.7
❌ "pre-body-3이겠지" → 실제는 pre-heading-5 (16px/700은 heading)
❌ "border-grey-3이겠지" → 실제는 border.basic.2 (같은 hex라도 컨텍스트가 다름)역색인 빌드 (추측 차단의 핵심)
token.json에서 3가지 역색인을 빌드하면, Figma 노드 데이터에서 정확한 토큰 이름을 자동 resolve할 수 있다.
// 1. VariableID → 시맨틱 토큰 (색상)
semanticById["VariableID:4021:1253"] = { token: "border-basic-2", value: "#cfd2d5" }
// 2. VariableID → 원시 색상
rawById["VariableID:100:50"] = { name: "grey-50", value: "#f9f9fa" }
// 3. fontSize/fontWeight → textStyle 토큰
textStyleIndex["14/600"] = "pre-body-5"
textStyleIndex["24/700"] = "pre-heading-2"매핑 우선순위:
- boundVariables VariableID → 역색인 (정확도 100%)
- hex + CSS 컨텍스트 폴백 (같은 hex라도 stroke vs fill로 다른 토큰)
- 프로젝트 토큰 파일 역방향 조회 (최후 수단)
초기 세팅: token-to-tailwind / token-to-chakra
이 스킬들은 오케스트레이터 플로우에 포함되지 않는 독립 실행 스킬이다. 프로젝트 초기에 한 번 실행하면 토큰이 세팅되고, 이후 퍼블리싱 스킬들은 세팅된 토큰을 참조만 한다.
Tailwind 변환 흐름
token.json → CSS Variables (globals.css) + tailwind.config.ts 확장/* globals.css */
:root {
--color-grey-50: #f9f9fa;
--color-accent-yellow1: #fef2e3;
}
.dark {
--color-accent-yellow1: #322304;
}// tailwind.config.ts
colors: {
grey: { 50: 'var(--color-grey-50)' },
accent: { yellow1: 'var(--color-accent-yellow1)' },
}Chakra 변환 흐름
token.json → Chakra theme 객체 (colors, semanticTokens, textStyles)v2와 v3의 차이:
v3 (createSystem) | v2 (extendTheme) | |
|---|---|---|
| 색상 | { value: '#hex' } 래퍼 | '#hex' 직접 |
| 시맨틱 | { _light, _dark } | { default, _dark } |
| textStyle | { value: { ... } } 래퍼 | 래퍼 없이 직접 |
.claude/skills/ vs .claude/agents/ 차이
스킬을 정리하면서 skills/와 agents/로 분리하려 했으나, Claude Code에서 이 둘은 동작이 다르다:
.claude/skills/→/slash-command로 호출 가능.claude/agents/→Task({ subagent_type: "name" })으로만 호출 가능
현재 오케스트레이터들이 서브에이전트 안에서 /figma-publish-tailwind 등을 슬래시 커맨드로 호출하고 있어서, 실행 스킬을 agents/로 옮기면 호출이 깨진다. 따라서 모두 skills/에 유지하고, README에서 카테고리를 분류하는 방식을 택했다.
프로젝트 간 이식
다른 프로젝트에 스킬을 복사할 때 수정해야 하는 부분:
| 항목 | 현재 값 | 수정 대상 |
|---|---|---|
| token.json 경로 | apps/admin/public/token.json | 프로젝트 실제 경로 |
| FIGMA_TOKEN 위치 | apps/admin/.env.local | 프로젝트 실제 경로 |
| 컴포넌트 디렉토리 | apps/admin/src/components/ | 프로젝트 실제 경로 |
| 아이콘 생성 명령 | pnpm tokript gen:icon | 프로젝트 실제 명령 |
| type-check 명령 | pnpm type-check | 프로젝트 실제 명령 |
UI 프레임워크에 따라 사용하는 스킬 세트가 다르다:
| 스택 | 퍼블리싱 | 반응형 | 토큰 세팅 |
|---|---|---|---|
| Tailwind + shadcn/ui | figma-to-code-tailwind | responsive-publish-tailwind | token-to-tailwind |
| ChakraUI | figma-to-code-chakra | responsive-publish-chakra | token-to-chakra |
공통 스킬(figma-fetch, figma-icons, figma-images, publish-qa, a11y-seo)은 프레임워크 무관하게 사용 가능하다.
배운 것
-
컨텍스트 윈도우는 유한하다. 무거운 작업을 메인에서 전부 돌리면 뒤쪽에서 품질이 떨어진다. 서브에이전트 위임이 답이다.
-
추측은 구조적으로 차단해야 한다. "토큰을 추측하지 마라"는 규칙만으로는 부족하다. 역색인 빌드를 강제하고, 매핑 근거를 테이블에 기재하게 해야 실제로 추측이 사라진다.
-
캐싱은 PHASE 간 다리다. 서브에이전트는 메인 컨텍스트를 공유하지 않으므로,
/tmp파일이 유일한 데이터 전달 수단이다. PRE/POST 정리까지 포함해야 오염을 방지한다. -
스킬 분리의 기준은 "컨텍스트 무게"다. 가벼운 작업(pnpm build)은 메인에서, 무거운 작업(디자인 QA, 리팩토링)은 서브에이전트에서 실행한다.
-
skills/ vs agents/ 구분을 이해해야 한다. Claude Code에서 이 둘은 호출 방식이 다르다. 오케스트레이터가 슬래시 커맨드로 실행 스킬을 호출하는 구조라면, 실행 스킬도
skills/에 있어야 한다.