| name | figma-v3-migration-plugin-dev |
| description | Figma V3 Migration Plugin development specialist. Use when developing Figma V3 Migration Plugin. Focuses on Figma V3 Migration Plugin development. |
| allowed-tools | Read, Write, Edit, MultiEdit, Bash, Glob, Grep |
개요
Figma V3 Migration 플러그인은 SEED Design System V2 컴포넌트를 V3로 마이그레이션하는 Figma 플러그인입니다.
디렉토리 구조
tools/figma-v3-migration/
├── src/main/
│ ├── mapping/ # 매핑 정의 파일들
│ │ ├── types.ts # 타입 정의
│ │ ├── index.ts # 모든 매핑 export
│ │ ├── buttons.ts # 버튼 컴포넌트 매핑
│ │ ├── action-sheet.ts # Action Sheet 매핑
│ │ └── [component].ts # 기타 컴포넌트별 매핑
│ ├── data/
│ │ └── __generated__/ # 자동 생성된 메타데이터
│ │ ├── v2-component-sets/ # V2 컴포넌트 메타데이터
│ │ └── v3-component-sets/ # V3 컴포넌트 메타데이터
│ └── services/ # Figma API 서비스
├── figma-extractor.config.ts # 추출 설정
└── package.json
환경 설정
1. 환경변수 설정
export FIGMA_PERSONAL_ACCESS_TOKEN="your-figma-token"
Figma Personal Access Token은 Figma Settings > Account > Personal access tokens에서 생성할 수 있습니다.
2. 의존성 설치
cd tools/figma-v3-migration
bun install
매핑 최신화 프로세스
1. 메타데이터 추출
cd tools/figma-v3-migration
bun extract
이 명령어는 Figma API에서 V3 컴포넌트 메타데이터를 추출하여 src/main/data/__generated__/v3-component-sets/ 디렉토리에 .d.ts 파일로 저장합니다.
2. 변경된 파일 확인
git status
git diff src/main/data/__generated__/
3. 매핑 파일 업데이트 워크플로우
- 변경된 Generated 파일 분석: 새로 추가되거나 변경된 컴포넌트 확인
- 관련 매핑 파일 수정:
src/main/mapping/디렉토리의 해당 컴포넌트 매핑 업데이트 - index.ts 업데이트: 새 매핑 추가 시 export 목록에 추가
- 타입 체크:
bun run typecheck:main으로 매핑 파일 타입 에러 확인
매핑 파일 작성 가이드
기본 구조
매핑 파일은 ComponentMapping<OldComponentName, NewComponentName> 타입을 사용합니다.
import type { ComponentMapping, NewComponentProperties } from "./types";
export const exampleMapping: ComponentMapping<"Old Component Name", "New Component Name"> = {
oldComponent: "Old Component Name", // V2 컴포넌트 이름 (Generated 파일의 name과 일치)
newComponent: "New Component Name", // V3 컴포넌트 이름
variantMap: {
// Variant 값 매핑
},
calculateProperties(oldProperties) {
// 프로퍼티 변환 로직
const newProperties: NewComponentProperties<"New Component Name"> = {};
return newProperties;
},
};
실제 예제: buttons.ts
// tools/figma-v3-migration/src/main/mapping/buttons.ts
import type { ComponentMapping, NewComponentProperties } from "./types";
export const boxButtonMapping: ComponentMapping<"✅ Box Button v2", "🟢 Action Button"> = {
oldComponent: "✅ Box Button v2",
newComponent: "🟢 Action Button",
// 1. variantMap: Variant 값 매핑
variantMap: {
// 형식: "VariantName:OldValue": "VariantName:NewValue"
"Size:XSmall": "Size:Small",
"Size:Small": "Size:Small",
"Size:Medium": "Size:Medium",
"Size:Large": "Size:Large",
"Size:XLarge": "Size:Large",
"State:Enabled": "State:Enabled",
"State:Disabled": "State:Disabled",
"State:Loading": "State:Loading",
"State:Pressed": "State:Pressed",
"Variant:Primary": "Variant:Neutral Solid",
"Variant:Primary low": "Variant:Neutral Weak",
"Variant:Secondary": "Variant:Neutral Weak",
"Variant:Danger": "Variant:Critical Solid",
},
// 2. calculateProperties: 프로퍼티 변환 로직
calculateProperties(oldProperties) {
const newProperties: NewComponentProperties<"🟢 Action Button"> = {
// TEXT 프로퍼티 매핑: "PropertyName#NodeId"
"Label#5987:61": oldProperties["Label#28272:77"].value,
};
// BOOLEAN 프로퍼티 읽기
const prefixIcon = oldProperties["Prefix icon#28272:78"].value;
const suffixIcon = oldProperties["Suffix icon#28272:76"].value;
// 조건부 로직으로 Layout 설정
if (prefixIcon && suffixIcon) {
newProperties.Layout = "Icon Last";
newProperties["Prefix Icon#5987:305"] = oldProperties["↳Icons#28292:0"].value;
} else if (prefixIcon) {
newProperties.Layout = "Icon First";
newProperties["Prefix Icon#5987:305"] = oldProperties["↳Icons#28292:0"].value;
} else if (suffixIcon) {
newProperties.Layout = "Icon Last";
} else {
newProperties.Layout = "Text Only";
}
return newProperties;
},
};
중첩 컴포넌트 처리: action-sheet.ts
부모 컴포넌트 내부에 자식 컴포넌트가 있는 경우 childrenMappings를 사용합니다.
// tools/figma-v3-migration/src/main/mapping/action-sheet.ts
import type { ComponentMapping, NewComponentProperties } from "./types";
// 자식 컴포넌트 매핑 정의
const itemMenuGroupMapping: ComponentMapping<"Action button group", ".Item / Menu Group"> = {
oldComponent: "Action button group",
newComponent: ".Item / Menu Group",
variantMap: {},
calculateProperties(oldProperties) {
const newProperties: NewComponentProperties<".Item / Menu Group"> = {
"Action Count":
oldProperties["Action count"].value === "8 (Max)"
? "8"
: oldProperties["Action count"].value,
};
return newProperties;
},
};
const itemMenuItemMapping: ComponentMapping<"Action button", ".Item / Menu Item"> = {
oldComponent: "Action button",
newComponent: ".Item / Menu Item",
variantMap: {
"State:Default": "State:Enabled",
"State:Pressed": "State:Pressed",
"Type:Destructive": "Tone:Critical",
"Type:Enabled": "Tone:Neutral",
"Prefix icon:True": "Layout:Text with Icon",
"Prefix icon:False": "Layout:\bText Only",
},
calculateProperties(oldProperties) {
const newProperties: NewComponentProperties<".Item / Menu Item"> = {
"Label#55905:8": oldProperties["🅃 Action label#55905:8"].value,
};
const hasPrefixIcon = oldProperties["Prefix icon"].value === "True";
if (hasPrefixIcon) {
newProperties["Show Prefix Icon#17043:5"] = true;
newProperties["Prefix Icon#55948:0"] = oldProperties["Icon#55948:0"].value;
}
return newProperties;
},
};
// 부모 컴포넌트 매핑 (childrenMappings 포함)
export const actionSheetMapping: ComponentMapping<"✅ Action Sheet v2", "🟢 Menu Sheet"> = {
oldComponent: "✅ Action Sheet v2",
newComponent: "🟢 Menu Sheet",
variantMap: {},
calculateProperties(oldProperties) {
const newProperties: NewComponentProperties<"🟢 Menu Sheet"> = {
Layout: "Text Only",
"Show Safe Area#25531:15": true,
"Menu Group Count": "1",
};
const hasTitle = oldProperties.Title.value === "True";
if (hasTitle) {
newProperties["Show Header#17043:12"] = true;
}
return newProperties;
},
// 자식 컴포넌트 매핑 배열
childrenMappings: [itemMenuGroupMapping, itemMenuItemMapping],
};
새 매핑 추가 시 체크리스트
Generated 파일 확인
- V2:
src/main/data/__generated__/v2-component-sets/[component].d.ts - V3:
src/main/data/__generated__/v3-component-sets/[component].d.ts
- V2:
매핑 파일 생성/수정
src/main/mapping/[component].ts파일 생성 또는 기존 파일에 추가
index.ts 업데이트
// src/main/mapping/index.ts import { newComponentMapping } from "./new-component"; export default [ // ... 기존 매핑들 newComponentMapping, ] as const;타입 체크
cd tools/figma-v3-migration bun run typecheck:main # 매핑 파일 타입 체크 bun run typecheck # 전체 타입 체크 (main + ui)
타입 시스템
Generated 메타데이터 구조
// src/main/data/__generated__/v3-component-sets/action-button.d.ts
export declare const metadata: {
"name": "🟢 Action Button",
"key": "450ede9d0bf42fc6ef14345c77e6e407d6d5ee89",
"componentPropertyDefinitions": {
"Label#5987:61": {
"type": "TEXT",
"defaultValue": "라벨"
},
"Size": {
"type": "VARIANT",
"defaultValue": "XSmall",
"variantOptions": ["XSmall", "Small", "Medium", "Large"]
},
"Layout": {
"type": "VARIANT",
"defaultValue": "Text Only",
"variantOptions": ["Text Only", "Icon First", "Icon Last", "Icon Only"]
},
"Prefix Icon#5987:305": {
"type": "INSTANCE_SWAP",
"defaultValue": "37665:153410",
"preferredValues": []
}
}
};
프로퍼티 타입별 처리
| 타입 | 설명 | 값 형식 |
|---|---|---|
VARIANT |
선택 가능한 옵션들 | variantOptions 중 하나 |
TEXT |
텍스트 입력 | string |
BOOLEAN |
참/거짓 | boolean |
INSTANCE_SWAP |
컴포넌트 교체 | 컴포넌트 key (string) |
타입 안전성
ComponentMapping 타입은 Generated 메타데이터를 기반으로 타입 검증을 수행합니다:
- 컴포넌트 이름: V2/V3 Generated 파일의
name과 일치해야 함 - 프로퍼티 키:
"PropertyName#NodeId"형식으로 정확히 일치해야 함 - Variant 값:
variantOptions에 정의된 값만 사용 가능
잘못된 프로퍼티 이름이나 값을 사용하면 TypeScript 컴파일 에러가 발생합니다.
트러블슈팅
일반적인 에러
1. "Property does not exist" 타입 에러
원인: 프로퍼티 이름이 Generated 파일과 일치하지 않음
해결: Generated .d.ts 파일에서 정확한 프로퍼티 이름 확인
cat src/main/data/__generated__/v3-component-sets/[component].d.ts
2. "Type is not assignable" 에러
원인: Variant 값이 variantOptions에 없는 값
해결: Generated 파일의 variantOptions 확인 후 올바른 값 사용
3. Extract 명령어 실패
원인: FIGMA_PERSONAL_ACCESS_TOKEN 미설정 또는 만료
해결:
export FIGMA_PERSONAL_ACCESS_TOKEN="new-token"
bun extract
디버깅 팁
프로퍼티 이름 확인
# V2 컴포넌트 프로퍼티 확인 cat src/main/data/__generated__/v2-component-sets/[component].d.ts # V3 컴포넌트 프로퍼티 확인 cat src/main/data/__generated__/v3-component-sets/[component].d.ts기존 매핑 패턴 참고
# 비슷한 컴포넌트의 매핑 확인 cat src/main/mapping/buttons.ts cat src/main/mapping/checkbox.ts타입 에러 확인
cd tools/figma-v3-migration bun run typecheck:main # 매핑 파일만 체크 bun run typecheck # 전체 체크