Skip to content

test(mindrecord): screenshotTest 소스셋을 세우고 추억 공간 baseline 을 넣는다 (#1131) - #1154

Merged
1hyok merged 4 commits into
developfrom
test/1131-mindrecord-screenshot
Aug 26, 2026
Merged

1hyok merged 4 commits into
developfrom
test/1131-mindrecord-screenshot

Conversation

@Sadturtleman

Copy link
Copy Markdown
Collaborator

#1131 의 인프라 + 추억 공간을 담는다. 작성 화면 2개는 뺐다 — 아래 「왜 나눴나」.

무엇을

렌더를 위해 MemorySpaceContent 를 private → internal 로 열었다. 이 모듈의 상태 없는 본문 컴포저블이 화면당 하나뿐이라 다른 선택지가 없었고, feature:receiver 의 MasterKeyScreenContent 가 같은 형태다.

첫 사이클에 결함이 하나 잡혔다 — #1153

360dp 에서 「돌아가기」 버튼이 헤더 부제(「화면을 드래그해 공간을 둘러보세요」)와 겹친다. 기본 크기에서는 여백이 남는다.

memorySpaceScreenErrorCompactScreenshot 을 비롯한 compact 3장이 그 겹친 상태를 담고 있다. 감시 장치를 먼저 넣고 수정은 #1153 에서 한다(#1128 이 OnboardingTermsScreen 에서 택한 것과 같은 순서). 고친 뒤 이 3장을 갱신해야 한다.

왜 나눴나 — 작성 화면 2개는 후속

이슈가 든 DiaryWriteScreen·DailyQuestionWriteScreen 은 상태 없는 본문 컴포저블이 없다. 화면 함수가 viewModel = hiltViewModel() 을 직접 받고 피커·시트 상태를 안에 들고 있어, baseline 을 붙이려면 먼저 content 를 뽑아내는 리팩터가 필요하다(그 화면들의 기존 @Preview 가 «ViewModel 의존이 있어 Preview 는 비워둠» 인 이유이기도 하다).

그 두 파일은 지금 제 열린 PR 둘(#958 · #1121)이 건드리는 중이라, 지금 뽑아내면 셋이 같은 자리에서 충돌한다. 그 PR 들이 들어간 뒤 후속으로 올리겠다 — 이 PR 은 #1131 을 닫지 않는다.

검증

baseline 생성·검증 모두 screenshot-baseline 절차(docker linux/amd64)를 따랐다.

:feature:mindrecord:presentation:updateScreenshotTest    BUILD SUCCESSFUL (6m 14s) → PNG 6장
:feature:mindrecord:presentation:validateScreenshotTest  (같은 컨테이너에서 재검증)
게이트 결과
compileDebugScreenshotTestKotlin BUILD SUCCESSFUL
전 모듈 testDebugUnitTest BUILD SUCCESSFUL

실기동 QA: 해당 없음. 프로덕션 동작 변경이 없다 — 가시성 완화 한 줄과 테스트 소스셋뿐이다.

Sadturtleman and others added 2 commits August 25, 2026 23:21
이 모듈에는 시각 회귀 baseline 이 하나도 없었다. 추억 공간은 verticalScroll·Lazy*·
Pager 가 없어 세로가 모자라면 그대로 잘리는데, 실기 QA 가 돌던 화면(446x967dp)은
국내 보급형보다 넓어 잘림이 구조적으로 안 잡혔다.

상태 3종(기록 있음·0건·조회 실패)을 기본 크기와 360x800dp 양쪽에서 고정한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docker(linux/amd64) 에서 생성했다. 상태 3종 × 기본/360x800dp.

compact 3장은 좁은 화면에서 「돌아가기」가 헤더 부제와 겹친 상태를 그대로 담고 있다 —
감시 장치를 먼저 넣고 수정은 #1153 에서 한다. 고친 뒤 이 3장을 갱신해야 한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Sadturtleman

Copy link
Copy Markdown
Collaborator Author

컨테이너 validate 통과를 확인했습니다 — 스크린샷 CI 는 사전 확정입니다.

docker run ... ./gradlew :feature:mindrecord:presentation:validateScreenshotTest
BUILD SUCCESSFUL in 3m 57s

생성(6m 14s)과 검증(3m 57s) 모두 같은 afternote-screenshot:latest(linux/amd64) 에서 돌렸습니다.

@1hyok 1hyok left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경 요청

세운 것들은 좋다. 이 모듈에 baseline 이 하나도 없었다는 것, 상태 3종을 기본·좁은 화면 양쪽으로 가른 것, COMPACT_DEVICE_SPEC 의 dpi 근거를 주석으로 남긴 것, internal 승격의 이유와 선례(MasterKeyScreenContent)를 밝힌 것, 작성 화면 2개를 뺀 판단과 「이 PR 은 #1131 을 닫지 않는다」까지 — 범위 결정에 군더더기가 없다.

막는 것은 둘이다. 둘 다 baseline 이 감시 자산으로 서지 못한다는 같은 성질이다.

  1. [blocking] CI 가 이 모듈을 한 번도 돌지 않는다 — 초록인 이유는 통과가 아니라 부재다 → build.gradle.kts:16 인라인 코멘트.
  2. [blocking] empty baseline 2장이 앱에 없는 문구를 담고 있다 → MemorySpaceScreenScreenshotTest.kt:53 인라인 코멘트.

1번이 풀리기 전에는 PR 본문의 「감시 장치를 먼저 넣고 수정은 #1153 에서 한다」가 성립하지 않는다 — 감시가 CI 에 연결돼 있지 않으니, 그 사이 추억 공간이 더 망가져도 아무도 모르고, #1153 을 고친 뒤 compact 3장을 갱신하지 않아도 빨개지지 않는다. 순서 자체(#1128 의 선례)는 맞다. 연결만 빠졌다.


검증

항목 결과
CI (head fc5ec9c5) 전 항목 pass — 단, Screenshot 잡은 이 모듈을 돌지 않았다(1번)
Screenshot 잡 로그 실측 (run 32860511624) :feature:mindrecord:presentation 태스크 0줄
.github/ 전체 mindrecord 문자열 0건
git merge-tree origin/develop clean
문구 대조 (strings.xml ↔ 테스트 하드코딩) empty 2장 불일치, error 2장 일치

실기동 QA — 해당 없음. 프로덕션 동작 변경이 가시성 완화 한 줄뿐이라는 PR 본문 판단이 맞다.

시안 대조 — 해당 없음. 이 PR 은 화면을 바꾸지 않는다. 다만 #1153 에서 겹침을 고칠 때는 시안 대조가 필요하다 — 헤더 부제와 「돌아가기」의 세로 관계를 시안이 어떻게 잡고 있는지가 수정 방향(줄바꿈 허용 · 버튼 위치 · 부제 축약)을 가른다.

testOptions.unitTests.isIncludeAndroidResources = true

// 스크롤이 없는 화면은 세로가 모자라면 그대로 잘린다 — 좁은 화면 회귀를 CI 가 잡게 한다 (#1131).
experimentalProperties["android.experimental.enableScreenshotTest"] = true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] 소스셋은 켜졌지만 CI 는 이 모듈의 validateScreenshotTest 를 돌지 않는다. .github/workflows/screenshot.yml:60-64 가 모듈을 하드코딩한다:

./gradlew :core:ui:validateScreenshotTest \
          :feature:home:presentation:validateScreenshotTest \
          :feature:receiver:presentation:validateScreenshotTest \
          :feature:onboarding:presentation:validateScreenshotTest \
          :feature:afternote:presentation:validateScreenshotTest \
          --parallel --build-cache

실측 두 가지:

  • 이 PR 의 Screenshot / Validate Compose Preview Screenshots 잡(run 32860511624) 로그에 :feature:mindrecord:presentation 태스크는 한 줄도 없다. 초록인 것은 통과해서가 아니라 돌지 않아서다.
  • .github/ 전체에서 mindrecord 문자열이 0건이다.

그러니 지금 상태로 들어가면 baseline 6장은 저장소에만 있고 아무도 보지 않는 파일이 된다. 좁은 화면 잘림을 잡겠다는 이 PR 의 목적이 CI 에서 실현되지 않는다.

세 곳을 함께 고쳐야 한다:

  • screenshot.yml:60-64 — 태스크 목록에 :feature:mindrecord:presentation:validateScreenshotTest 추가
  • screenshot.yml:81-85 — 실패 시 렌더 PNG 를 받는 아티팩트 업로드 경로(feature/mindrecord/presentation/build/outputs/screenshotTest-results/). 이게 빠지면 CI 가 빨개졌을 때 무엇이 어떻게 그려졌는지 볼 방법이 없다.
  • screenshot.yml:106 — 실패 안내 문구의 모듈 목록

덧붙여, 모듈이 하나 늘면 이 잡의 벽시계 시간도 늘어난다(현재 7분대). --parallel 이 걸려 있어 치명적이지는 않겠지만, 이 PR 에서 실제 증가폭을 한 번 확인해 두면 다음 모듈을 붙일 때 근거가 된다.

onBackClick = {},
modifier = Modifier.fillMaxSize(),
memories = emptyList(),
statusText = "아직 담긴 기록이 없어요.\n일기나 데일리 질문에 답하면 이곳에 쌓입니다.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocking] 이 문구는 앱에 없다. feature/mindrecord/presentation/src/main/res/values/strings.xml:19 실측:

실제 : 아직 담긴 기록이 없어요.\n일기나 데일리 질문을 «남기면» 이곳에 쌓입니다.
여기 : 아직 담긴 기록이 없어요.\n일기나 데일리 질문에 «답하면» 이곳에 쌓입니다.

화면은 MemorySpaceUiState.statusText() 가 stringResource(R.string.mindrecord_memory_space_empty) 로 만든 문구를 넘긴다(MemorySpaceScreen.kt:85). 그러니 memorySpaceScreenEmptyScreenshot · memorySpaceScreenEmptyCompactScreenshot 두 장은 앱이 그린 적 없는 화면을 baseline 으로 고정한 것이다.

이 PR 의 목적에서 특히 아프다 — 잡으려는 것이 «세로가 모자라 잘리는가» 인데, 문구가 다르면 줄바꿈과 세로 점유가 달라져 감시 대상 자체가 어긋난다. 360dp 에서 두 문장의 접히는 위치가 같다는 보장이 없다.

에러 쪽(기록을 불러오지 못했습니다.)은 mindrecord_error_memory_space_failed 와 우연히 일치한다. 하드코딩인 한 다음에 또 어긋난다.

리소스를 직접 부르면 된다 — @Preview 컴포저블 안이므로 stringResource 가 그대로 동작하고(같은 모듈의 다른 화면 Preview 들이 이미 그렇게 쓴다), 문구가 바뀌면 baseline 이 빨개져 «갱신하라» 고 알려 준다. 지금은 그 신호가 없다.

statusText = stringResource(R.string.mindrecord_memory_space_empty),

baseline 6장이 저장소에만 있고 아무도 보지 않았다. screenshot.yml 이 모듈 목록을 실행
태스크·아티팩트 경로·실패 안내 세 곳에 각자 하드코딩하고 있어, 이 모듈이 어디에도 없었다 —
Screenshot 잡이 초록인 이유는 통과가 아니라 부재였다.

- 모듈 목록을 job env(SCREENSHOT_MODULES) 한 벌로 모으고 세 곳이 거기서 파생하게 한다.
  다음 모듈을 붙일 때 한 줄만 늘리면 되고, 셋이 갈릴 수 없다
- 아티팩트 경로를 같은 목록에서 만든다 — 여기만 빠지면 CI 가 빨개졌을 때 무엇이 어떻게
  그려졌는지 볼 방법이 없다
- empty baseline 2장이 앱에 없는 문구("답하면")를 담고 있었다. 실제 리소스는 "남기면" 이다.
  잡으려는 것이 «세로가 모자라 잘리는가» 인데 문구가 다르면 줄바꿈과 세로 점유가 달라져
  감시 대상 자체가 어긋난다. statusText 를 stringResource 로 바꾸고 baseline 을 재생성했다.
  이제 문구가 바뀌면 baseline 이 빨개져 갱신을 요구한다

CI 벽시계 증가폭: 이 모듈 validateScreenshotTest 단독 179초(같은 docker 이미지, 워밍된
gradle 캐시). 잡 전체는 --parallel 이라 이보다 적게 는다.

docker(linux/amd64) validateScreenshotTest BUILD SUCCESSFUL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Sadturtleman

Copy link
Copy Markdown
Collaborator Author

막는 것 둘 다 고쳤습니다. 특히 1번 지적이 맞습니다 — 초록인 이유가 부재였습니다.

1. [blocking] CI 가 이 모듈을 돌지 않음

말씀하신 세 곳을 각각 고치는 대신 목록을 한 벌로 모았습니다. 세 곳이 각자 하드코딩하고 있던 게 이 문제의 원인이라, 다시 갈릴 수 있는 구조를 남기고 싶지 않았습니다.

env:
  SCREENSHOT_MODULES: >-
    :core:ui
    :feature:home:presentation
    ...
    :feature:mindrecord:presentation
  • 실행 태스크 — 컨테이너에 -e SCREENSHOT_MODULES 로 넘겨 sh -c 안에서 :validateScreenshotTest 를 붙여 만듭니다
  • 아티팩트 경로 — 앞선 스텝(Resolve screenshot result paths)이 같은 목록에서 <module>/build/outputs/screenshotTest-results/ 를 만들어 $GITHUB_OUTPUT 으로 넘깁니다
  • 실패 안내 문구 — github-script 안에서 process.env.SCREENSHOT_MODULES 를 파싱해 렌더합니다

다음 모듈을 붙일 때 늘릴 줄은 한 줄이고, 셋이 어긋날 수 없습니다.

2. [blocking] empty baseline 2장이 앱에 없는 문구

실측 그대로였습니다 — strings.xml:19 는 「남기면」, baseline 은 「답하면」 이었습니다. statusText 를 리소스에서 읽게 바꾸고(@Preview 안이라 stringResource 가 그대로 동작합니다) docker 로 재생성했습니다.

바뀐 baseline 은 정확히 그 2장뿐입니다 — error 2장은 우연히 일치하고 있었다는 지적도 맞았고, 리소스로 바꾼 뒤 픽셀 무변경입니다. 이제 우연이 아니라 계약입니다.

재생성한 compact baseline 이 마침 #1153 을 그대로 보여 줍니다 — 「돌아가기」가 헤더 부제를 덮고 있고, 실제 문구는 360dp 에서 두 번째 줄이 거의 좌우 끝까지 찹니다. 감시 대상이 어긋나 있었다는 지적이 이 그림에 드러납니다.

벽시계 증가폭

물어보신 것 — 같은 docker 이미지·워밍된 gradle 캐시에서 이 모듈 validateScreenshotTest 단독 179초 입니다. 잡 전체는 --parallel 이라 증가폭은 이보다 작습니다. cold baseline 생성(updateScreenshotTest)은 5분 11초였습니다.

검증

  • docker(linux/amd64) validateScreenshotTest → BUILD SUCCESSFUL
  • git merge-tree origin/develop → clean

#1153 은 별도로 두겠습니다 — 말씀대로 수정 방향(줄바꿈 허용 · 버튼 위치 · 부제 축약)이 시안 대조에 달려 있어서, 감시 장치와 같은 PR 에 넣으면 그 판단이 묻힙니다.

@github-actions github-actions Bot added the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

이 PR 은 develop 와 충돌 상태이고, 그래서 CI 가 돌지 않는다.

pull_request 워크플로는 base 와 head 를 합친 가상 머지 커밋(refs/pull/<N>/merge)에서
실행되는데, 충돌이면 GitHub 이 그 ref 를 만들지 못해 워크플로가 트리거조차 되지 않는다.
체크가 실패하는 것이 아니라 아예 생성되지 않으므로, 새 커밋을 올려도 no checks reported
인 채 최신 커밋이 한 번도 검증되지 않은 상태로 남는다 (#1028).

develop 를 병합해 충돌을 풀면 검증이 다시 돈다. 해소되면 이 라벨은 자동으로 떨어진다.

@1hyok 1hyok left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재리뷰 — blocking 둘 다 닫혔습니다. 승인합니다

1번을 «목록에 한 줄 더하기» 로 끝내지 않고 세 곳이 각자 목록을 들던 구조 자체를 없앤 것이 이 회차의 핵심입니다. 지적은 「mindrecord 가 빠졌다」 였는데, 고친 것은 「다음 모듈도 빠질 수 있다」 쪽입니다.

# 지적 처리
1 [blocking] CI 가 이 모듈을 한 번도 돌지 않는다 닫힘 — 아래 로그
2 [blocking] empty baseline 2장이 앱에 없는 문구를 담고 있다 닫힘 — 리소스에서 읽음

1번 — 이번엔 실제로 돌았습니다

지난 회차에서 「:feature:mindrecord:presentation 태스크 0줄」로 적발했던 자리를, 같은 방법으로 다시 봤습니다. head 9e590bcc 의 Screenshot 잡(run 32942258395, conclusion success) 로그입니다.

$ gh api .../actions/jobs/98095488409/logs | grep -oE "Task :[a-z:]+:validateScreenshotTest"

Task :core:ui:validateScreenshotTest
Task :feature:home:presentation:validateScreenshotTest
Task :feature:onboarding:presentation:validateScreenshotTest
Task :feature:afternote:presentation:validateScreenshotTest
Task :feature:mindrecord:presentation:validateScreenshotTest   ← 07:28:52
Task :feature:receiver:presentation:validateScreenshotTest

컨테이너에 실제로 전달된 값도 로그에 찍혀 있어 «워크플로 파일만 고쳐 놓고 안 먹는» 경우가 아님이 확인됩니다.

SCREENSHOT_MODULES: :core:ui :feature:home:presentation :feature:receiver:presentation
                    :feature:onboarding:presentation :feature:afternote:presentation
                    :feature:mindrecord:presentation

목록이 실제 소스셋과 일치하는지도 전수로 맞춰 봤습니다 — find -type d -name screenshotTest 로 나온 6개 모듈과 위 6개가 정확히 같습니다. 누락도, 없는 모듈을 가리키는 줄도 없습니다.

아티팩트 경로와 실패 안내를 같은 목록에서 파생시킨 것도 맞습니다. 특히 안내 문구를 modules.map(...) 으로 만든 덕에, 다음에 모듈이 늘 때 «봇은 옛 목록을 말하는데 실제로는 더 돌고 있는» 어긋남이 생기지 않습니다.

2번 — 키가 실제 화면의 것과 같습니다

strings.xml:19  mindrecord_memory_space_empty          ← MemorySpaceScreen.kt:86 이 쓰는 그 키
strings.xml:20  mindrecord_error_memory_space_failed   ← MemorySpaceViewModel.kt:59 가 넣는 그 키

종전 하드코딩(「일기나 데일리 질문에 답하면」)과 실제 문구(「일기나 데일리 질문을 남기면」)가 달랐던 것이 이렇게 구조적으로 닫힙니다. 이제 문구를 고치면 baseline 이 빨개져 갱신을 요구하고, 그게 KDoc 에 적어 두신 의도 그대로입니다.


머지 전 필수 — develop 과 충돌합니다 (기계적)

mergeable=CONFLICTING 입니다. 충돌은 한 파일 한 자리뿐이고 서로 다투는 변경이 아닙니다.

CONFLICT (content): feature/mindrecord/presentation/build.gradle.kts
plugins {
    …
<<<<<<< HEAD
    alias(libs.plugins.compose.screenshot)     // 이 PR
=======
    id("afternote.kover")                      // develop
>>>>>>> origin/develop
}

둘 다 남기면 끝입니다. 그 조합이 실제로 서는지 로컬에서 해소해 확인했습니다 — kover 가 새 screenshotTest 소스셋을 만나 configuration 이 깨지는 일은 없었습니다.

./gradlew :feature:mindrecord:presentation:ktlintCheck \
          :feature:mindrecord:presentation:compileDebugKotlin \
          :feature:mindrecord:presentation:compileDebugScreenshotTestKotlin \
          :feature:mindrecord:presentation:compileDebugUnitTestKotlin
→ BUILD SUCCESSFUL

다만 픽셀 비교(validateScreenshotTest)는 로컬 arm64 로 판정할 수 없어 돌리지 않았습니다. 충돌이 있는 동안은 CI 도 돌지 않으므로(merge ref 미갱신), 해소해 올리시면 그때 CI 가 이 조합에서 baseline 6장을 다시 봐 줍니다. 위 07:28 run 이 이미 통과한 조합이라 새로 깨질 이유는 보이지 않습니다.

[normal] 네 번째 자리가 남았습니다 — README.md:257-263

이 PR 이 없앤 「목록이 여러 곳에 있다」 가 문서에 하나 더 있습니다. 실행 태스크·아티팩트 경로·실패 안내 셋은 통합됐는데, 로컬 검증 절차의 모듈 목록은 5개인 채입니다 — mindrecord 가 빠져 있습니다.

# README.md — 로컬 baseline 검증 (CI 실패 재현)
docker run … ./gradlew :core:ui:validateScreenshotTest \
            :feature:home:presentation:validateScreenshotTest \
            :feature:receiver:presentation:validateScreenshotTest \
            :feature:onboarding:presentation:validateScreenshotTest \
            :feature:afternote:presentation:validateScreenshotTest
            # :feature:mindrecord:presentation 없음

같은 문서가 세 줄 위에서 이렇게 적고 있어 더 그렇습니다.

대상 모듈 목록의 정본은 .github/workflows/screenshot.yml 이다. 모듈을 추가·이전했다면 워크플로와 이 문서를 함께 갱신한다.

여기가 어긋나면 결과는 CI 때와 같은 종류입니다 — 사람이 로컬에서 재현할 때 mindrecord 만 조용히 빠져, 「로컬에선 통과했는데 CI 가 빨갛다」 가 됩니다. 워크플로처럼 파생시킬 수 없는 자리라 손으로 한 줄 더하거나, 문서를 «정본은 워크플로이니 그 목록을 그대로 쓰라» 는 안내로 바꾸는 편이 낫겠습니다.

승인을 막지는 않습니다 — 충돌 해소하실 때 같이 얹으면 됩니다.


검증

항목 결과
CI (head 9e590bcc) 전 항목 pass — 이번엔 mindrecord validate 를 포함해서
Screenshot 잡 로그 실측 (run 32942258395) :feature:mindrecord:presentation:validateScreenshotTest 실행 확인
모듈 목록 ↔ 실제 screenshotTest 소스셋 6 ↔ 6 일치
문구 대조 (strings.xml ↔ 테스트) 리소스 참조로 전환, 화면과 동일 키
git merge-tree origin/develop CONFLICT 1건 (위 참조, 해소본 컴파일 확인)
.github 내 모듈 하드코딩 잔재 0건
문서 내 잔재 1건 (README.md, 위 normal)

실기동 QA — 해당 없음. 이번 증분은 워크플로·테스트·baseline 뿐이고 프로덕션 코드 변경이 없습니다.

시안 대조 — 해당 없음. 화면을 바꾸지 않습니다. 지난 회차에 적어 둔 대로, #1153 에서 겹침을 고칠 때는 헤더 부제와 「돌아가기」의 세로 관계를 시안에서 확인하셔야 합니다.

…rd-screenshot

# Conflicts:
#	feature/mindrecord/presentation/build.gradle.kts
@github-actions github-actions Bot removed the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Aug 26, 2026
@1hyok
1hyok merged commit aa6b740 into develop Aug 26, 2026
14 checks passed
@1hyok
1hyok deleted the test/1131-mindrecord-screenshot branch August 26, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants