Figma → 랜딩 페이지
Figma 디자인에서 배포 가능한 정적 활동 랜딩 페이지를 생성합니다: index.html + css/main.css + js/{tool,pm_main,main}.js + images/*.webp.
호출: /figma-landing-page <Figma 링크> [활동명] [출력 디렉토리]
고정 계약 (어떤 경우에도 이탈 금지):
- 템플릿 플레이스홀더는 정확히 3개:
__PAGE_TITLE__,__FUNCTION_NAME__(모두 main.js에 있으며, title은 index.html에도 추가로 존재),__PC_BASE__(tool.js에 있음; 375 = H5 단일 프레임; 1920 = H5+PC 이중 프레임). - CTA 요소는 반드시 추가로
.join-btn(고정 JS 셀렉터 계약)을 달아야 하며, BEM 클래스와 공존해야 함. - 중단점 576px은 세 군데에서 일관되게 사용: JS rem 분기, CSS
@media (min-width: 576px), 플랫폼 판단. - px 정규화: H5
px × 375 ÷ H5프레임너비 ÷ 100 = rem; PCpx × 1920 ÷ PC프레임너비 ÷ 100 = rem. - 템플릿 JS 세 개 (
tool.jsrem 적응 /pm_main.jspostMessage 브릿지 /main.js활동 비즈니스 레이어, 로드 순서 동일)의 고정 부분은 한 글자도 수정 금지; 페이지 상호작용은main.js의// === Page-specific interactions ===아래에만 삽입 (IIFE 내부,$p/pm/track/Date.prototype.format직접 사용 가능). - 상세 규칙(이미지 컷아웃 경계, 클래스명, 상호작용 추론 테이블)은
references/patterns.md참조, 납품 기준은references/checklist.md참조 — 생성 전에 이 두 파일을 먼저 읽을 것.
단계 1 — 입력 파싱 및 환경 점검
- 링크에서 file key와 node-id 추출. node-id 내 하이픈을 콜론으로 변환 (
123-456→123:456) 후 Figma API 호출; API 호출 시 404/빈 결과가 나오면 이 단계를 먼저 확인. - 세 가지 환경 점검 (통과하지 못하면 먼저 수정 후 계속):
- Figma MCP 연결됨 (연결되지 않았다면 사용자 인증 유도, 인증 관련 상호작용은 확인 차단의 '단 한 번의 중단'에 포함되지 않음). 먼저
whoami(할당량 면제) 호출로 시트 확인: View/Collab 또는 Starter 시트는 월 6회 MCP 호출만 가능 — 예산이 부족할 경우 MCP를 건너뛰고 Step 2/4의 REST 경로로 직접 진행 (PAT은~/.figma_token에 저장, 없을 경우 사용자에게 Figma Settings → Security에서 생성하도록 요청, 사용자 자신의 터미널에 파일을 쓰도록 하여 채팅 기록에 남지 않게 함). which cwebp— 없으면 사용자에게brew install webp안내; 사용자가 설치를 거부하면 전체 페이지를 png 산출물로 폴백하고 납품 보고서에 명시.node -e "require('playwright-core')"— 없으면 scratchpad 디렉토리에npm i playwright-core실행; 브라우저는 로컬 Chrome 재사용 (channel: 'chrome'), 브라우저 바이너리 다운로드 안 함.
- Figma MCP 연결됨 (연결되지 않았다면 사용자 인증 유도, 인증 관련 상호작용은 확인 차단의 '단 한 번의 중단'에 포함되지 않음). 먼저
- 출력 디렉토리 기본값
./output/<활동명-slug>/(skill의 모든 산출물은 통일적으로output/아래에 배치; 활동명이 제공되지 않은 경우 Figma 페이지/프레임명에서 추론하여 확인 차단 시 확인). 디렉토리가 이미 존재하고 비어 있지 않은 경우 지금 중단하지 말 것 — 덮어쓰기 확인은 확인 차단 내용에 포함됨.
단계 2 — 디자인 명세 파악
- 희소 메타데이터 도구(get_metadata 계열)로 노드 트리 읽기; 루트 프레임에 대해 design context를 한 번에 전부 가져오지 말 것.
- MCP 사용 불가/예산 소진 시: REST
GET /v1/files/<key>/nodes?ids=<node>(depth를 지정하지 않음 = 전체 서브트리 JSON, 전체 텍스트, 글꼴, fills 그라데이션 원본 값, 정확한 bbox 포함 — 정보가 get_metadata보다 더 풍부함). 로컬에 저장하여 반복적으로 파싱, 한 번 호출로 전체 과정 커버; CSS 색상값/그라데이션/문구는 이 JSON에서 직접 가져오며 get_code 불필요. - 프레임 식별: 너비 ≤ 480 → H5 프레임; ≥ 1200 → PC 프레임; 판단 불가한 프레임은 확인 차단에 포함. H5 프레임만 있음 →
__PC_BASE__ = 375; H5+PC 이중 프레임 →__PC_BASE__ = 1920. - 산출물 자산 목록 생성: section 목록 (hero, banner, steps, tnc…); 각 노드에 이미지 컷아웃 / 실제 텍스트 / 상호작용 컴포넌트 레이블 지정 (판단 규칙은
references/patterns.md참조). - 모든 CTA (Join Now / 등록 / Sign Up 계열 버튼) 표시 — 이들에
.join-btn을 달아야 함.
단계 3 — 확인 차단 (정상 흐름에서 생성 전 유일한 중단)
한 메시지에 다음 내용을 전부 나열하여 한 번에 사용자 확인을 받고, 그 이후로 납품까지 중단하지 않음:
- 활동명,
__PAGE_TITLE__값,__FUNCTION_NAME__값 (예:WinsdayBillionDropLandingPage) - 출력 디렉토리 (디렉토리가 비어 있지 않은 경우 여기서 덮어쓰기 확인)
- 이미지 컷아웃 목록 (노드 →
images/<name>.webp) - CTA 노드 목록
- 상호작용 목록: 각 항목에 추론 근거와 필요한 매개변수 함께 첨부 (카운트다운→종료 시간/만료 시 동작; 캐러셀→자동 재생/간격; 모달→트리거 시점; 표는
references/patterns.md참조). 디자인에서 추론할 수 없는 매개변수는 사용자가 여기서 입력; 분류할 수 없는 노드는 '정적'으로 표시. - 프레임 식별 의심 항목
단계 4 — 이미지 컷아웃 내보내기 (4단계 폴백 체인, 가용성에 따라 다운그레이드)
- MCP
download_assets(시트 예산 충분 시): 배치당 ≤ 20개 노드, scale 2x, format png; 임시 URL을 curl로 다운로드. - REST 렌더링
GET /v1/images/<key>?ids=…&format=png&scale=2: ids는 쉼표로 묶은 배치이지만 배치당 ≤ 4개 큰 노드 (초과 시 "Render timeout" 오류); 429 = 시트 렌더링 예산 소진 (View 시트 약 12장 후 트리거, 윈도우는 시간 단위), 다음 단계로 전환, 기다리지 말 것. - REST 원본 이미지 엔드포인트
GET /v1/files/<key>/images: 전체 파일의 imageRef → 원본 이미지 URL 매핑 반환, 파일 예산 사용, 거의 렌더링 제한 없음. 이미지 채우기 노드(사진/스티커/아이콘 — 노드 JSON의fills[].imageRef대조하여 원본 해상도 획득, 종종 2x를 훨씬 초과)에 적합. fill의 scaleMode에 따라 크롭/trim alpha 후 크기 조정. 벡터 합성 노드(패널/리본)에는 부적합. - 사용자 수동 내보내기 (할당량 제로): Figma에서 사용자가 노드를 선택하여 Export하도록 요청. SVG 형식 우선 — 벡터 부분은 무한 해상도, 비트맵은 원본 해상도로 base64 인라인 포함 (
<image>를 정규식으로 추출하여 디코딩 가능, 투명 요소 추출에 최적의 소스). SVG→2x 비트맵 래스터화 파이프라인: SVG를 임시 디렉토리에 복사 (kebab 방식의 안전한 파일명, 원본 파일명은 종종&/공백 포함), 동일 디렉토리에 HTML 래퍼<img src="./x.svg">작성 (file://로 SVG를 직접 열거나 상위 디렉토리 file 참조 시 Chrome 차단), playwrightdeviceScaleFactor:2+omitBackground:true스크린샷으로 투명 PNG 획득.
- 공통: webp 변환
cwebp -q 80; 명명은 의미있는 kebab-case, hash 미포함; 순수 그라데이션/순수 색상 노드는 JSON을 직접 읽어 CSS로 작성, 이미지 미생성. - 주의: Figma에서 내보낸 SVG의 viewBox에는 드롭 섀도우/블리드(노드 박스보다 큼)가 포함될 수 있으므로, 컨테이너는 노드 박스를 기준으로 위치를 잡을 때 이미지가 비율을 유지하며 오버플로우되도록 하고, 찌그러뜨리지 말 것.
- 단일 이미지 내보내기 실패: 2회 재시도 → 다음 폴백 체인 레벨로 다운그레이드; 전체 체인 실패 시에만 건너뛰고 납품 보고서에 기록.
단계 5 — 코드 생성
assets/template/에서 시작:base.css의 내용을css/main.css의 시작 스켈레톤으로 사용 (산출물은 단일 main.css만 존재하며, base.css는 별도로 출력하지 않음).index.html:<title>교체; 폰트 링크는 디자인의 폰트로 변경 (Google Fonts와 일치하는 폰트가 없는 경우 가장 유사한 폰트를 선택하고 보고서에 기록);#app내에 section별로 시맨틱 구조 생성, BEM 클래스명 (.hero__bg,.banner__headline), CTA 요소에는 추가로join-btn클래스 부여.css/main.css: 값은 Figma의 정확한 치수/색상값/그라데이션에서 가져옴 (get_code의 plain HTML+CSS 출력이 값 추출에 도움 가능), 모두 먼저 px 정규화 후 rem으로 작성; 배경background-image, 내용 이미지는<img>태그. 이중 프레임 페이지: H5 규칙이 먼저, PC 재정의는 전부@media (min-width: 576px)안에 배치.js/: 템플릿 세 개 (tool.js / pm_main.js / main.js) 복사, 3개 플레이스홀더 교체 (tool.js의__PC_BASE__, main.js의 나머지 두 개); 확인된 상호작용 코드는 main.js의 마커 아래에 삽입.- 디자인에서 프레임 가장자리로 블리드되어 잘리는 요소(bbox가 프레임 너비를 초과)는 컨테이너에
overflow: hidden을 적용하여 프레임 크롭을 재현해야 하며, 그렇지 않으면 페이지가 수평으로 오버플로우됨. - 자체 점검:
node --check js/*.js세 파일 모두 통과.
단계 6 — 스크린샷 비교 반복 (section별 분할, 최대 3회)
- 출력 디렉토리에서 로컬 서버 시작:
python3 -m http.server <포트>. - 다음 스크린샷 스크립트를 scratchpad에 작성하고 이를 사용하여 section별 분할 스크린샷 촬영 (clip 좌표는 DOM에서 획득:
page.evaluate에서 각 section 요소에 대해getBoundingClientRect호출):
// shot.mjs <url> <out.png> <width> <dsf> [clipY clipH]
import { chromium } from 'playwright-core';
const [url, out, width, dsf, clipY, clipH] = process.argv.slice(2);
const browser = await chromium.launch({ channel: 'chrome' });
const page = await browser.newPage({
viewport: { width: +width, height: 800 },
deviceScaleFactor: +dsf,
});
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot(clipY === undefined
? { path: out, fullPage: true }
: { path: out, clip: { x: 0, y: +clipY, width: +width, height: +clipH } });
await browser.close();
- H5 비교: 로컬 width 375, dsf 2; Figma 측에서는
download_assets를 사용하여 section 노드별로 2x png 비교 이미지 내보내기 (비교 이미지는 scratchpad에 보관,images/에 넣지 않음). - section별로 나란히 비교하여 차이점 수정; 최대 3회까지, 그 후에도 차이가 있으면 납품 보고서에 기록.
- 비교 수렴 후, 별도로 width 414 (dsf 2) 전체 페이지 빠른 회귀 테스트를 한 번 수행하여 어긋남이나 오버플로우가 없는지 확인 (checklist에서 375/414 두 가지 요구).
- 전체 프레임 스크린샷은 납품 보고서의 시각적 확인용으로만 사용하며, 차이 판단 기준으로 절대 사용하지 않음 (긴 페이지 전체를 프레임으로 찍으면 글자 크기/색상 차이를 식별할 수 없을 정도로 다운샘플링됨).
- 이중 프레임 페이지: 별도로 width 1920, dsf 1로 PC 레이아웃에 대해 위의 분할 비교 반복 (PC 프레임 1x 내보내기 이미지 기준); 그리고 width 1280 전체 페이지 빠른 회귀 테스트 수행 (checklist 이중 프레임 요구 사항은 1280/1920).
- 함께 확인: 브라우저 콘솔에 오류 없음 (
page.on('console')또는 개발자 도구).
단계 7 — 납품
납품 보고서에는 다음이 포함:
- 출력 디렉토리 트리
references/checklist.md의 항목별 체크 결과- 남은 알려진 차이점 (3회 내에 제거되지 않은 것)
- 분할 비교 스크린샷 경로
- 사용된 폴백 (png 대신 webp, 근사 폰트, 건너뛴 이미지 컷아웃)
오류 처리 참고표
| 시나리오 | 처리 |
|---|---|
| Figma MCP 연결 안 됨/인증 만료 | 인증 유도 후 중단된 단계부터 계속 (확인 차단의 중단에 포함되지 않음) |
| node-id API 호출 404/빈 결과 | 하이픈→콜론 변환 확인 |
| 이미지 컷아웃 내보내기 실패/429 제한 | 2회 재시도 → 다음 폴백 체인 레벨로 다운그레이드 (Step 4 참조) → 그래도 실패하면 건너뛰고 보고서 기록 |
| cwebp 없고 사용자가 설치 거부 | 산출물을 images/*.png로 폴백 + 보고서 기록 |
| 프레임 구조 식별 불가 | 확인 차단에 포함시켜 사용자에게 질문 (구체적인 node-id 필요) |
| 출력 디렉토리가 비어 있지 않음 | 확인 차단 내에서 덮어쓰기 확인을 함께 처리 |