Files
C.E.L_Slide_test2/FIGMA-EXTRACTION.md
kyeongmin 05703c8e72 WIP: hero-icon-cards_1 블록 + 오답노트 + figma 관련 파일
- hero-icon-cards_1.html: hero-icon-cards 변형 (icon → 소제목+불릿 계층)
- compare-detail-gradient.html: 하단 2열 비교 블록 (Figma Frame 4 기반)
- 오답노트.md: 절대 하지 말아야 하는 실수 목록
- figma_to_html.py: Figma→HTML 변환 스크립트
- static/figma-assets/: Figma export 이미지 (배지, 화살표)
- 주의: compare-detail-gradient CSS 폰트 크기가 임의 수정됨 — 원본 복원 필요

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:14:09 +09:00

14 KiB
Raw Blame History

Figma → HTML 블록 변환 프로세스

2026-04-07 확립. Figma 디자인을 design_agent 블록으로 변환하는 정확한 방법론.


1. 전체 워크플로우

[Step 1] Figma API로 파일 구조 추출
    ↓
[Step 2] 프레임별 렌더링 이미지(PNG) 다운로드
    ↓
[Step 3] 노드별 상세 데이터 추출 (좌표, 색상, 폰트, 크기)
    ↓
[Step 4] 디자인 언어 분석 (공통 패턴 vs 콘텐츠 전용 구분)
    ↓
[Step 5] 블록 설계 (슬롯, 동적 규칙, schema)
    ↓
[Step 6] 수학적 계산 (Figma 좌표 → 스케일 → CSS값)
    ↓
[Step 7] HTML/CSS 구현
    ↓
[Step 8] 비교 리뷰 (Figma PNG vs HTML, 같은 폭으로 위/아래 배치)
    ↓
[Step 9] 피드백 반영 → Step 6~8 반복
    ↓
[Step 10] Jinja2 템플릿화 + catalog.yaml 등록

2. Figma API 사용법

2.1 파일 구조 가져오기

curl -s -H "X-Figma-Token: {TOKEN}" \
  "https://api.figma.com/v1/files/{FILE_KEY}" \
  | python -m json.tool

2.2 특정 노드 상세 데이터

curl -s -H "X-Figma-Token: {TOKEN}" \
  "https://api.figma.com/v1/files/{FILE_KEY}/nodes?ids={NODE_IDS}&geometry=paths"

2.3 노드 이미지 렌더링 (PNG)

curl -s -H "X-Figma-Token: {TOKEN}" \
  "https://api.figma.com/v1/images/{FILE_KEY}?ids={NODE_IDS}&format=png&scale=2"
  • scale=2: 2배 해상도로 다운로드 (선명도 확보)
  • 응답의 images 객체에 각 노드 ID별 S3 URL 제공

2.4 추출해야 하는 핵심 데이터

데이터 API 필드 용도
위치 absoluteBoundingBox.x, .y 요소 간 관계 계산
크기 absoluteBoundingBox.width, .height 스케일 계산
텍스트 characters 콘텐츠 확인
폰트 style.fontFamily, .fontSize, .fontWeight 타이포그래피
색상 fills[].color 색상 팔레트
테두리 strokes[], strokeWeight 박스 스타일
라운드 cornerRadius border-radius
이미지 fills[].imageRef 이미지 자산 식별

3. 수학적 계산 (핵심)

3.1 스케일 팩터

슬라이드 콘텐츠 폭 = 1280px - padding(40px × 2) = 1200px

scale = 1200 / figma_frame_width
Figma 프레임 스케일
Frame 2 1808px 0.6637
Frame 3 1807px 0.6641
Frame 4 3848px 0.3118

3.2 요소 간 정렬 계산

절대 원칙: Figma 좌표 차이값 → 스케일 적용 → CSS값

# 예: 리본 접힘선과 박스 테두리 정렬
badge_y = 1431      # Figma에서 badge 이미지 top Y
box_y = 1449        # Figma에서 box top Y
fold_offset = box_y - badge_y  # = 18px (Figma 기준)

# 스케일 적용
fold_offset_css = round(fold_offset * scale)  # = 12px (CSS)

금지: "좀 더 올려볼게요" 식의 시행착오 px 조정

3.3 이미지 자산 크기 계산

# Figma 원본 크기에 스케일 적용
ribbon_width_css = round(badge_img_width * scale)
ribbon_height_css = round(badge_img_height * scale)

# 비율 계산 (CSS에서 width만 지정하면 height는 자동)
aspect_ratio = badge_img_width / badge_img_height

3.4 패딩/여백 계산

# 리본이 박스 안에 들어오는 높이 = 리본 전체 높이 - 접힘선 오프셋
ribbon_inside_box = ribbon_height_css - fold_offset_css

# 박스 상단 패딩 = 리본 침입 높이 + 여유
box_padding_top = ribbon_inside_box + 6  # 6px 여유

3.5 실제 계산 예시 (Frame 2)

입력 (Figma 원본):
  badge 이미지: 508×94px, y=1431
  box: y=1449
  frame width: 1808px

계산:
  scale = 1200/1808 = 0.6637
  ribbon_w = 508 × 0.6637 = 337px
  ribbon_h = 94 × 0.6637 = 62px
  fold_offset = (1449-1431) × 0.6637 = 12px
  ribbon_below_fold = 62 - 12 = 50px
  box_padding_top = 50 + 6 = 56px

CSS 출력:
  .ribbon { width: 337px; top: -12px; }
  .box { padding-top: 56px; }

4. 이미지 자산 처리

4.1 CSS로 만들면 안 되는 것

요소 이유 처리
3D 리본/두루마리 입체감, 그림자, 곡면 → CSS 불가 Figma에서 PNG 추출
복잡한 그라디언트 배경 다중 정지점, 비선형 → CSS 근사 불가 이미지 사용
아이콘 이미지 디자이너가 만든 고유 자산 원본 이미지 사용

4.2 CSS로 만들 수 있는 것

요소 CSS 구현
단색/2색 그라디언트 배경 linear-gradient()
둥근 모서리 테두리 박스 border + border-radius
텍스트 스타일 font-size, font-weight, color
그리드/플렉스 레이아웃 display: grid / flex
구분선 border or background

4.3 이미지 추출 및 저장

# Figma API로 특정 노드 이미지 추출
curl -s -H "X-Figma-Token: {TOKEN}" \
  "https://api.figma.com/v1/images/{FILE_KEY}?ids={NODE_ID}&format=png&scale=2"

# 다운로드 → static/figma-assets/ 에 저장
curl -s -o static/figma-assets/{name}.png "{S3_URL}"

저장 위치: static/figma-assets/


5. 비교 리뷰 페이지 작성법

5.1 레이아웃

같은 폭으로 위/아래 배치 (좌/우 아님 — 크기 차이 문제)

┌─ 빨간 테두리 ──────────────┐
│  Figma Original (PNG)       │
└─────────────────────────────┘
         ─ 구분선 ─
┌─ 초록 테두리 ──────────────┐
│  HTML Block                 │
└─────────────────────────────┘

5.2 HTML 스케일링

.html-inner {
  width: 1280px;                    /* 슬라이드 원본 크기 */
  transform-origin: top left;
  transform: scale(0.74);          /* 960px 컨테이너에 맞춤: 960/1280 */
}

5.3 비교 리뷰 파일 위치

data/figma_ref/comparison.html


6. Jinja2 템플릿 변환 규칙

6.1 고정값 → 변수

<!-- Figma 원본의 텍스트 → Jinja2 변수 -->
<span>정책 달성</span><span>{{ badge_title }}</span>
<span>Engn. Solution</span><span>{{ left_title }}</span>

6.2 반복 요소 → 루프

<!-- N개 카드 → for loop -->
{% for card in cards %}
<div class="card">{{ card.title }}</div>
{% endfor %}

6.3 이미지 자산 → 슬롯

<!-- 리본 이미지: 색상에 따라 다른 자산 사용 가능 -->
<img src="{{ ribbon_image | default('figma-assets/badge_solution.png') }}">

6.4 계산된 CSS → CSS 변수

<!-- 수학적 계산 결과를 CSS 변수로 -->
<div style="--ribbon-width: {{ ribbon_width }}px; --fold-offset: {{ fold_offset }}px;">

7. 디자인 언어 vs 콘텐츠 전용 구분

디자인 언어 (블록에 포함, 재사용 가능)

  • 색상 팔레트 (warm 테마: 브라운, 틸, 베이지)
  • 타이포그래피 위계 (크기, 굵기 단계)
  • 레이아웃 구조 (2열 비교, N열 카드 등)
  • 장식 요소 (3D 리본, 둥근 컨테이너)

콘텐츠 전용 (블록에 포함하지 않음)

  • 특정 텍스트 ("디지털전환은 사용자...")
  • 특정 아이콘 이미지 (brain, thunder 등)
  • 도메인 전문 용어 (DfMA, Engn. Solution)

8. 파일 구조

design_agent/
├── static/figma-assets/       ← Figma에서 추출한 이미지 자산
│   ├── badge_policy.png       (틸 3D 리본)
│   ├── badge_solution.png     (빨간 3D 리본)
│   ├── box_policy_container.png
│   └── box_solution_cards.png
├── data/figma_ref/            ← 비교 리뷰용
│   ├── comparison.html        (Figma vs HTML 비교 페이지)
│   ├── frame2_1-5.png         (Figma 원본 PNG)
│   ├── frame3_1-35.png
│   └── frame4_1-49.png
├── templates/blocks/cards/    ← 블록 템플릿
│   ├── hero-icon-cards.html
│   ├── compare-2col-badge.html
│   └── compare-detail-gradient.html
├── FIGMA-DESIGN-LANGUAGE.md   ← 디자인 언어 분석 결과
├── FIGMA-EXTRACTION.md        ← 이 문서
└── PHASE-FIGMA-BLOCKS.md      ← 블록 설계 명세

9. 고급 레이아웃 패턴

9.1 좌/우 열 섹션 Y선 정렬 (CSS Grid 행 공유)

2열 비교에서 좌/우 섹션 제목이 같은 Y선에 있어야 할 때:

문제: 각 열을 독립 flex-column으로 만들면, 좌측 섹션 본문이 길면 우측 다음 섹션이 밀림.

flex-column (잘못):
  좌: [제목1] [긴본문] [제목2]
  우: [제목1] [짧은본문] [제목2]   ← 제목2가 좌측과 Y가 다름

해결: CSS Grid 2열 × N행으로 행을 공유하면 자동 정렬.

.block {
  display: grid;
  grid-template-columns: 1fr 1fr;  /* 2열 */
  grid-template-rows: auto auto auto auto;  /* 헤더 + N행 */
}
Grid (올바름):
  [좌 헤더]  [우 헤더]      ← Row 0
  [좌 섹션1] [우 섹션1]    ← Row 1 (행 높이 = max(좌,우))
  [좌 섹션2] [우 섹션2]    ← Row 2 (Y선 자동 정렬!)

실제 계산 (Frame 4):

Figma Y좌표:
  Row 1: 좌 1166, 우 1166 → 0px 차이 (이미 정렬)
  Row 2: 좌 1529, 우 1467 → 62px 차이 (Grid가 해결)
  Row 3: 좌 1845, 우 1845 → 0px 차이 (이미 정렬)
원인: Row 1 좌측에 As-Is→To-Be 구조가 있어서 본문이 62px 더 높음

9.2 As-Is → To-Be 수평 서브 레이아웃

한 섹션 안에서 변환 전/후를 수평 배치할 때:

<div class="asis-tobe">
  <div class="asis">
    <div class="bullet">이전 상태 1</div>
    <div class="bullet">이전 상태 2</div>
  </div>
  <img src="arrow.png" class="arrow" alt="→">
  <div class="tobe">
    <div class="bullet">변환 후 1</div>
    <div class="bullet">변환 후 2</div>
  </div>
</div>
.asis-tobe { display: flex; align-items: center; gap: 8px; }
.asis, .tobe { flex: 1; }
.arrow { width: 60px; height: auto; flex-shrink: 0; }

Figma 좌표로 검증:

As-Is: x=2737, w=539
Arrow: x=3375, w=252
To-Be: x=3687, w=672
→ 세 요소가 같은 Y(1269)에 수평 배치됨을 좌표로 확인

9.3 3D 리본/두루마리 배지 정렬 공식

리본 이미지의 접힘선(fold-back)이 박스 테두리와 정확히 일치해야 할 때:

┌── 리본 이미지 ──────────────┐
│  접힘 삼각형 (fold)          │  ← fold_offset (이미지 top에서)
│  리본 본체                   │
│                              │
└──────────────────────────────┘
════════════════════════════════  ← 박스 top border (여기에 fold가 일치해야 함)
┌── 박스 ──────────────────────┐
│  padding-top = ribbon_below  │
│  콘텐츠 시작                  │

계산:
  fold_offset = (box_y - badge_y) × scale    → CSS: top 값
  ribbon_below = ribbon_height - fold_offset  → 박스 안 침입 높이
  box_padding_top = ribbon_below + 여유(6px)  → 콘텐츠 겹침 방지

핵심: 리본을 올리거나 내리는 게 아니라, 박스의 위치를 계산하는 것.

  • top: -fold_offset → 리본 접힘선 = 박스 top border
  • 리본은 그대로, 박스와의 관계만 수학적으로 결정

10. 실수 방지 (Anti-patterns)

10.1 절대 하면 안 되는 것

Anti-pattern 왜 안 되는지 올바른 방법
px 시행착오 조정 ("좀 더 올려볼게") 3번 이상 실패, 시간 낭비 Figma 좌표에서 수학적 계산
3D 효과를 CSS로 재현 평면적이라 품질 차이 심각 Figma에서 PNG 추출
비교 리뷰를 좌/우 배치 크기 차이로 비교 불가 위/아래 같은 폭으로 배치
Jinja2 템플릿을 브라우저에서 직접 열기 변수 미렌더, 이미지 경로 깨짐 comparison.html 또는 FastAPI로 확인
독립 flex-column으로 2열 비교 행 정렬 안 됨 CSS Grid 행 공유
느낌으로 폰트/색상 설정 Figma와 다른 결과물 Figma API에서 정확한 값 추출

10.2 반드시 해야 하는 것

원칙 이유
CSS 주석에 계산 근거 기록 나중에 왜 이 값인지 추적 가능
비교 리뷰 후 진행 디자인 차이를 사전에 발견
이미지 자산은 static/figma-assets/에 저장 FastAPI가 서빙, 경로 일관성
comparison.html에 모든 프레임 포함 한 페이지에서 전체 리뷰 가능
Figma 노드 ID 기록 나중에 업데이트된 디자인 재추출 가능

11. Figma 소스 정보

현재 등록된 Figma 파일

항목
File Key 9S6LsQyO6zlRxtiqZccOUM
Page Page 1 (0:1)
Frame 2 (hero-icon-cards) Node 1:5
Frame 3 (compare-2col-badge) Node 1:35
Frame 4 (compare-detail-gradient) Node 1:49
Badge (빨간 리본) Node 1:33 (image 4019)
Badge (틸 리본) Node 1:43 (image 2197)
Arrow (As-Is→To-Be) Node 1:67 (image 2645)
Box (빨간 테두리) Node 1:12 (Rectangle 42894)
Box (틸 테두리) Node 1:37 (Rectangle 42598)

12. 체크리스트

새 Figma 프레임을 블록으로 변환할 때:

  • Figma API로 노드 데이터 추출 (좌표, 크기, 색상, 폰트)
  • PNG 렌더링 다운로드 (scale=2)
  • 복잡한 비주얼 요소 식별 → 이미지로 추출 (CSS로 만들지 않음)
  • 스케일 팩터 계산 (1200 / frame_width)
  • 핵심 정렬 포인트 수학적 계산 (좌표 차이 × 스케일)
  • CSS 값 도출 (계산 근거를 주석으로 기록)
  • 비교 리뷰 페이지에 추가 (위/아래 같은 폭)
  • 사용자 피드백 확인
  • Jinja2 템플릿 변환 (고정값→변수, 반복→루프)
  • catalog.yaml 등록