스크린샷과 에셋7 분 읽기

제 테마에는 보라가 없습니다. 스토어 스크린샷에는 있었습니다

파랑 accent 앱인데 선택된 카테고리 칩이 라벤더로 찍혀 스토어에 나갔습니다. 색을 잘못 쓴 게 아니라, 아예 정의하지 않은 슬롯이 있었습니다.

#gotchas#android#screenshots#reality-check#verification
Left panel: a color scheme defining six slots, all brand blue. Right panel: the screen reading three slots that were never defined, filled with Material default lavender.
정의한 슬롯은 파랑으로 나옵니다. 정의하지 않은 슬롯도 뭔가로는 나옵니다.

질문 하나로 시작하겠습니다. 당신의 앱 테마는 화면이 실제로 읽는 색을 전부 정의하고 있습니까?

저는 그렇다고 믿고 있었습니다. FreshSave 안드로이드의 색 스킴은 브랜드 파랑 기준으로 잘 정리돼 있었고, 앱을 열면 파랑이 보였습니다. 테마 작업은 끝난 일이었습니다.

그런데 스토어에 올라간 스크린샷에서, 선택된 카테고리 칩이 라벤더였습니다.

어디에도 보라를 쓴 적이 없습니다

색 상수를 전부 뒤졌습니다. 보라 계열 값은 하나도 없었습니다. 칩을 그리는 코드에도 하드코딩된 색이 없었습니다. 색을 잘못 쓴 게 아니었습니다.

lightColorScheme(...) 호출부를 열고 나서야 보였습니다. 채워져 있는 인자는 여섯 개였습니다.

primary, secondary, tertiary, background, surface, error

그리고 화면이 실제로 읽는 슬롯은 그 여섯 개가 아니었습니다.

secondaryContainer   <- FilterChip 의 선택 상태 배경
onSurfaceVariant     <- 보조 텍스트
outline              <- 카드·입력창 외곽선

이 셋은 스킴에 없습니다. 그러면 어떻게 될까요. 에러가 아닙니다. lightColorScheme() 은 생략된 인자에 Material 기본값을 조용히 넣습니다. 경고도, 컴파일 오류도, 린트 경고도 없습니다. 그리고 M3 기본 중립 팔레트에는 보라가 섞여 있습니다.

즉 파랑 앱의 칩이 라벤더로 칠해진 건 버그가 아니라 명세대로 동작한 결과였습니다. 제가 아무 말도 안 했으니 기본값이 말을 한 겁니다.

이 함정이 특히 안 보이는 이유

정의한 슬롯은 정확히 브랜드 색으로 나옵니다. 그래서 앱을 열어보면 "테마가 적용됐다"는 감각이 확실하게 생깁니다. 그 감각이 안 쓰는 슬롯안 정의한 슬롯을 구분할 필요를 없애 버립니다. 둘 다 스킴에 안 적혀 있으니 코드에서는 똑같아 보이고, 화면에서만 갈립니다.

게다가 어긋남이 미묘합니다. 형광 초록이었다면 즉시 잡혔을 겁니다. M3 기본 중립색은 회보라에 가까워서, 파랑 accent 옆에 놓이면 "약간 탁한 파랑" 처럼 보입니다. 스크린샷을 넘겨보면서도 그냥 지나갑니다.

여기서 당신이라면?

이 시점에 선택지가 둘 있습니다.

  1. 라벤더가 보이는 화면을 하나씩 찾아 그 자리에 색을 지정한다.
  2. 화면이 읽는 슬롯 전체를 뽑아 스킴과 대조한다.

1번은 다음 화면에서 또 나옵니다. 눈에 띈 것만 고치는 방식이라, 스크린샷을 안 찍은 화면은 그대로 남습니다. 저는 2번으로 갔습니다.

읽는 슬롯을 grep 해서 대조합니다

방법은 단순합니다. 코드가 참조하는 슬롯 이름을 전부 뽑습니다.

grep -rho 'MaterialTheme\.colorScheme\.[A-Za-z]*' app/src | sort -u

그 목록과 lightColorScheme(...) / darkColorScheme(...) 의 인자 목록을 대조합니다. 차집합이 곧 기본값으로 새어 나오는 슬롯입니다.

빠지는 자리는 거의 정해져 있습니다.

  • 컨테이너 계열: secondaryContainer, onSecondaryContainer, primaryContainer, tertiaryContainer
  • 중립 계열: onSurfaceVariant, outline, surfaceVariant, outlineVariant

값은 이미 있는 accent 에서 파생시킵니다. 여기서 새 색을 발명하면 팔레트가 하나 더 생기는 것이고, 다음 사람이 또 같은 질문을 하게 됩니다.

자가진단 3개

  • MaterialTheme.colorScheme. 참조를 grep 한 목록과, 스킴에 실제로 넘긴 인자 목록의 차집합이 비어 있습니까?
  • 라이트/다크 양쪽 스킴에 같은 검사를 돌렸습니까? 한쪽만 채워 두는 경우가 흔합니다.
  • 지금 스토어에 올라가 있는 스크린샷에, 팔레트에 없는 색이 찍혀 있지 않습니까?

솔직한 부분

가장 아픈 건 이게 이미 나간 뒤에 발견됐다는 점입니다. 앱을 고치면 다음 빌드부터는 파랑이 나옵니다. 하지만 스토어에 올려둔 스크린샷은 저절로 안 고쳐집니다. 앱 수정과 별개로 다시 캡처해서 다시 올리는 작업이 남습니다. 관련해서 스크린샷에 페이월이 찍혔다에서도 같은 종류의 뒤처리를 했었습니다.

그리고 이건 대비 검사 같은 자동 점검으로 안 걸립니다. 라벤더도 대비는 통과하니까요 — 대비 검사는 전부 통과했습니다, 화면은 죽어 있었습니다와 같은 계열의 실패입니다. 색이 "틀렸다"는 판정은 결국 팔레트 목록과의 대조로만 나옵니다.

지금 딱 하나만 해보세요. 위의 grep 한 줄을 돌리고, 나온 슬롯 목록을 테마 파일과 눈으로 맞춰보시기 바랍니다. 차집합이 비어 있던 적이 있으신가요? 저는 처음 돌렸을 때 세 개가 나왔습니다.

유튜브

제 테마에는 보라가 없습니다. 스토어 스크린샷에는 있었습니다

관련 글