본문으로 건너뛰기

Figma 퍼블리싱 파이프라인 05. 제품 관리 탭 재퍼블리싱 실행 사례

·9 min read·5 / 12

운영 관리 상세 페이지의 제품 관리 탭을 다시 퍼블리싱했다. 이미 구현돼 있는 화면이라 새로 만드는 게 아니라 시안과 코드의 차이를 찾아 맞추는 작업이었다. --report 옵션을 붙여 단계마다 토큰이 얼마나 드는지도 같이 쟀다.

퍼블리싱해줘 https://www.figma.com/design/MiI74Bwt1BQ89xxzaMQ81u/...?node-id=19838-69031 --report

3,285개 노드에서 두 섹션만 골라냈다

Phase 0에서 Python 스크립트가 Figma를 분석한다. 여기까지는 LLM을 안 쓰기 때문에 토큰이 거의 안 든다.

python3 .claude/scripts/figma_analyze.py "<FIGMA_URL>" \
  --app admin --out /tmp/figma_spec.json
항목개수
추출된 노드3,285개
이미지 에셋15개
테이블 헤더8개
테이블 바디122개
토큰 매핑112개

감지된 섹션은 LNB와 Main Container 둘이었고, 이번에 볼 건 Main Container라 그것만 골랐다. 여기까지 누적 400 토큰이다.

대상 코드는 세 파일이었다.

.../product-management/
├── product-management.tsx          (메인)
├── product-management-control.tsx  (필터)
└── product-management-table.tsx    (테이블) ← 주요 변경

필터는 손대지 않았다

탭 프레임과 필터 영역부터 대조했다. 활성 탭은 border-b-2grey.10, 비활성 탭 5개는 grey.5, 폰트는 SemiBold 14px, 좌우 패딩 40px에 간격 20px이었다.

필터는 광고 상태와 1차 카테고리 셀렉트, 시작일~종료일 날짜 범위, 상품명 검색, 초기화 버튼으로 구성돼 있었다. 여기는 건드리지 않았다. 기존 구현이 이미 시안과 일치했기 때문이다.

테이블은 컬럼 구조부터 달랐다

문제는 테이블이었다. 기존 코드는 7컬럼인데 시안은 8컬럼이었다.

#기존 코드너비Figma 시안너비
1광고 상태120px상태120px
2상품명440px상품 정보272px
31차 카테고리flex-1카테고리flex-1
4상품 가격flex-1상품 가격flex-1
5판매 수수료율flex-1구매 확정 수flex-1
6노출 시작일flex-1전환율flex-1
7상세90px광고 집행 기간flex-1
8상세90px

컬럼 이름만 바뀐 게 아니라 지표 자체가 교체됐다. 판매 수수료율이 빠지고 구매 확정 수와 전환율이 들어왔다. 크기도 전반적으로 줄었다.

항목기존 코드Figma 시안
상품 이미지62×62px40×40px
행 높이78px56px
행 배경색whitergba(234,255,233,0.3)

Phase 2에서 이 내용을 Chakra UI 기준 설계서로 정리했다. 컬럼마다 어떤 컴포넌트로 렌더링할지까지 확정해둔다.

{
  "tableColumns": [
    { "name": "상태", "width": "fixed(120px)", "component": "StatusBadge" },
    { "name": "상품 정보", "width": "fixed(272px)", "structure": "ImageAsNext(40x40px) + Text(lineClamp=2)" },
    { "name": "카테고리", "width": "flex-1", "component": "Text(pre-caption-2)" },
    { "name": "상품 가격", "width": "flex-1", "component": "Text(pre-caption-1, localeString)" },
    { "name": "구매 확정 수", "width": "flex-1", "component": "Text(pre-caption-1, localeString)" },
    { "name": "전환율", "width": "flex-1", "component": "Text(pre-caption-1) + % suffix" },
    { "name": "광고 집행 기간", "width": "flex-1", "component": "Text(pre-caption-2, yyyy. MM. dd. ~)" },
    { "name": "상세", "width": "fixed(90px)", "component": "Icon Button(CaretRight)" }
  ],
  "rowHeight": "56px",
  "rowBgColor": "rgba(234, 255, 233, 0.3)"
}

설계서를 먼저 확정하고 코드를 고치는 순서라, 이 JSON이 그대로 수정 지시서가 된다.

컬럼 정의만 바꾸면 되는 구조라 30줄로 끝났다

테이블이 컬럼 배열을 받아 렌더링하는 구조여서, 실제 수정은 그 배열을 갈아끼우는 일이었다.

// BEFORE
const columns: TableColumn<MockProductManagementItem>[] = [
  {
    key: 'status',
    label: '광고 상태',
    width: { type: 'fixed', value: 120 },
  },
  {
    key: 'productName',
    label: '상품명',
    width: { type: 'fixed', value: 440 },
    render: (item) => <ImageAsNext w="62px" h="62px" />,
  },
  {
    key: 'commissionRate',
    label: '판매 수수료율',
  },
]
 
// AFTER
const columns: TableColumn<MockProductManagementItem>[] = [
  {
    key: 'status',
    label: '상태',
    width: { type: 'fixed', value: 120 },
  },
  {
    key: 'productName',
    label: '상품 정보',
    width: { type: 'fixed', value: 272 },
    render: (item) => <ImageAsNext w="40px" h="40px" />,
  },
  {
    key: 'purchaseConfirmCount',
    label: '구매 확정 수',
    render: (item) => <Text>{item.purchaseConfirmCount.toLocaleString()}</Text>,
  },
  {
    key: 'conversionRate',
    label: '전환율',
    render: (item) => {
      const conversionRate = item.clickCount > 0
        ? Math.round((item.purchaseConfirmCount / item.clickCount) * 100)
        : 0
      return <Text>{conversionRate}%</Text>
    },
  },
  {
    key: 'startedAt',
    label: '광고 집행 기간',
  },
]

전환율은 시안에 값이 그려져 있지만 저장된 필드가 아니다. 구매 확정 수를 클릭 수로 나눠 렌더링 시점에 계산했다. 분모가 0일 때를 막아두지 않으면 InfinityNaN이 화면에 찍힌다.

행 높이와 배경색은 한 곳에서 바뀐다.

// BEFORE
getRowProps={() => ({ className: 'group', h: '78px' })}
 
// AFTER
getRowProps={() => ({
  className: 'group',
  h: '56px',
  bg: 'rgba(234, 255, 233, 0.3)'
})}

Mock 데이터 인터페이스에는 conversionRate를 선택 필드로 추가했다. 렌더링 시점에 계산하는 값이라 데이터에 반드시 있어야 하는 건 아니다.

export interface MockProductManagementItem {
  conversionRate?: number
}

pnpm type-check는 통과했다. 다른 파일에 있던 기존 오류는 이번 작업과 무관한 것들이었다.

3,850 토큰으로 끝났다

Phase작업누적 토큰
0Figma 분석약 300
0.5섹션 선택약 400
0.6상세 분석약 500
1에셋 추출약 600
2설계서 작성약 1,000
2.5코드 비교약 1,200
3코드 생성약 2,000
4검증약 3,850

같은 작업을 손으로 하면 15,000 토큰이 넘게 든다. 76% 정도 줄었다.

실제 변경량은 파일 2개에 30줄 남짓이다. 컬럼 2개 추가, 1개 삭제, 이름 3개 변경, 크기 관련 3개 조정.

막힌 지점과 원인

문제원인상태
새 패턴이 오면 분기를 통째로 고쳐야 함Phase 6~7이 TABLE 같은 패턴 이름에 if-elif로 묶여 있음미해결
Figma와 코드 불일치를 눈으로 대조자동 검증 단계 없음미해결
비교 기준이 되는 코드 정보를 매번 손으로 읽음Figma 쪽만 Python으로 분석하고 코드 분석 스크립트가 없음미해결

파이프라인이 시안 쪽은 자동으로 읽는데 코드 쪽은 못 읽는 게 이번에 제일 걸렸다. 불일치 6건을 찾아낸 건 스크립트가 아니라 내 눈이었고, 페이지가 늘어나면 이 방식으로는 놓치는 게 생긴다.