Skip to content

fix(app): 수신자 홈 기록 수 실패를 개수 자리 대시로 표기 (#952) - #1020

Merged
Sadturtleman merged 3 commits into
developfrom
fix/952-receiver-count-dash
Aug 24, 2026
Merged

Sadturtleman merged 3 commits into
developfrom
fix/952-receiver-count-dash

Conversation

@Sadturtleman

Copy link
Copy Markdown
Collaborator

Closes #952

무엇을 했나

시안 확정값(0823, 4309:19394)대로 레이아웃은 유지하고 개수 자리만 – 로 바꿨다.

종전 이 PR
세 섹션 countLine 문장 통째 교체 — "기록 수를 불러오지 못했습니다." –개 마음의 기록이 있습니다. (문장 구조 유지)
마음의 기록 통계 카드 summary == null 이면 행을 통째 숨김 카드 유지 + 개수 –

왜 카드를 남기나

숨기면 «기능이 없는 것» 과 «못 불러온 것» 이 구분되지 않는다. 실패한 화면과 정상 화면의 구성이 달라지는 것도 이슈가 든 문제다. 개수만 미상으로 두면 둘 다 해소된다.

– 는 en dash 다(hyphen 아님). 문자열 리소스에 그 취지를 주석으로 남겼다.

회귀 가드

ReceiverHomeCountFallbackTest 3개 — Robolectric + Compose 로 실제로 그려서 문구와 카드 존재를 읽는다. 「레이아웃을 유지하는지」는 상태 단언으로는 확인되지 않는 성질이라 렌더로 봤다.

고치기 전 코드로 되돌리면 잡힌다.

문구·카드 숨김을 되돌림 → 3 tests completed, 2 failed
  조회에 실패해도 문장 구조가 그대로다
  조회에 실패해도 통계 카드가 남는다
원복 → 3개 통과

⚠️ 스크린샷 baseline 갱신이 필요하다 (머지 전 필수)

receiverHomeScreenPartialFailureScreenshot 이 정확히 이 상태(mindRecord = null, afternoteTotalCount = null)를 덮고 있어 의도된 시각 변경으로 baseline 이 어긋난다. CI 의 Validate Compose Preview Screenshots 가 이 PR 에서 실패한다.

제 환경에 Docker 가 올라와 있지 않아 갱신하지 못했다. README 가 «host 직접 실행은 CI 와 baseline 이 어긋난다» 고 명시하고 있어 macOS 에서 생성하지 않았다 — 잘못된 baseline 을 커밋하면 다른 PR 까지 깨진다.

docker build -t afternote-screenshot:latest -f Dockerfile.screenshot .
docker run --rm -v "$PWD":/workspace -w /workspace afternote-screenshot:latest \
  ./gradlew :feature:receiver:presentation:updateScreenshotTest

diff 는 «통계 카드 행이 살아나고 개수 자리가 – 로 바뀐» 한 장이어야 한다. 그 외 변화가 있으면 알려 주시면 확인하겠다.

실기기 시연은 못 했다

수신자 홈은 delivery-verification APPROVED 상태라야 진입하는데 승인 API 가 수신자·발신자 어느 쪽에도 없다(#618·#614 에서 확인한 것과 같은 사정). 그래서 실기 대신 JVM 에서 컴포넌트를 실제로 렌더해 문구를 읽는 테스트로 갈음했다.

검증

ktlintCheck · :app:assembleDebug · :feature:receiver:presentation:testDebugUnitTest → BUILD SUCCESSFUL

🤖 Generated with Claude Code

종전에는 문장을 통째로 «기록 수를 불러오지 못했습니다.» 로 갈아 끼우고 마음의 기록
통계 카드 행을 숨겼다. 실패한 화면과 정상 화면의 구성이 달라지고, 카드가 통째로
사라지면 «기능이 없는 것» 과 «못 불러온 것» 도 구분되지 않는다.

시안 확정값(4309:19394)은 레이아웃 유지 + 개수 자리만 대시다. 세 섹션 공통으로 문장
구조를 그대로 두고, 통계 카드도 숨기지 않고 «–» 를 표시한다.

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

Copy link
Copy Markdown
Contributor

❌ Screenshot test fail — baseline 과 CI rendered PNG 차이.

📦 Artifact: https://github.com/Afternote/Afternote-FE/actions/runs/32707204627 (페이지 하단 Artifacts → screenshot-test-results)

🔍 확인 위치 (artifact zip 내부):

  • diff PNG: preview/debug/diffs/...
  • rendered PNG: preview/debug/rendered/... (CI 환경에서 새로 그린 결과)
  • baseline PNG: repo <module>/src/screenshotTestDebug/reference/... (:core:ui · :feature:home:presentation · :feature:receiver:presentation · :feature:onboarding:presentation · :feature:afternote:presentation)

📝 조치 가이드:

  • 의도된 시각 변경 → docker build -t afternote-screenshot:latest -f Dockerfile.screenshot . 후 docker run --rm -v "$PWD":/workspace -w /workspace afternote-screenshot:latest ./gradlew :<module>:updateScreenshotTest 로 baseline 갱신 + commit/push
  • diff 가 anti-aliasing/font 차이로 보이면 docker 환경 변경 여부 (Dockerfile.screenshot) 점검

CI ↔ 로컬 환경 차이는 본 워크플로의 container 단계가 root fix 한다 (README 의 "Compose Preview Screenshot Testing (docker baseline)" 섹션 참조).

receiverHomeScreenPartialFailureScreenshot 이 mindRecord=null 상태를 그린다 — 통계
카드가 살아나고 개수 자리가 «–» 로 바뀐 의도된 시각 변경이다.

CI 와 같은 docker 컨테이너(Dockerfile.screenshot)에서 생성했다.

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

@OnlyReadOneBook OnlyReadOneBook 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.

총평

«레이아웃 유지 + 숫자 자리만 대시» 라는 확정값을 정확히 옮겼고, «카드를 숨기면 기능 없음과 못 불러옴이 구분되지 않는다» 는 판단도 맞습니다. en dash 를 리소스로 빼고 주석으로 못 박은 것도 좋습니다. 머지 전 CI red 하나만 정리해 주세요.

지적

  1. [blocking] 스크린샷 baseline 미갱신 — CI red. 본문에 적어 두신 그대로입니다. receiverHomeScreenPartialFailureScreenshot 이 정확히 이 상태를 덮고 있어 의도된 변경이 맞습니다. Docker 가 없어 못 하신 거면 제가 대신 돌려도 됩니다 — 말씀 주세요.
  2. [normal] unavailableCountLine 이 남아 파라미터 2개로 부풀었다 → feature/receiver/presentation/src/main/kotlin/com/afternote/feature/receiver/presentation/home/component/HomeSectionCard.kt:99 인라인.
  3. [normal] 인덱스로 두 번째 노드만 본다 → feature/receiver/presentation/src/test/kotlin/com/afternote/feature/receiver/presentation/home/component/ReceiverHomeCountFallbackTest.kt:49 인라인.

참고 — Analyze (java-kotlin) red 는 이 PR 책임이 아닙니다

실패 원인이 KSP/Hilt 였습니다.

e: [ksp] MainActivity.kt:19: [Hilt] @AndroidEntryPoint base class must extend ComponentActivity, …
e: [ksp] GlobalApplication.kt:13: [Hilt] @HiltAndroidApp base class must extend Application. Found: kotlin.Any
> KSP failed with exit code: PROCESSING_ERROR

이 PR 은 :app 을 건드리지 않고 :feature:receiver:presentation 에서 :app 으로 가는 의존도 없습니다. 같은 base(develop)의 다른 PR(#1052·#1021)은 같은 체크가 green 이었습니다.

재실행해서 확정했습니다 — run 32728279766 에서 pass (7m38s). 코드 변경 없이 green 이 됐으니 캐시/CodeQL 트레이서 쪽 플레이키였습니다. 이 항목은 신경 쓰지 않으셔도 됩니다.

검증

  • 시안 대조: 링크는 확인, 노드 직접 열람은 못 했습니다. 본문의 4309:19394 가 근거로 걸려 있으나, 이 세션의 Figma 커넥터가 미인증 상태라 노드를 직접 열지 못했습니다. 코드 쪽 결과(문장 구조 유지, 카드 존치, 개수 자리만 –)가 본문 서술과 일치하는 것까지 확인했습니다.
  • 실기동 QA: 해당 없음. 수신자 홈은 delivery-verification APPROVED 상태라야 진입하는데 승인 API 가 없어 도달 불가라는 본문 설명이 #618·#614 와 일치합니다. Robolectric 렌더 테스트로 갈음한 판단에 동의합니다.

*/
@Composable
fun unavailableCountLine(): AnnotatedString = AnnotatedString(stringResource(R.string.receiver_home_section_count_unavailable))
fun unavailableCountLine(

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.

[normal] 이 함수가 이제 rememberCountLine 을 한 겹 감싸기만 하는데, 그 대가로 호출부마다 suffix 리터럴이 if/else 양쪽에 두 번 적히게 됐습니다.

// MindRecordSection.kt:35-42
if (summary == null) {
    unavailableCountLine(suffix = "마음의 기록이 있습니다.")
} else {
    rememberCountLine(prefix = "${summary.totalCount}개 ", suffix = "마음의 기록이 있습니다.")
}

한 문장이 두 곳에 있으면 나중에 한쪽만 고쳐질 자리입니다. 게다가 애프터노트 섹션만 «개» 뒤 공백이 없다는 차이를 맞추려고 countUnitSpacing 파라미터까지 생겼습니다.

갈래를 개수 자리 하나로 좁히면 함수도 파라미터도 사라집니다.

countLine = rememberCountLine(
    prefix = "${summary?.totalCount?.toString() ?: stringResource(R.string.receiver_home_section_count_unavailable)}개 ",
    suffix = "마음의 기록이 있습니다.",
)

이 PR 이 만든 문제는 아니지만(문구 하드코딩은 종전부터입니다), 지금이 갈래를 늘리지 않고 줄일 수 있는 시점이라 짚습니다.

composeRule.onNodeWithText("데일리 질문").assertIsDisplayed()
composeRule.onNodeWithText("일기").assertIsDisplayed()
// 두 카드의 개수 자리가 모두 대시다.
composeRule.onAllNodesWithText("–")[1].assertIsDisplayed()

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.

[normal] 주석은 «두 카드의 개수 자리가 모두 대시» 인데, 단언은 인덱스 1 하나만 봅니다. 0번은 확인되지 않고, 노드 순서가 바뀌면 조용히 다른 것을 보게 됩니다.

의도를 그대로 쓰는 편이 낫습니다.

composeRule.onAllNodesWithText("–").assertCountEquals(2)

이러면 «한 장만 대시로 남는» 회귀도 잡힙니다.

@Sadturtleman

Copy link
Copy Markdown
Collaborator Author

스크린샷 baseline 갱신했습니다 — CI 와 같은 docker 컨테이너(Dockerfile.screenshot)에서 :feature:receiver:presentation:updateScreenshotTest 를 돌렸고, 바뀐 PNG 는 예상대로 receiverHomeScreenPartialFailureScreenshot 한 장뿐입니다(통계 카드가 살아나고 개수 자리가 –). 나머지 두 baseline 은 무변경입니다.

unavailableCountLine 이 rememberCountLine 을 한 겹 감싸기만 하면서, 호출부마다 같은
문장이 if/else 양쪽에 두 번 적히게 됐다 — 한쪽만 고쳐질 자리다. 애프터노트 섹션의
«개» 뒤 공백 차이를 맞추려고 파라미터도 하나 늘었다.

countText(count) 하나로 좁히면 함수도 파라미터도 사라지고 문장은 한 곳에만 남는다.

테스트도 인덱스 하나가 아니라 두 카드가 모두 대시인지 센다.

리뷰 지적.

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

@OnlyReadOneBook OnlyReadOneBook 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.

총평

지적 3건 전부 정리됐습니다. 특히 2번은 «갈래를 문장이 아니라 개수 자리 하나로 좁힌다» 를 그대로 실행해, 함수 하나(unavailableCountLine)와 파라미터 하나(countUnitSpacing)가 통째로 사라지고 호출부 세 곳의 if/else 도 없어졌습니다. 같은 문장이 두 번 적혀 한쪽만 고쳐질 자리가 정리됐습니다. 승인합니다.

반영 확인

  1. [blocking] 해소. a6de2040 에서 baseline 갱신, 최신 head 기준 Validate Compose Preview Screenshots green.
  2. [normal] 해소. countText(count: Int?) 하나로 수렴. 애프터노트 섹션만 «개» 뒤 공백이 없는 차이도 각 호출부의 suffix 리터럴에 그대로 남아 표기가 바뀌지 않았습니다.
  3. [normal] 해소. onAllNodesWithText("–").assertCountEquals(2) — «한 장만 대시로 남는» 회귀도 이제 잡힙니다.

검증

  • CI: head 84c4181d 기준 실패·취소 체크 0건.
  • 리팩터링이 렌더를 바꾸지 않았다는 실증: baseline 은 21:39(a6de2040)에 갱신됐고 그 뒤 22:25 의 이번 리팩터링 커밋에서는 baseline 이 손대지지 않았는데도 스크린샷 체크가 green 입니다. 즉 unavailableCountLine → countText 전환이 출력 문자열을 그대로 유지했다는 뜻이라, 별도 확인이 필요 없습니다.
  • develop 접점: merge-tree 충돌 0건, merge-base 이후 develop 이 건드린 파일과의 교집합도 0건.
  • 시안 대조: 지난번과 같음 — 링크는 확인, 노드 직접 열람은 못 했습니다. Figma 커넥터가 여전히 미인증입니다. 다만 이번 변경은 표기가 아니라 갈래 구조만 손댔고 위 스크린샷 근거로 렌더가 동일함이 실증돼, 시안 확정값(레이아웃 유지 + 숫자 자리만 –)과의 관계는 지난 리뷰에서 확인한 그대로입니다.
  • 실기동 QA: 해당 없음(지난번과 동일). 수신자 홈은 delivery-verification APPROVED 라야 진입하는데 승인 API 가 없어 도달 불가 — #618·#614 와 같은 사정입니다. Robolectric 렌더 테스트로 갈음한 판단에 동의합니다.

작은 관찰 하나를 인라인으로 남겼습니다 — blocking 아닙니다.

*/
@Composable
fun unavailableCountLine(): AnnotatedString = AnnotatedString(stringResource(R.string.receiver_home_section_count_unavailable))
fun countText(count: Int?): String = count?.toString() ?: stringResource(R.string.receiver_home_section_count_unavailable)

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.

[nit] 이름이 아주 일반적인데(countText) 패키지 레벨 public 입니다. 같은 패키지의 다른 화면에서 «개수를 문자열로» 정도의 뜻으로 끌어다 쓰면, 실제로는 «수신자 홈의 조회 실패 표기 규칙» 이 딸려 옵니다.

internal 로 좁히거나 이름에 맥락을 넣으면(countOrDash 류) 오용 여지가 줄어듭니다. 지금 범위에서 문제가 생기는 건 아니니 후속으로 보셔도 됩니다.

@Sadturtleman
Sadturtleman merged commit 848351d into develop Aug 24, 2026
13 checks passed
@Sadturtleman
Sadturtleman deleted the fix/952-receiver-count-dash branch August 24, 2026 14:47
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.

fix(app): 수신자 홈 섹션 기록 수 실패 표기 — 안내 문구 대신 '–', 통계 카드 유지

2 participants