Communitygithub.com

kyjongSNSoft/landing-page-skill

Figma 디자인 링크/파일로부터 프로모션/마케팅 랜딩 페이지를 생성합니다 (SNSoft 활동 랜딩 페이지). 배포 준비된 정적 HTML + CSS + JS를 rem 레이아웃, 추출된 이미지 컷아웃, 고정된 분석/postMessage 런타임과 함께 생성합니다. 트리거: /figma-landing-page, "figma 랜딩 페이지", "이 Figma를 랜딩 페이지로", Figma 링크 + 랜딩 페이지 요청.

landing-page-skill란 무엇인가요?

이 스킬은 Figma 디자인 파일(링크 또는 파일)을 입력받아 배포 가능한 정적 활동 랜딩 페이지를 자동 생성합니다. 주로 한중일 시장의 프로모션/마케팅 랜딩 페이지를 대상으로 하며, index.html, CSS, JS, WebP 이미지로 구성된 완전한 산출물을 제공합니다. 이 스킬은 Figma MCP 프로토콜 또는 REST API를 통해 디자인 데이터를 추출하고, 정해진 템플릿 구조(tool.js, pm_main.js, main.js)와 세 개의 고정 플레이스홀더(__PAGE_TITLE__, __FUNCTION_NAME__, __PC_BASE__)를 기반으로 코드를 생성합니다. 또한 CTA 요소에 .join-btn 클래스 적용, 576px 단일 중단점, px→rem 변환 규칙, 이미지 내보내기(4단계 폴백), 섹션별 스크린샷 비교(최대 3회 반복) 등 엄격한 계약을 따릅니다. 이 스킬은 Claude Code, Cursor, Codex 등 MCP를 지원하는 모든 AI 에이전트에서 사용할 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kyjongSNSoft/landing-page-skill/tree/main/skill

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

미리보기

스킬 README에서

landing-page-skill preview

문서

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; PC px × 1920 ÷ PC프레임너비 ÷ 100 = rem.
  • 템플릿 JS 세 개 (tool.js rem 적응 / pm_main.js postMessage 브릿지 / main.js 활동 비즈니스 레이어, 로드 순서 동일)의 고정 부분은 한 글자도 수정 금지; 페이지 상호작용은 main.js의 // === Page-specific interactions === 아래에만 삽입 (IIFE 내부, $p/pm/track/Date.prototype.format 직접 사용 가능).
  • 상세 규칙(이미지 컷아웃 경계, 클래스명, 상호작용 추론 테이블)은 references/patterns.md 참조, 납품 기준은 references/checklist.md 참조 — 생성 전에 이 두 파일을 먼저 읽을 것.

단계 1 — 입력 파싱 및 환경 점검

  1. 링크에서 file key와 node-id 추출. node-id 내 하이픈을 콜론으로 변환 (123-456 → 123:456) 후 Figma API 호출; API 호출 시 404/빈 결과가 나오면 이 단계를 먼저 확인.
  2. 세 가지 환경 점검 (통과하지 못하면 먼저 수정 후 계속):
    • 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'), 브라우저 바이너리 다운로드 안 함.
  3. 출력 디렉토리 기본값 ./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단계 폴백 체인, 가용성에 따라 다운그레이드)

  1. MCP download_assets (시트 예산 충분 시): 배치당 ≤ 20개 노드, scale 2x, format png; 임시 URL을 curl로 다운로드.
  2. REST 렌더링 GET /v1/images/<key>?ids=…&format=png&scale=2: ids는 쉼표로 묶은 배치이지만 배치당 ≤ 4개 큰 노드 (초과 시 "Render timeout" 오류); 429 = 시트 렌더링 예산 소진 (View 시트 약 12장 후 트리거, 윈도우는 시간 단위), 다음 단계로 전환, 기다리지 말 것.
  3. REST 원본 이미지 엔드포인트 GET /v1/files/<key>/images: 전체 파일의 imageRef → 원본 이미지 URL 매핑 반환, 파일 예산 사용, 거의 렌더링 제한 없음. 이미지 채우기 노드(사진/스티커/아이콘 — 노드 JSON의 fills[].imageRef 대조하여 원본 해상도 획득, 종종 2x를 훨씬 초과)에 적합. fill의 scaleMode에 따라 크롭/trim alpha 후 크기 조정. 벡터 합성 노드(패널/리본)에는 부적합.
  4. 사용자 수동 내보내기 (할당량 제로): Figma에서 사용자가 노드를 선택하여 Export하도록 요청. SVG 형식 우선 — 벡터 부분은 무한 해상도, 비트맵은 원본 해상도로 base64 인라인 포함 (<image>를 정규식으로 추출하여 디코딩 가능, 투명 요소 추출에 최적의 소스). SVG→2x 비트맵 래스터화 파이프라인: SVG를 임시 디렉토리에 복사 (kebab 방식의 안전한 파일명, 원본 파일명은 종종 &/공백 포함), 동일 디렉토리에 HTML 래퍼 <img src="./x.svg"> 작성 (file://로 SVG를 직접 열거나 상위 디렉토리 file 참조 시 Chrome 차단), playwright deviceScaleFactor: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 — 납품

납품 보고서에는 다음이 포함:

  1. 출력 디렉토리 트리
  2. references/checklist.md의 항목별 체크 결과
  3. 남은 알려진 차이점 (3회 내에 제거되지 않은 것)
  4. 분할 비교 스크린샷 경로
  5. 사용된 폴백 (png 대신 webp, 근사 폰트, 건너뛴 이미지 컷아웃)

오류 처리 참고표

시나리오처리
Figma MCP 연결 안 됨/인증 만료인증 유도 후 중단된 단계부터 계속 (확인 차단의 중단에 포함되지 않음)
node-id API 호출 404/빈 결과하이픈→콜론 변환 확인
이미지 컷아웃 내보내기 실패/429 제한2회 재시도 → 다음 폴백 체인 레벨로 다운그레이드 (Step 4 참조) → 그래도 실패하면 건너뛰고 보고서 기록
cwebp 없고 사용자가 설치 거부산출물을 images/*.png로 폴백 + 보고서 기록
프레임 구조 식별 불가확인 차단에 포함시켜 사용자에게 질문 (구체적인 node-id 필요)
출력 디렉토리가 비어 있지 않음확인 차단 내에서 덮어쓰기 확인을 함께 처리

관련 스킬