안녕하세요, 로건입니다 👋
여러분, 6편까지 오셨다면 한 가지 분명한 사실을 알게 되셨을 거예요.
스토리를 잘 짜는 것만으로는 안전망이 완성되지 않는다는 거. 누군가 PR을 올렸을 때 그 스토리들이 자동으로 돌고, 디자인이 어디에서 어떻게 바뀌었는지 정직하게 알려주는 그물이 있어야 합니다. 이 글에서는 그 그물을 어떻게 짰는지 — Chromatic으로 시각 회귀를 자동화하는 과정에서 박아둔 단계와 정책에 대해 풀어볼게요.
처음부터 PR 머지를 막는 식으로 도입했다면 팀이 도구를 미워하게 됐을 거예요. 단계적으로 풀었습니다. 그 단계가 이 글의 골격입니다.
Chromatic — 30초 정의
먼저 모르시는 분을 위해 짧게 정리해드릴게요.
Chromatic은 Storybook의 모든 스토리를 PR마다 자동으로 픽셀 캡처하고, 이전 baseline과 diff를 비교해주는 SaaS입니다.
흐름은 단순해요.
PR마다 자동으로 도세요. 의도된 변경(디자인 변경, 새 컴포넌트)이면 Accept로 baseline에 흡수되고, 의도 못 한 변경(회귀)이면 Deny로 머지를 막을 수 있습니다.
도입은 한 번에 풀지 않는다
이게 가장 강조하고 싶은 부분이에요. 기술적으로 Chromatic을 PR마다 도는 자동화로 만드는 건 한나절이면 끝납니다. CLI 한 줄 추가하고 GitHub Action에 token 등록하면 되거든요. 그런데 팀이 그 도구와 함께 일하는 근육을 만드는 데는 한 달 가까이 걸렸어요.
| 단계 | trigger | 정책 | 기간 |
|---|---|---|---|
| Pilot | 수동 (chromatic --dry-run) | 결과 보고만, accept 불필요 | 2주 |
| Phase 2 | PR마다 자동 | --exit-zero-on-changes, deny만 알림 | 1개월 |
| Phase 3 | PR마다 자동 | deny가 있으면 status check fail → 머지 블록 | 안정화 후 |
각 단계가 왜 필요했는지 풀어볼게요.
Pilot — "이 도구가 우리 코드베이스에서 작동하는가"
처음 2주는 사람의 워크플로에 전혀 영향을 주지 않는 상태로 돌렸어요.
# CI에 추가, 하지만 dry-run
chromatic --project-token=$CHROMATIC_PROJECT_TOKEN --dry-run목표는 기술 검증이에요. 우리 빌드에서 storybook이 잘 떨어지는지, snapshot이 정확히 잡히는지, false positive가 얼마나 발생하는지를 조용히 측정합니다. 이 시기에 발견된 false positive 패턴이 두 종류였어요.
- styled-components 잔재 — className hash가 매번 달라져서 모든 스토리가 빨갛게 떨어짐 → 2편의
StyleSheet.create정책으로 해결 - Reanimated worklet의 unhandled error — headless chrome에서
requestAnimationFrame이 없어서 던지는 에러가 캡처에 빨갛게 잡힘 → 글로벌 ignore 정책
만약 Pilot 없이 바로 Phase 3로 갔으면, false positive로 팀이 매일 "또 빨갛네 진짜 회귀야 아냐?" 같은 대화를 했을 거예요. 그 마찰이 누적되면 안전망 자체를 미워하게 됩니다.
Phase 2 — "보고만 받자"
다음 한 달은 PR마다 자동으로 돌게 하되, 머지는 막지 않았어요.
chromatic --exit-zero-on-changes이 한 줄이 핵심이에요. --exit-zero-on-changes는 변경이 잡혔어도 CI를 성공으로 처리합니다. 그러니까 PR 작성자가 의식적으로 Chromatic 대시보드를 열어보고 결정해야 해요. "어, 이거 회귀네" → 코드 수정. "이건 의도된 변경" → Accept.
| PR check | 정책 |
|---|---|
| ✅ lint | 필수 |
| ✅ typecheck | 필수 |
| ✅ unit tests | 필수 |
| ✅ chromatic | 탐지만, 머지 차단 안 함 |
이 시기에 팀이 "Chromatic 대시보드를 열어보고 decision하는 근육"을 만들었어요. 한 달쯤 지나니까 PR 올린 사람이 자기 변경의 시각적 결과를 자동으로 확인하는 습관이 굳어졌습니다.
Phase 3 — "머지를 막는다"
근육이 충분히 만들어지고 false positive가 한 자릿수 PR 빈도로 떨어진 다음에야, 마침내 머지 블로킹을 켰어요.
chromatic # --exit-zero-on-changes 제거이제 deny가 남아있는 채로는 머지가 안 됩니다. Accept를 명시적으로 누르거나, 코드를 수정해서 diff를 해소해야 해요. 이 시점에서 안전망이 물리적으로 작동하기 시작합니다.
Baseline 관리 — main에서만 자동 accept
Chromatic의 핵심 개념 하나가 baseline이에요. "이게 정상 상태"의 스냅샷을 가리키죠. baseline이 잘못 관리되면 그물 자체가 무의미해집니다.
우리 팀은 한 줄 정책을 박아뒀어요.
# .github/workflows/chromatic.yml (요약)
- name: Chromatic
run: |
if [[ "$BRANCH" == "main" ]]; then
chromatic --auto-accept-changes
else
chromatic
fi
main에 머지된 변경만 자동으로 baseline에 흡수된다. 기능 브랜치에서는 절대 자동 accept 하지 않는다.
이 정책이 베이스라인을 민주적으로 갱신해줍니다. 기능 브랜치에서 자동 accept를 허용하면, 누군가 회귀를 의도된 변경으로 오해해서 accept 하는 순간 베이스라인이 오염돼요. 한 번 오염되면 그 회귀가 영구히 baseline에 들어앉아서 다음 PR들이 "정상"으로 잘못 판정합니다.
글로벌 정책 두 줄
운영하면서 박아둔 글로벌 정책 두 줄을 공유해드릴게요. 이 두 줄이 false positive와 운영 마찰을 가장 크게 줄여줬어요.
1) dangerouslyIgnoreUnhandledErrors: true
// .storybook/preview.ts
export const parameters = {
chromatic: {
dangerouslyIgnoreUnhandledErrors: true,
},
};이름이 무서워 보이지만, Reanimated 같은 RN-only 라이브러리가 headless chrome 환경에서 던지는 unhandled error를 무시하는 안전한 정책이에요. RN-Web에서 worklet은 어차피 실행 안 되니까 그 에러는 항상 false positive입니다.
2) TurboSnap
chromatic --only-changedTurboSnap은 git diff를 분석해서 영향 받은 스토리만 캡처해줘요. 1편에서 봤듯 PR 한 번에 보통 5개 미만의 컴포넌트만 바뀌는데, 200개에 가까운 스토리를 매번 풀 캡처하는 건 낭비예요. TurboSnap을 켜고 나서 스냅샷 사용량이 큰 폭으로 줄었고 CI 시간도 1분 정도 빨라질 것으로 측정됐습니다.
비용 — 무료 플랜에 깔끔히 들어옴
3편에서 페이지 스토리 106개를 지운 결정이 여기서 또 한 번 빛을 발해요.
| 항목 | 우리 팀 사용량 | 무료 플랜 한도 |
|---|---|---|
| 월 스냅샷 | ~3,200 | 5,000 |
| 동시 빌드 | 1 | 5 |
| TurboSnap 절약분 | ~60% | (TurboSnap은 무료 플랜에서도 사용 가능) |
만약 페이지 스토리 106개를 유지했다면 무료 플랜을 진작 넘었을 거예요. 그러면 도구 비용 자체가 도입 명분을 흔들고, 결국 안전망을 포기하는 결정으로 이어질 수도 있었습니다. 안 짠 결정이 자동화 비용까지 절약해준 셈이에요.
PR 작성자의 워크플로
마지막으로, 모든 게 합쳐졌을 때 PR 작성자가 실제로 어떤 경험을 하는지를 그려볼게요.

이 흐름이 정착되고 나면, 디자이너가 시각 검수에 들이는 시간이 극적으로 줄어들어요. 같은 대시보드에서 diff를 같이 보면서 결정할 수 있게 되니까요.
도입 결정하실 분께
만약 여러분이 지금 Chromatic 도입을 검토 중이라면, 이 글에서 가장 중요한 메시지 한 줄을 드릴게요.
도구는 1주일이면 도입되지만, 도구와 함께 일하는 문화는 한 달 걸립니다. 한 달 동안 마찰 없이 사람이 도구와 친해질 수 있는 단계를 만들어주세요. Pilot → Phase 2 → Phase 3 같은 점진적 도입은 단순한 운영 팁이 아니라, 도구의 ROI를 가장 빠르게 끌어올리는 방법이에요.
다음 편에서는 이 안전망 위에 한 칸을 더 얹은 이야기를 풀어볼게요. Plasmic 같은 SaaS 대신 사내에서 직접 만든 page builder — Visual Builder에 대해서요. Storybook 안에 박고, 결과는 JSON으로만, 서버 0개로 어떻게 짰는지 그 창작 기록을 공유해드릴게요.