안녕하세요, 로건입니다 👋
어느 날 오후, 저는 페이지 스토리 106개를 한 번에 지웠습니다.
99% 커버율까지 갔던 작업이었어요. 메인 화면, 상세 페이지, 설정, 알림센터까지 — RN 앱의 거의 모든 화면을 1:1로 매칭해서 Storybook에 띄우는 작업이었습니다. mock store, navigation decorator, fixtures까지 다 짜놓고 검토 합격만 기다리던 상태였어요. 그런데 어느 순간부터 이게 정말 가치 있는가라는 질문이 머리를 떠나지 않았고, 결국 일괄 삭제를 결심했습니다.
아까운 마음이 없었다면 거짓말이지만, 지금 보면 그 결정이 시리즈에서 가장 잘한 일이에요. 안 짜는 게 짜는 것보다 어려울 때가 있더라고요.
처음의 야심
처음엔 이런 그림이었어요.
"디자이너가 어떤 화면을 봐도, 코드를 열지 않고 Storybook 사이드바에서 그 화면을 정확히 볼 수 있게 하자."
매력적이지 않나요? 모든 화면이 색인된 카탈로그 같은 상태. 디자이너는 디자인 변경 전에 현재 모습을 즉시 확인할 수 있고, QA는 회귀를 시각적으로 추적할 수 있고, 개발자는 디바이스 부팅 없이 화면을 본다는.
src/stories/pages/
├── Home/
│ ├── Default.stories.tsx
│ ├── EmptyState.stories.tsx
│ └── ErrorState.stories.tsx
├── Notifications/
│ ├── Empty.stories.tsx
│ ├── HasItems.stories.tsx
│ └── PermissionDenied.stories.tsx
├── Settings/
│ ...
└── (106개)
작업은 잘 진행됐어요. 첫 주에 30개, 둘째 주에 60개, 셋째 주에 90개. 화면 안에 들어가는 mock 데이터들을 fixture 디렉토리에 모아두고, Redux store를 decorator로 감싸고, navigation context를 fake로 주입하고요. 다 짜고 나면 정말 멋질 것 같았습니다.
그런데 mock이 무한 회귀로 부풀더라고요
문제는 한 화면을 띄우는 데 필요한 mock의 수가 점점 늘어난다는 거였어요.
100번째 화면쯤 되니까 작업의 70%가 fixture 보수 작업이 됐어요. 한 fixture가 production 스키마와 어긋나면, 그걸 참조하는 스토리 다섯 개가 동시에 깨졌고요.
결정타였던 한 줄
어느 회의에서 이 말이 나왔어요.
"그 mock이 정말 production이랑 같다고 어떻게 보장하죠?"
여기서 깨졌어요. mock은 production을 흉내내지만, 흉내는 본질이 아니에요. mock fixture에 있던 user.subscription.status: "active"가 어느 날 production에서 "trialing" 같은 새 케이스가 추가되면? mock은 그대로인 채 production만 바뀝니다. 그러면 Storybook에서는 분기가 안 보이고, 디바이스 QA로 가서야 발견되죠.
이게 무슨 뜻이냐 하면, 검수자가 Storybook에서 "괜찮네"라고 합격을 내준 화면이, production에서는 다른 모습일 수 있다는 거예요. 그 순간 페이지 스토리의 검수 가치는 0이 됩니다.
일괄 삭제
회의 다음 날 PR을 올렸어요.
✂️ delete src/stories/pages/ (106 files)
✂️ delete src/stories/mocks/ (32 files)
✂️ delete src/stories/fixtures/ (47 files)
+0 −12,438
5,000줄 가까이 사라졌습니다. 며칠 동안 마음이 무거웠어요. 그런데 1주일이 지나니까 분명해지더라고요. PR 머지 속도가 빨라졌어요. fixture 보수 PR이 사라지니까요. CI 시간도 절반으로 줄었고, Chromatic 스냅샷도 줄어서 무료 플랜 안에 깔끔히 들어왔습니다.
| Phase | 상태 | 결정 이유 |
|---|---|---|
| Phase 1 — 컴포넌트 카탈로그 | ✅ 완료 | ROI 명확. 회귀 자주 발생 영역. |
| Phase 2 — 페이지 1:1 매칭 | ⛔ 폐기 | mock 무한 회귀, 검수 가치 0. |
| Phase 3 — Figma 자동 변환 | ✅ 완료 | 본질 = 디자인 검수. mock 없음. |
| Phase 4 — Chromatic 시각 회귀 | ✅ 완료 | 자동화 ROI 명확. |
| Phase 5 — Visual Builder 보조 도구 | 🔄 진행 | 정적 런타임으로 피벗 후 안정화 중. |
그래서 페이지 검수는 어떻게 하느냐고요
페이지 검수의 책임은 이렇게 재분담됐어요.
페이지 스토리가 책임지려던 영역을 다른 그물들이 각자 본질에 맞게 나눠 가져갔어요. Storybook은 진짜 잘 하는 것만 하고, 못 하는 것은 다른 도구에 맡기는 거죠. 1편의 표가 여기서 다시 의미를 갖습니다.
안전망은 모든 칸을 채우려는 것이 아니라, 각 칸의 주인이 누구인지 정해주는 것입니다.
일반화 — ROI가 안 나오면 빠르게 죽인다
이 결정에서 가장 크게 배운 건 기술적인 것보단 사람의 결정 근육이었어요.
99% 다 한 작업을 지우는 것은, 1% 남은 작업을 완성하는 것보다 훨씬 어렵습니다. 매몰비용이 매번 발목을 잡거든요. 그런데 지금까지 쓴 시간을 돌이킬 수는 없지만, 앞으로 들 시간을 줄일 수는 있어요. "여기서 멈추면 지금까지 한 게 다 헛수고"라는 생각은 거의 항상 함정입니다. 헛수고가 아니라, 거기까지 가봐야 알 수 있는 진실에 도달한 거니까요.
다음 편에서는 살아남은 다른 한 쪽 이야기를 해볼게요. Figma에서 코드까지 자동 변환을 시도하면서, 첫 결과물로 받은 197줄짜리 깨진 JSX와의 시간에 대해서요.