Figma 퍼블리싱 파이프라인 08. Phase 4에서 5로 노드 정보 넘기기
Design IR과 Code IR을 각각 만들고 기계가 비교하는 구조는 세워뒀는데, 돌려보니 그 둘 사이가 끊겨 있었다.
엉뚱한 페이지의 코드를 읽고 있었다
Code IR을 만드는 Phase 5가 어떤 파일을 읽어야 할지 스스로 판단하고 있었는데, 그 판단이 자주 틀렸다. "CPA 외부 입력폼 현황" 페이지를 퍼블리싱하는데 marketer-member 관련 파일을 가져오는 식이었다.
원인은 Phase 4에 있었다. Design IR을 만들면서 최상위 페이지 정보를 버리고 있었다.
# 변경 전 — 섹션 노드 배열만 남는다
output = {
"meta": {"timestamp": ..., "total_nodes": len(design_ir_nodes)},
"nodes": design_ir_nodes
}nodes에는 LNB, Main Content 같은 섹션들만 들어 있다. 이 페이지가 원래 무슨 페이지였는지는 이 시점에 이미 사라진 상태다. Phase 5 입장에서는 단서 없이 폴더를 찾아야 했다.
첫 번째 노드를 root로 따로 빼뒀다.
# 변경 후
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이 섞여 들어오던 것도 이 김에 걸러냈다.
한글 페이지명이 폴더명으로 변환되지 않았다
Phase 4를 돌리기 전에 Figma API 응답을 그대로 저장해둔 raw.json이 있다. 여기엔 원본 페이지명이 살아 있어서, 먼저 이걸 읽어 폴더명을 추측하게 했다.
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 외부 입력폼 현황"여기서 막혔다. "CPA 외부 입력폼 현황"을 케밥 케이스로 바꾸면 cpa-외부-입력폼-현황이 나오는데, 실제 폴더명은 cpa-custom-form이다. 디자이너는 한글로 이름을 짓고 개발자는 영어로 폴더를 만드니 애초에 문자열 변환으로 이어질 수 있는 관계가 아니었다.
폴백을 하나 뒀다. raw.json 변환이 실패하면 Design IR의 섹션 이름을 쓴다. "LNB"처럼 영어로 된 섹션명이면 패턴 매칭으로 lnb-layout-provider.tsx 같은 파일을 찾을 수 있다. 느리고 정확도도 낮지만 아무것도 못 찾는 것보다는 낫다.
경로를 직접 받는 쪽이 확실했다
이름으로 추측하는 대신 사용자가 경로를 알려주게 했다.
parser.add_argument("--page-path",
help="페이지 경로 (/monitoring/cpa-custom-form) — 정확한 폴더 찾기용")--page-path "/monitoring/cpa-custom-form"을 넘기면 후보 경로를 순서대로 확인한다.
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",
](auth) 라우트 그룹을 먼저 보는 건 이 프로젝트의 페이지 대부분이 인증 뒤에 있어서다. 라우트 그룹은 URL에 안 나타나기 때문에, URL 경로만으로는 파일 경로를 알 수 없어 후보를 늘려 확인해야 한다.
첫 번째로 존재하는 폴더를 찾으면 거기서만 스캔한다. 전체 730개 파일을 뒤지는 대신 해당 페이지의 44개만 본다.
Phase 7이 나중에 어느 파일 기준으로 스캔했는지 알아야 한다는 걸 뒤늦게 알아채고, matched_file과 source_dir도 메타데이터에 남겼다.
{
"meta": {
"timestamp": 1776069686,
"total_nodes": 44,
"matched_file": "/Users/taehoon/.../cpa-custom-form-page.tsx",
"source_dir": "/Users/taehoon/.../cpa-custom-form/_source/components"
}
}오케스트레이터는 이미 앱 자동 감지 용도로 --page-path를 받고 있어서, 그대로 Phase 5에 넘기기만 하면 됐다.
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])경로를 주면 노드가 118개에서 44개로 줄었다
| 항목 | --page-path 없이 | --page-path 포함 |
|---|---|---|
| Code 노드 수 | 118개 | 44개 |
| 불필요 파일 | marketer-member 포함 | 없음 |
| diff 수 | 1,211개 | 1,186개 |
노드가 63% 줄고 엉뚱한 파일도 사라졌다. diff는 25개밖에 안 줄었는데, 남은 diff 대부분이 파일을 잘못 읽어서 생긴 게 아니라 디자인 구조 자체가 코드와 다른 데서 오는 것이었기 때문이다.
Phase 0부터 13까지 20.9초에 통과했고 pnpm type-check도 통과했다.
막힌 지점과 원인
| 문제 | 원인 | 해결 |
|---|---|---|
| Phase 5가 엉뚱한 페이지 코드를 읽음 | Phase 4가 최상위 페이지 정보를 버림 | Design IR 출력에 root 필드 추가 |
| 원본 페이지명으로 폴더를 못 찾음 | 한글 페이지명을 영어 폴더명으로 변환할 수 없음 | --page-path로 경로 직접 지정, 실패 시 섹션명 폴백 |
| 전체 730개 파일을 스캔 | 대상 폴더를 특정하지 못해 전수 탐색 | 후보 경로 우선 확인 후 해당 폴더만 스캔 |
| Phase 7이 스캔 기준을 모름 | 어느 파일을 읽었는지 기록이 없음 | matched_file·source_dir를 메타데이터에 저장 |
경로를 넘기는 쪽이 빠르고 정확했다. 없어도 돌아가긴 하지만 한글 페이지명이 많은 프로젝트에서는 결국 폴백에 기대게 된다.