본문으로 건너뛰기

Figma MCP vs REST API — 퍼블리싱 워크플로우에서 무엇을 써야 하는가

·12 min read

Figma에서 코드를 뽑아내는 방법이 두 가지가 됐다. Claude Code에서 get_design_context를 호출하는 MCP 방식과, Figma REST API를 직접 호출하는 방식. 둘 다 "Figma 데이터를 가져온다"고 하지만, 실제로 돌려보면 돌아오는 데이터의 성격이 완전히 다르다.

퍼블리싱 파이프라인을 MCP 기반에서 Python + REST API 기반으로 전환하면서 이 차이를 직접 체감했다. 이 글은 그 경험을 정리한 것이다.


MCP가 주는 것

get_design_context를 호출하면 세 가지가 온다.

참조 코드 스니펫. React + Tailwind 기준으로 컴포넌트 코드를 생성해준다. 빠르게 구조를 잡기에는 편하다. Code Connect가 연결돼 있으면 실제 코드베이스의 컴포넌트가 매핑되어 나오기도 한다.

스크린샷. 노드의 시각적 렌더링을 이미지로 반환한다. API로는 알 수 없는 "실제로 어떻게 보이는가"를 확인할 수 있다.

메타데이터. 색상, 간격, 폰트 크기 같은 속성값이 요약된 형태로 온다.

처음 이걸 썼을 때 "이제 Figma 퍼블리싱이 쉬워지겠구나" 싶었다. 코드가 거의 완성된 형태로 나오니까.


MCP의 구조적 한계

그런데 실제 프로덕션 코드를 짜면서 한계가 드러나기 시작했다. 근본 원인은 데이터가 전처리를 거치는 구조에 있다.

MCP 방식:
  Figma 원본 → MCP 서버가 전처리 (내부 로직 비공개) → React+Tailwind 힌트 → Claude가 해석 → 최종 코드
 
REST API 방식:
  Figma 원본 → raw JSON (정보 손실 없음) → Python이 직접 처리 → 최종 코드

MCP는 중간에 전처리 과정이 끼어 있어서, 원본 데이터가 "요약"되면서 정보가 빠진다. 구체적으로 어떤 게 빠지는지 하나씩 보면:

근사값 문제

MCP가 반환하는 코드는 Tailwind 클래스로 표현된 근사값이다. p-4는 16px인데, 실제 Figma 값이 12px인 경우도 p-3이 아니라 p-4로 나올 수 있다. Tailwind의 4px 그리드로 반올림되기 때문이다.

반면 REST API의 paddingTop, paddingLeft 필드는 픽셀 단위 정확한 값이다.

{
  "paddingTop": 12,
  "paddingLeft": 20,
  "paddingRight": 20,
  "paddingBottom": 12
}

디자이너가 공들여 잡은 간격이 반올림되는 건 미묘하지만 쌓이면 티가 난다.

노드별 토큰 바인딩 정보 부재

디자인 시스템에서 가장 중요한 건 색상이 어떤 시맨틱 토큰으로 연결돼 있는가다. #3B82F6을 하드코딩하면 안 되고, color.primary.500이나 var(--color-primary)로 써야 한다.

REST API 응답에는 boundVariables 필드가 있다.

{
  "fills": [{
    "type": "SOLID",
    "color": { "r": 0.231, "g": 0.51, "b": 0.965 },
    "boundVariables": {
      "color": {
        "type": "VARIABLE_ALIAS",
        "id": "VariableID:123:456"
      }
    }
  }]
}

VariableIDtoken.json의 역색인과 대조하면 "이 색상은 color.primary.blue다"라는 걸 정확히 알 수 있다. MCP에도 get_variable_defs라는 도구가 있어 변수 이름↔값 매핑은 조회 가능하지만, REST API의 boundVariables처럼 **"이 노드의 이 fill이 어떤 변수에 바인딩되어 있는가"**라는 노드별 바인딩 정보는 제공하지 않는다.

숨겨진 상태 미포함

컴포넌트에는 기본 상태(default), 호버(hover), 비활성(disabled) 등 여러 상태가 있다. Figma에서는 이것들이 visible: false 레이어나 componentProperties로 표현된다.

MCP의 get_design_context는 현재 보이는 상태 하나만 반환한다. REST API로 전체 노드 트리를 받으면 모든 상태가 다 있다. 조건부 렌더링을 구현할 때 이 차이가 결정적이다.

{
  "name": "state=disabled",
  "visible": false,
  "children": []
}

잘림(truncation) 문제

복잡한 컴포넌트에서 get_design_context의 응답이 잘리는 경우가 있다. 응답 크기 제한 때문이다. 이때 어느 부분이 잘렸는지 명확하지 않아 누락된 스펙이 조용히 무시될 수 있다.

REST API는 depth 파라미터로 조회 깊이를 조절하고, 잘린 노드(children이 비어있는데 실제로는 있는 경우)를 감지해서 추가 요청으로 채울 수 있다.


REST API가 주는 것

정리하면 REST API는 원시 데이터를 준다.

  • 모든 레이어 (visible: false 포함)
  • 픽셀 단위 정확한 수치
  • boundVariables로 토큰 연결 정보
  • componentProperties로 모든 UI 상태
  • 재귀적으로 깊은 노드까지 순회 가능

figma_analyze.py에서 구현한 핵심 로직도 이 위에 있다. boundVariables로 색상을 토큰으로 역추적하고, visible: false 레이어를 필터링하거나 조건부 렌더링 힌트로 변환하고, 잘린 노드를 감지해서 배치 요청으로 채운다.

def resolve_color(fills, token_index):
    """fills 배열에서 색상을 토큰으로 역추적"""
    for fill in fills:
        if fill.get('type') != 'SOLID':
            continue
        bound = fill.get('boundVariables', {}).get('color', {})
        if bound.get('type') == 'VARIABLE_ALIAS':
            var_id = bound['id']
            token_name = token_index['semanticById'].get(var_id) \
                      or token_index['rawById'].get(var_id)
            if token_name:
                return token_name  # "color.primary.blue"
        # 토큰 없으면 hex 반환
        r, g, b = fill['color']['r'], fill['color']['g'], fill['color']['b']
        return f"#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}"

이런 로직을 MCP 위에서 구현하는 건 불가능하다. MCP가 이 원시 데이터를 주지 않기 때문이다.


그럼 MCP는 언제 쓰는가

MCP가 쓸모없다는 게 아니다. 용도가 다르다.

빠른 시각적 참조. "이 컴포넌트 대략 어떻게 생겼지?" 확인할 때 스크린샷과 함께 오는 MCP 응답은 빠르다.

Code Connect가 잘 구성된 경우. Figma 컴포넌트와 코드베이스 컴포넌트가 정확히 1:1로 매핑돼 있으면, MCP가 <Button variant="primary" /> 같은 실제 코드를 바로 내려준다. 이 경우엔 REST API를 굳이 쓸 필요가 없다.

개략적인 레이아웃 파악. 전체 페이지의 구조를 빠르게 이해할 때 MCP의 구조화된 응답이 편하다.

결국 "정확한 값이 필요한가" 가 분기점이다. 디자인 QA, 토큰 매핑, 조건부 UI 구현처럼 정확도가 중요한 작업은 REST API. 빠른 탐색이나 참조는 MCP.


두 가지를 조합하는 패턴

실제 파이프라인에서는 이렇게 쓴다.

  1. REST API로 전체 스펙 수집 (figma_analyze.py): 토큰 매핑, 레이아웃 수치, 자산 목록, 조건부 UI 목록을 완전하게 구성
  2. MCP로 시각적 확인: 생성된 코드가 실제 시안과 맞는지 스크린샷으로 대조 (/design-qa)

REST API로 코드를 짜고, MCP로 눈으로 확인한다. 서로의 약점을 보완하는 구조다.

figma_analyze.py (REST API)
/tmp/figma-analysis.json  ← 정확한 수치, 토큰 정보
/tmp/figma-spec-cache.md  ← 스펙 요약
 
claude -p /figma-publish-tailwind (LLM + MCP)
  → 스펙 파일 기반 코드 생성
  → get_design_context로 특정 노드 시각 확인 (필요시)
 
/design-qa (MCP 스크린샷 비교)
  → 생성된 코드 vs Figma 스크린샷 대조

토큰 비용 비교

두 방식의 비용 차이도 크다. 아래는 실제 사용 중 체감한 대략적인 추정치다 (노드 복잡도에 따라 편차가 크다).

MCP 방식 (per 노드, 추정):
  get_design_context 호출       → ~1K (tool call)
  전처리된 결과 수신             → ~5~15K (코드 힌트 + 메타)
  합계: ~6~16K tokens
 
REST API + Claude 직접 해석 (per 노드, 추정):
  curl API 호출 + JSON 수신     → ~30~80K (raw 노드 데이터)
  Claude가 JSON 파싱            → ~5K (추론)
  합계: ~35~85K tokens
 
REST API + Python 전처리 (per 노드, 추정):
  Python이 API 호출 + 파싱      → 0 tokens (Claude 밖에서 처리)
  구조화된 스펙만 Claude에 전달  → ~3~8K tokens
  합계: ~3~8K tokens

MCP가 토큰 효율적으로 보이지만, Python 전처리 파이프라인을 쓰면 REST API 방식이 오히려 더 적게 소모한다. 정확도와 토큰 효율 모두 잡는 셈이다.


트레이드오프 요약

MCP (get_design_context)REST API
수치 정확도근사값 (Tailwind 그리드 반올림)픽셀 단위 정확
토큰 역추적변수 정의 조회 가능 (get_variable_defs), 노드별 바인딩은 미제공boundVariables로 노드별 바인딩
숨겨진 레이어미포함전체 포함
조건부 UI 상태현재 상태만모든 상태
스크린샷있음없음
Code Connect지원별도 구현
설정 필요 없음바로 사용FIGMA_TOKEN 필요
응답 잘림 가능성있음제어 가능
토큰 비용응답 크기만큼 소모0 (Python으로 처리)

권장: 프로덕션 퍼블리싱 파이프라인에서 정확한 스펙 수집은 REST API, 시각적 확인과 코드 생성 보조는 MCP. Code Connect가 잘 구성된 디자인 시스템이라면 MCP 비중을 높여도 된다.