Android 개발/Jetpack Compose

4px 패딩 회귀를 눈으로 못 잡던 나, Compose 스크린샷 테스트로 넘긴 이야기

stackD 2026. 9. 26. 18:00
반응형

 

얼마 전에 릴리즈 하나 올리기 전이었어요. 저번 버전 캡처랑 이번 빌드를 나란히 띄워놓고 4px 패딩이 밀렸나 안 밀렸나 눈으로만 대조하고 있었습니다. 딱 5분, 매번 이러고 있더라고요.

 

근데 이게 문제가, 사람 눈은 생각보다 못 미덥습니다. 4px 정도 밀린 여백, 다크모드에서 살짝 흐려진 글자색, 이런 건 두 창을 아무리 째려봐도 그냥 넘어가버리거든요. 그래서 안드로이드 스튜디오(Android Studio)에서 굴러다니던 @Preview를 그대로 재활용해서 Compose 스크린샷 테스트를 붙여봤습니다.

 

4px 여백을 육안으로 잡던 자리가 왜 뚫리나

제가 통과시킨 UI가 사실은 통과가 아니었던 적이 많았어요.

 

대표적인 게 다크모드 대비였습니다. 배경과 글자 대비가 4.3:1로 떨어졌는데, WCAG 기준으로는 본문 텍스트가 4.5:1 이상이어야 해요. 이 0.2 차이를 사람 눈이 잡을 수 있을까요? 저는 못 잡았습니다. "느낌상 잘 보이는데?" 하고 그냥 넘겼거든요.

 

폰트를 1.5배로 키우면 버튼 안 글자가 잘리는 것도 마찬가지였습니다. 기본 배율만 확인하고 릴리즈했다가, 접근성 설정 켠 분들 화면에서 텍스트가 뭉텅 잘려나간 걸 뒤늦게 알았네요.

 

이미지 비교는 픽셀 단위라 이런 미세한 변화를 첫 프레임에 실패로 띄웁니다. 사람이 "느끼지 못하는 회귀"를 기계가 잡아주는 거죠.

 

 

com.android.compose.screenshot 플러그인 세팅 순서

이 도구 정식 이름은 Compose Preview Screenshot Testing, 2024년 구글 I/O에서 공개됐어요. 플러그인 ID는 com.android.compose.screenshot 입니다.

 

버전 요건은 계속 바뀌니까 공식 문서를 꼭 확인하셔야 되는데요, 대략 이런 조합으로 알려져 있어요.

 

  1. 코틀린(Kotlin)은 1.9.20 이상, AGP는 8.5.0-beta01 이상. 예시에 따라 AGP 8.6.1, 코틀린 2.0.21 조합도 돌아갑니다.
  2. 플러그인 버전은 composeScreenshot="0.0.1-alpha08" 처럼 아직 알파라 고정해두는 게 안전합니다. 다만 알파는 자주 바뀌니까 최신 알파 버전은 공식 문서에서 한번 확인하고 넣으세요.
  3. gradle.properties에 android.experimental.enableScreenshotTest=true 플래그, screenshotTest 소스셋, ui-tooling 의존성 이 세 가지를 순서대로 넣으면 됩니다.

 

진짜 마음에 드는 건 실행 방식이었어요. 에뮬레이터 없이 호스트 쪽에서 렌더링하거든요. 기기 부팅 기다릴 필요가 없습니다.

 

그리고 이게 킬러 포인트인데, 이미 짜둔 @Preview를 폴더만 screenshotTest 쪽으로 옮기면 그대로 테스트 자산이 됩니다. 프리뷰 짜놨으면 테스트는 거의 공짜라는 얘기예요.

 

레퍼런스 갱신과 HTML diff 리포트 읽는 법

기준 이미지 만드는 건 updateDebugScreenshotTest, 검증은 validateDebugScreenshotTest 로 돌립니다. 로컬 태스크명은 recordDebugScreenshotTest / verifyDebugScreenshotTest 로 뜨는 경우도 있으니, 프로젝트 태스크 목록 한번 보시는 게 정확해요.

 

검증이 실패하면 HTML 리포트가 나오는데, 기준 이미지·실제 이미지·차이 이미지 세 개를 나란히 보여줍니다. 여기서 판단이 갈립니다.

 

내가 의도한 UI 수정이면 update 명령으로 기준 이미지를 갱신하면 되고, 의도 안 한 변화면 회귀니까 코드를 되돌리면 돼요. 리포트의 diff 이미지에 빨간 영역이 뜨면 딱 그 자리가 밀린 거죠.

 

다만 맞바꿈이 있습니다. UI를 바꿀 때마다 기준 이미지를 다시 찍고 리뷰해야 하고, PR에 이미지 diff가 무더기로 올라올 수 있어요. 이걸 감당할 팀인지는 미리 생각해보셔야 합니다.

 

 

@PreviewTest로 폰트 스케일·다크모드 한 번에 스냅샷

여기서부터가 진짜 재밌더라고요. @Preview 파라미터를 여러 개 걸면 한 컴포저블에서 라이트/다크, 폰트 1.0배/1.5배를 조합해 여러 장을 한꺼번에 찍습니다. 조합 수만큼 기준 이미지가 늘어나긴 하지만, 육안으로는 도저히 못 챙기던 커버리지가 확보돼요.

 

@PreviewTest를 붙이면 기존 프리뷰를 스크린샷 테스트로 승격시킬 수 있는데, 최근 버전에서는 이게 붙은 프리뷰만 실행되고 안 붙은 건 아예 안 도니까, 이제는 선택이 아니라 기본으로 달아둔다고 보시는 게 맞아요. 안드로이드 스튜디오 Otter Feature Drop 계열에서는 이게 붙은 자리 IDE 왼쪽 여백에 초록색 실행 아이콘이 떠서, IDE 안에서 바로 실행하고 실패 분석까지 된다고 해요.

 

주의하실 게 하나 있는데요, OS별 폰트·배율 차이로 멀쩡한데도 실패로 뜨는 false positive가 생길 수 있습니다. 허용 오차(tolerance) 설정, 무시 영역(ignore region), 폰트·테마 고정 같은 방어를 안 해두면 "또 빨간불이네" 하고 팀 전체가 리포트를 무시하는 사태가 옵니다. 그럼 도구 도입한 의미가 없어져요.

 

Mac 로컬 초록불, Linux CI 빨간불 폰트 지뢰

여기서 제가 제일 크게 삽질했어요.

 

로컬에서는 전부 초록불이었는데, CI에 validateDebugScreenshotTest 올리자마자 화면이 전부 빨간불이 됐거든요. 코드는 한 줄도 안 건드렸는데 말이죠.

 

원인은 코드가 아니라 렌더링 환경이었습니다. 제 맥(Mac)에서 찍은 기준 이미지랑, 리눅스(Linux) CI가 그리는 이미지가 안티앨리어싱이랑 폰트 처리가 미묘하게 달랐어요. 글자 테두리 몇 픽셀 차이로 전부 실패 처리된 거죠.

 

해법은 기준 이미지를 CI와 똑같은 환경, 그러니까 같은 도커(Docker) 이미지 안에서 찍어 통일하는 겁니다. 이 단계가 튜토리얼에는 잘 안 나와서 초보분들이 여기서 제일 많이 밟는 지뢰예요. 개인적으로는 처음 도입할 때 이 부분부터 세팅해두시라고 권하고 싶습니다. 안 그러면 저처럼 하루 날립니다.

 

그리고 이게 구글 공식 도구인데도, 서드파티인 Paparazzi가 더 빠르다는 비교 결과가 여럿 나와 있어요. 대규모 CI에서는 이 속도 차이가 부담될 수 있습니다. 상호작용이나 상태 변화 테스트도 안 되고(정적 스냅샷이라서요), XML 뷰는 커버 못 하니까 하이브리드 앱이면 Roborazzi 쪽이 나을 수 있어요. 나중에 다른 도구로 갈아탈 여지를 두고 싶으면 ComposablePreviewScanner로 프리뷰를 자동 수집해 Paparazzi/Roborazzi와 엮는 방법도 있습니다.

 

솔직히 소규모 앱이면 이 세팅 자체가 과할 수 있어요. 화면 몇 개 안 되면 그냥 디자인 QA가 빠를 때도 있습니다.

 

그래도 이미 @Preview를 잔뜩 짜둔 프로젝트라면 이야기가 달라집니다. UI 회귀 감시를 사실상 공짜로 얹는 셈이니까요. 대신 기준 이미지는 반드시 CI와 같은 환경에서 찍어라, 이 한 줄만 몸에 박아두고 시작하시는 게 좋겠습니다. 5분 눈알 굴리기를 CI 초록불 하나로 넘긴 대신, 그 5분은 이제 진짜 코드 볼 시간으로 돌아왔습니다.

 

 

반응형
LIST