안녕하세요, 로건입니다 👋
디자이너가 Figma URL을 던져주면 컴포넌트 스토리가 자동으로 생기는 — 그런 꿈을 한 번쯤 꾸지 않은 프론트는 없을 거예요. 저도 처음엔 REST API로 노드 트리만 잘 파싱하면 끝날 줄 알았습니다.
그런데 첫 자동 생성 결과를 본 순간, 197줄짜리 깨진 JSX와 마주했죠. 그날부터 알게 됐어요. 자동 변환은 출발선일 뿐이고, 본작업은 따로 있다는 걸. 이 글은 SECTION을 케이스 스토리로 쪼개고, 픽셀이 맞아 떨어질 때까지 7번씩 다시 그리던 시간에 대한 이야기입니다.
파이프라인의 큰 그림
먼저 전체 흐름을 한눈에 보고 시작할게요.
각 단계를 풀어볼게요.
휴리스틱 매핑이 시작점이다
자동 생성기의 첫 일은 figma에 그려진 이름들을 우리 디자인 시스템 컴포넌트로 매핑하는 것입니다. 휴리스틱 한 줄 한 줄이 변환 품질을 결정해요.
| Figma에서 본 이름 | 매핑 대상 | 비고 |
|---|---|---|
Top Navigation | <TopNavigation> | 정확한 PascalCase 매핑 |
switch (lowercase) | <ToggleSwitch> | 통상명 → 사내명 |
Status Bar | (삭제) | OS가 그리는 영역 |
icon_01, icon_02 | (삭제 or <Icon name="" />) | annotation node |
그리드, restricted area | (삭제) | figma annotation |
INSTANCE 노드 (Button/Primary/MD) | <Button tone="primary" size="md"> | variant 자동 추출 |
여기서 깨달은 게 하나 있어요. figma는 자유로운 캔버스라, 디자이너마다 노드 이름 규칙이 조금씩 달라요. 우리 팀은 디자이너와 합의해서 INSTANCE의 variant 이름을 props 이름과 1:1로 맞추기로 약속했어요. 그 약속 한 줄이 휴리스틱 정확도를 80%에서 95%로 올렸습니다.
SECTION을 케이스로 쪼개기
이게 처음에 가장 헷갈렸던 부분이에요. figma에는 SECTION이라는 컨테이너 노드가 있어서, 디자이너가 한 화면의 여러 케이스를 묶어 그릴 때 자주 사용합니다. 예를 들면 알림 설정 화면의 "권한 허용 / 권한 거부 / 권한 미정" 같은 상태들을 한 SECTION 안에 나란히 그려요.
자동 변환기는 이걸 어떻게 처리해야 할까요?
답은 각 FRAME을 별도 스토리로 분리하는 것이었어요. 결과적으로 한 figma SECTION이 N개 케이스 스토리가 됩니다.
// NotificationSettings.stories.tsx
export const PermissionGranted: Story = { args: { permission: "granted" } };
export const PermissionDenied: Story = { args: { permission: "denied" } };
export const PermissionUndetermined: Story = {
args: { permission: "undetermined" },
};그리고 케이스들이 공유하는 layout/data는 별도 디렉토리에 추출했어요.
src/stories/screens/NotificationSettings/
├── NotificationSettings.stories.tsx
└── _shared/
├── Layout.tsx // ScreenLayout wrapper
├── data.ts // fixture-free dummy data
└── components.tsx // 화면 안 sub-block
이 _shared/ 디렉토리 패턴이 케이스 5개 이상 되는 화면에서 코드 중복을 깔끔하게 제거해줬어요.
첫 자동 생성 결과는 거의 항상 깨진다
이게 가장 솔직한 진실이에요. 자동 변환기에 figma URL을 넣어서 나온 첫 결과는 거의 모두 한두 곳이 깨져 있습니다. 흔한 깨짐은 이런 것들이에요.
| 증상 | 원인 |
|---|---|
| 카드 우측이 잘림 | device frame (390) ≠ figma frame (360) |
| 아이콘 색이 안 먹음 | SVG loader가 color prop을 무시 |
| trailing gap이 남음 | flex-wrap + gap RN-Web 버그 |
<View> 안 children이 stretch됨 | width/height가 잘못 주입됨 |
| 폰트가 OS 기본으로 fallback | tokens.typography 매핑 누락 |
여기서 핵심은 — 이 깨짐들을 한 번 잡아두면 다음 변환에도 안 깨진다는 거예요. 그래서 자동 변환기 작업은 변환 자체가 아니라 깨짐 사례를 휴리스틱에 흡수시키는 것이 본작업이 됩니다.
시각 회귀 루프 — 3~7번 다시 그린다
자동 변환을 한 번 돌리고 끝나는 화면은 거의 없어요. 보통 이런 루프를 돕니다.

처음엔 "7번이나 돌면 자동이 아니잖아요?"라고 생각했어요. 그런데 한 화면을 손으로 처음부터 짜려면 보통 반나절이 들어요. 자동 + 7번 보정은 한 시간 안쪽이고요. 그리고 보정 한 번 한 변환 룰은 영구히 휴리스틱에 흡수되니까, 변환할수록 빨라집니다. 100번째 화면쯤 되면 첫 결과의 정확도가 95% 가까이 올라옵니다.
Storybook과 Figma를 함께 보기
각 스토리에는 figma 원본 URL을 메타데이터로 박아둡니다.
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/design/<FILE_KEY>/?node-id=<NODE_ID>",
},
}Storybook의 Designs 탭에서 옆에 figma 원본이 함께 떠요. 디자이너가 합격 검수할 때 두 화면을 같은 창에서 비교할 수 있게 되는 거죠. 작은 디테일이지만 합격 검수 시간을 절반으로 줄여준 한 줄이었습니다.
자동의 환상과 본작업
요약하면 이래요. 자동 변환은 본작업을 시작하게 해주는 도구이지, 본작업을 대체해주는 도구가 아닙니다. 본작업은 두 가지예요.
- 휴리스틱 보정: 첫 결과의 깨짐을 룰로 흡수시키기
- 케이스 설계: SECTION을 어떻게 N개 스토리로 쪼갤지 결정하기
자동이 해주는 건 그 본작업의 시간을 절반으로 줄여주는 것이에요. 그래서 자동화에 환상을 안 갖되, 자동화의 ROI는 명확하게 인정하는 게 — 그게 검증 단계에서 손에 남은 한 줄 정도의 결론입니다.
다음 편에서는 이 변환 작업을 하면서 마주친 함정 여섯 개에 대해 풀게요. 그 중에는 "사용자가 분노한 오후"도 있어요. 디자이너의 표정이 굳었던 그 순간에 대해서요.