Figma 퍼블리싱 파이프라인 08. v5: Phase 4→5 자동 연동
·8 min read·8 / 8
v5 파이프라인: Phase 4→5 자동 연동 구현
목표
Figma 디자인 노드를 Code 파일로 자동 매칭하여 Design ↔ Code 정확한 비교
구현 완료
1. phase4_design_ir.py (391-398줄)
목표: Design IR 출력에 root 필드 추가 → Phase 5에서 원본 노드 정보 추출 가능
변경:
# 변경 전
output = {
"meta": {"timestamp": ..., "total_nodes": len(design_ir_nodes)},
"nodes": design_ir_nodes
}
# 변경 후
output = {
"meta": {"timestamp": ..., "total_nodes": len(design_ir_nodes)},
"root": design_ir_nodes[0] if design_ir_nodes else None, # ← 추가
"nodes": design_ir_nodes
}
# 또한 None 값 필터링 추가
design_ir_nodes = [n for n in design_ir_nodes if n is not None]이유:
- Phase 5가 design_ir의 root.name으로 원본 페이지명을 추출하려면 필요
- 섹션 노드들(LNB, Main Content)의 이름도 폴백으로 활용 가능
결과:
{
"meta": {"timestamp": 1776069686, "total_nodes": 2},
"root": {
"nodeId": "21495:202306",
"name": "LNB",
"children": [...]
},
"nodes": [...] # 모든 노드
}2. phase5_code_ir.py (다중 개선)
A) raw.json 기반 원본 노드명 추출 (296-352줄)
목표: 원본 페이지 이름(한글)을 raw.json에서 직접 추출
변경:
# raw.json → 원본 nodeId의 페이지명 추출
if raw_path.exists():
with open(raw_path) as f:
raw_data = json.load(f)
if "nodes" in raw_data and "root" in raw_data["nodes"]:
node_name = raw_data["nodes"]["root"].get("name", "")
# 예: "CPA 외부 입력폼 현황"
# design_ir → 섹션 노드명 추출 (fallback)
if not node_name and design_ir_path.exists():
children = root_node.get("children", [])
section_names = [c.get("name") for c in children if c.get("name")]
if section_names:
node_name = section_names[0] # 예: "LNB"이유:
- raw.json이 원본 페이지 정보(Design 루트 노드)를 보유
- design_ir은 섹션들이므로 원본 이름이 손실됨
- 폴백: design_ir의 섹션 이름으로도 패턴 매칭 가능
결과:
원본 노드명 (raw.json): CPA 외부 입력폼 현황
→ 케밥케이스: cpa-외부-입력폼-현황 (한글이라 실패)
→ fallback: design_ir 첫 섹션 "LNB" → lnb
→ 패턴 매칭으로 lnb-layout-provider.tsx 발견B) --page-path 옵션 추가 (284줄, 290-303줄)
목표: 페이지 경로로 정확한 _source/components 폴더 찾기
변경:
# 파라미터 추가
parser.add_argument("--page-path",
help="페이지 경로 (/monitoring/cpa-custom-form) — 정확한 폴더 찾기용")
# 페이지 경로로 폴더 찾기 로직
if args.page_path:
page_path = Path(args.page_path.strip("/"))
code_path_obj = Path(args.code_path)
candidates = [
code_path_obj / "src/app/(auth)" / str(page_path) / "_source/components",
code_path_obj / "src/app" / str(page_path) / "_source/components",
code_path_obj / "src" / str(page_path) / "_source/components",
]
for candidate in candidates:
if candidate.exists():
args.code_path = str(candidate.resolve())
print(f"📍 페이지 경로로 폴더 찾음: {candidate.relative_to(code_path_obj)}")
break이유:
- 한글 페이지명으로는 패턴 매칭 불가
- --page-path로 직접 폴더 지정하면 모든 tsx 파일 정확히 스캔
- 불필요한 모든 페이지의 컴포넌트를 읽지 않아 효율성 증대
사용 예:
orchestrate_v5.py \
--page-path "/monitoring/cpa-custom-form" \
...결과:
입력: /monitoring/cpa-custom-form
찾은 폴더: src/app/(auth)/monitoring/cpa-custom-form/_source/components
코드 노드: 44개 (전체 730개 중 정확한 페이지만)C) code_ir.json 메타데이터 (413-416줄)
목표: Phase 5가 발견한 파일 정보를 메타데이터에 저장 → Phase 7 참조용
변경:
output = {
"meta": {
"timestamp": int(__import__("time").time()),
"total_nodes": len(code_ir_nodes),
"matched_file": str(target_file.resolve()) if target_file else None, # ← 추가
"source_dir": str(code_path.resolve()) # ← 추가
},
"nodes": code_ir_nodes
}이유:
- Phase 7 (매칭 엔진)이 어느 파일에서 스캔했는지 알아야 함
- 추후 Code IR 재사용 시 파일 참조 가능
결과:
{
"meta": {
"timestamp": 1776069686,
"total_nodes": 44,
"matched_file": "/Users/taehoon/.../cpa-custom-form/_source/components/cpa-custom-form-page.tsx",
"source_dir": "/Users/taehoon/.../cpa-custom-form/_source/components"
}
}3. orchestrate_v5.py (547-552줄)
목표: orchestrate에서 --page-path를 Phase 5로 전달
변경:
# Phase 5 arg 조립 (기존)
orch.run_phase(
5, "phase5_code_ir.py",
["--code-path", code_path, "--node-id", args.node_id, "--design-ir", ...],
...
)
# Phase 5 arg 조립 (변경 후)
phase5_page_path = args.page_path if hasattr(args, 'page_path') and args.page_path else None
phase5_args = ["--code-path", code_path, "--node-id", args.node_id, "--design-ir", ...]
if phase5_page_path:
phase5_args.extend(["--page-path", phase5_page_path])
orch.run_phase(
5, "phase5_code_ir.py",
phase5_args,
...
)이유:
- orchestrate가 이미 --page-path를 받음 (앱 자동 감지용)
- Phase 5에도 같은 정보를 전달하면 정확한 폴더 찾기 가능
작동 흐름
사용자 입력
↓
--page-path "/monitoring/cpa-custom-form" (선택사항)
--file-key "MiI74Bwt1BQ89xxzaMQ81u"
--node-id "21495:202305"
↓
Phase 0-3: Figma API + 딥 분석
↓
Phase 4: Design IR 생성
└─ root 필드에 첫 노드 저장
↓
Phase 5: Code IR 생성
├─ IF --page-path 있음:
│ └─ src/app/(auth)/{page-path}/_source/components 폴더 직접 사용
│ → 44개 코드 노드 추출
├─ ELSE (--page-path 없음):
│ ├─ raw.json에서 원본 페이지명 추출
│ │ → "CPA 외부 입력폼 현황"
│ ├─ 케밥케이스 변환 (한글 → 실패)
│ └─ fallback: design_ir 섹션명 사용
│ → "LNB" → lnb-layout-provider.tsx
│ → 730개 모든 파일 스캔 (비효율)
↓
Phase 6-8: 정규화 + 매칭 + Diff
├─ Phase 7: Code 파일 기준으로 Design과 매칭
└─ 결과: cpa-custom-form-* 완벽 매칭
↓
Phase 9-13: 리포트 생성 + 검증 + 정리성능 비교
--page-path 없이
Code 노드: 118개 (모든 _source/components)
매칭된 쌍: 27개
미매칭: 1748개
결과: marketer-member-* 불필요하게 포함 (1211 diffs)--page-path 포함
Code 노드: 44개 (정확한 페이지만)
매칭된 쌍: 16개
미매칭: 1759개 (Design 구조 차이)
결과: cpa-custom-form-* 올바르게 매칭 (1186 diffs)개선 사항:
- diff 개수 감소: 1211 → 1186 (-25개, -2%)
- Code 스캔 효율: 118 → 44 노드 (-63%)
- 파일 매칭 정확도: 53개(marketer) → 0개
핵심 설계
| 레이어 | 목표 | 구현 |
|---|---|---|
| Phase 4 | 원본 Design 정보 보존 | root 필드 추가 |
| Phase 5 (일반) | 다양한 경로 대응 | raw.json + design_ir fallback |
| Phase 5 (최적) | 정확한 폴더 찾기 | --page-path 직접 사용 |
| Phase 7 | 올바른 매칭 | 정확한 파일 기준 비교 |
검증 결과
세션: 1776069686_379293_d5906b
✅ Phase 0-13 완전 통과 (20.9초)
✅ pnpm type-check 통과
✅ cpa-custom-form 모든 코드 ID 올바른 매칭
✅ 불필요한 marketer-member 제거됨사용 가이드
최적 사용법
orchestrate_v5.py \
--file-key "MiI74Bwt1BQ89xxzaMQ81u" \
--node-id "21495:202305" \
--app admin \
--page-path "/monitoring/cpa-custom-form" # ← 추가폴백 사용법 (한글 페이지명도 동작)
orchestrate_v5.py \
--file-key "MiI74Bwt1BQ89xxzaMQ81u" \
--node-id "21495:202305" \
--app admin
# --page-path 없이도 작동 (raw.json + design_ir fallback)확장 가능성
향후 개선 방향
- 자동 page-path 감지: URL 패턴에서 page-path 자동 추출
- 캐싱: raw.json → page-path 매핑 캐시 (재실행 시 빠름)
- 에러 처리: page-path 잘못됐을 때 자동 폴백
- 멀티 페이지: 여러 페이지를 한 번에 처리