단위 테스트 통과했는데 왜 UI는 박살 났을까? 시각적 회귀 테스트가 필요한 이유
"초록색 체크박스"의 배신
금요일 오후 4시 55분. 결제 모듈 리팩토링을 마치고 npm test를 돌립니다.
PASS components/Checkout.test.tsx
✓ renders checkout button (23ms)
✓ handles submit action (15ms)
✓ displays success message (12ms)터미널에 뜬 초록색 체크 표시들을 보니 마음이 평온해집니다. "완벽해." 배포 버튼을 누르고 팀원들과 주말 인사를 나눈 뒤 퇴근합니다.
오후 6시, 전화벨이 울립니다. "모바일에서 결제 버튼이 안 보여요!!"
당신은 멘붕에 빠집니다. 다시 테스트를 돌려봅니다. 여전히 전부 통과(PASS)입니다. 도대체 왜? 논리적으로는(Logically) 버튼이 존재하니까요. DOM에도 있고, 클릭 이벤트도 달려 있고, 텍스트도 맞습니다.
하지만 눈으로 보면(Visually) 이야기가 다릅니다. 꼬여버린 CSS z-index 때문에 버튼이 하단 푸터 뒤에 숨어버린 겁니다. Jest는 눈이 없습니다. 코드는 맞지만, 화면은 틀린 거죠.
코드 테스트만으로는 부족한 이유
Jest, React Testing Library, 혹은 Cypress 같은 도구들은 주로 요소가 있는지, 기능하는지를 봅니다. 어떻게 생겼는지는 잘 안 봅니다.
그들이 알려주는 것:
- ✅
<h1>태그가 한 개 있다. - ✅ API가 200(성공)을 반환했다.
- ✅
isOpen변수가 참(True)이다.
그들이 절대 모르는 것:
- ❌ CSS 그리드가 깨져서 레이아웃이 겹쳤다.
- ❌ 글자색이랑 배경색이 똑같아서 글씨가 안 보인다.
- ❌ 아이폰 SE에서만 버튼이 화면 밖으로 밀려났다.
이 빈틈을 메워주는 게 바로 **시각적 회귀 테스트(Visual Regression Testing)**입니다.
'틀린 그림 찾기'의 자동화
시각적 테스트는 복잡하지 않습니다. 쉽게 말해 **"틀린 그림 찾기"**를 기계가 해주는 겁니다.
작동 원리
- 찍기: 봇이 사이트에 들어가서 스크린샷을 찍습니다.
- 겹치기: 이전에 '정상'이라고 판정한 스크린샷 위에 겹쳐봅니다.
- 찾기: 픽셀이 달라진 부분을 빨간색으로 표시합니다. (이걸 'Diff'라고 부릅니다)
expect(button).toHaveStyle('margin-top: 10px') 같은 코드를 수백 줄 짜는 대신, 그냥 빨간색으로 표시된 이미지만 보면 됩니다. 이상하면, 그게 버그입니다.
"1분 지났다고 에러라니요?" (노이즈 문제)
시각적 테스트를 도입할 때 가장 큰 불만은 **"너무 예민하다"**는 겁니다. "시간이 10:00에서 10:01로 바뀌었다고 테스트 실패가 뜨면 어떡해요?"
SiteSnapshot 같은 똑똑한 도구들은 이렇게 해결합니다:
- 마스킹(Masking): 날짜, 광고 배너, 매번 바뀌는 ID 같은 건 자동으로 모자이크 처리해서 무시합니다.
- 임계치(Threshold) 설정: "1% 미만 차이는 봐줘" 같이 설정할 수 있어서, 미세한 폰트 렌더링 차이 때문에 알림이 울리지 않게 합니다.
결론: 두 발 뻗고 잡시다
단위 테스트 없이 코드 배포하는 건 상상도 못 하시죠? 그런데 왜 화면(UI)은 테스트 없이 배포하시나요?
배포 파이프라인에 시각적 테스트 한 줄만 추가하세요. 당신이 의도한 화면 그대로 사용자에게 보인다는 확신을 가질 수 있습니다. 더 이상 "z-index" 때문에 주말에 불려 나갈 일은 없습니다.
Is your site visually healthy?
Don't guess. Run a deeper visual scan right now and catch hidden bugs before your users do.