Skip to content

fix(mindrecord): MEMORIES 카드의 '그날의 기록 다시 읽기' 죽은 영역을 없앤다 (#793) - #1105

Merged
Sadturtleman merged 4 commits into
developfrom
fix/793-read-again-destination
Aug 25, 2026
Merged

Sadturtleman merged 4 commits into
developfrom
fix/793-read-again-destination

Conversation

@Sadturtleman

Copy link
Copy Markdown
Collaborator

#793 의 증상을 없앤다. 최종 목적지 확정은 여전히 열려 있다 — 아래 「무엇이 남는가」.

무엇이 문제였나

목적지가 미확정이라 onReadAgainClick 을 비워 뒀는데, 버튼이 자체 clickable 이라 클릭을 삼킨다.

// MemoriesCard — 버튼
.clickable(role = Role.Button, onClick = onReadAgainClick)   // 기본값 {}

카드를 감싼 Column 은 이미 추억 공간으로 간다(onMemoriesSectionClick → Route.MemorySpace). 그래서 실제 사용자 경험은 «카드 여백을 누르면 추억 공간이 열리는데, 정작 가장 눌러 보고 싶은 버튼만 아무 일도 안 한다» 였다. 비워 두는 선택이 아무 동작 없음이 아니라 카드 안의 구멍이 된 셈이다.

무엇을 바꿨나

버튼에 카드 자신의 목적지를 물려준다. 새 목적지를 지어내는 게 아니라, 이미 동작하는 컨테이너와 같은 곳으로 보내 구멍을 메우는 것이다.

MemoriesCard(
    question = question,
    answer = answer,
    onReadAgainClick = onMemoriesSectionClick,
)

확정이 오면 호출부의 이 인자 하나만 바꾸면 된다 — #721 리뷰가 남긴 «호출부에서 인자 하나만 채우면 된다» 구조를 그대로 유지한다.

무엇이 남는가

최종 목적지는 여전히 미확정이다. 후보 (a) 그 기록의 상세 · (b) 추억 공간 · (c) 마인드레코드 목록 중 이 PR 은 (b) 를 임시로 따른다 — 카드 전체가 이미 (b) 로 가고 있어 «버튼만 다른 곳» 이 되지 않기 때문이다.

시안 쪽 근거도 하나 덧붙인다: 「정리 Screen Design」 페이지의 홈 섹션(4327:99448) 안에 MEMORY SPACE 화면(4327:67706)이 함께 놓여 있다. 여전히 이 버튼에 프로토타입 연결은 없어 확정 근거로는 부족하지만, (b) 가 (a)·(c) 보다 배치상 가깝다.

회귀 가드

MemoriesReadAgainTest — 버튼을 실제로 눌러 카드와 같은 콜백이 도는지 확인한다. 인자를 도로 빼면 실패한다.

(onReadAgainClick 제거)
MemoriesReadAgainTest > 버튼을 누르면 카드와 같은 목적지로 간다  FAILED
(현재)
BUILD SUCCESSFUL

검증

게이트 결과
전 모듈 testDebugUnitTest BUILD SUCCESSFUL
:app:assembleDebug BUILD SUCCESSFUL

실기동 QA: 못 했다. dev 서버가 점검 시간대라 홈이 «정보를 불러오지 못했습니다» 로 뜬다(에뮬레이터 확인). 서버가 열리면 홈 → MEMORIES 카드 버튼 → 추억 공간 진입을 확인해 코멘트로 남기겠다. 대신 Robolectric 으로 실제 컴포지션에서 버튼을 눌러 콜백을 확인했다.

목적지가 미확정이라 onReadAgainClick 을 비워 뒀는데, 버튼이 자체 clickable 이라
클릭을 삼켰다. 결과는 «카드 여백을 누르면 추억 공간이 열리고, 정작 가장 눌러 보고
싶은 버튼만 아무 일도 안 한다» 였다.

새 목적지를 지어내지 않는다 — 이미 동작하는 카드 자신의 목적지를 물려준다. 확정이
오면 호출부의 인자 하나만 바꾸면 된다.

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

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

죽은 영역 진단은 맞고, «새 목적지를 지어내는 게 아니라 이미 동작하는 카드의 목적지를 물려준다» 는 논리도 이슈 #793 의 후보 (b)(추억 공간)와 어긋나지 않는다. 방향에는 이견 없다.

다만 CI 가 red 이고 원인이 이 PR 이 추가한 테스트 코드라 그대로는 머지되지 않는다.

검증

  • CI: Check Project Issues (Android Lint) fail. 스텝 단위로 특정했다 — 실패한 것은 7번 Run Android Lint 이고, 8번 merge·9번 Report Android Lint (Reviewdog) 는 성공했다. 즉 reviewdog 인프라 문제가 아니라 코드에서 나온 lint error 다. 나머지 10종은 green.
  • 실패 원문: MemoriesReadAgainTest.kt:37: Error: Calling a @RememberInComposition annotated declaration inside composition without using remember [RememberInComposition] — Lint found 1 error, 3 warnings → Lint found errors in the project; aborting build.
  • 시안 대조 해당 없음 — 이슈 #793 본문대로 Figma 카드 노드 109:15028 의 이 버튼에는 프로토타입 연결이 없어, 시안이 목적지를 판정해 주지 못한다. 실기동 QA 해당 없음 — 클릭 전달 경로를 새 Robolectric 테스트가 덮는다(단, 그 테스트가 아래 blocking 의 당사자다).

지적

  1. [blocking] 새 테스트가 Android Lint 를 깨뜨린다 → MemoriesReadAgainTest.kt:37 인라인 코멘트.
  2. [normal] 종전 «목적지를 비워 둔다» 결정을 뒤집으면서 이슈에 근거를 남기지 않았다 → HomeTabMindRecordLazyItems.kt:152 인라인 코멘트.
  3. [normal] 테스트를 위한 가시성 완화 → HomeTabMindRecordLazyItems.kt:121 인라인 코멘트.

MemoriesSectionContent(
onMemoriesSectionClick = { clicks += 1 },
clickLabel = "추억 공간 열기",
interactionSource = MutableInteractionSource(),

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 의 Android Lint 를 깨뜨린다.

MemoriesReadAgainTest.kt:37: Error: Calling a @RememberInComposition annotated declaration
inside composition without using remember [RememberInComposition from androidx.compose.runtime]

MutableInteractionSource() 는 @RememberInComposition 이 붙어 있어 setContent { } 안에서 맨손으로 호출할 수 없다. 컴포지션마다 새 인스턴스가 생겨 상호작용 상태가 유실되는 것을 막으려는 규칙이다.

둘 중 아무 쪽이나 통한다.

// (a) 컴포지션 밖에서 만들어 넘긴다
val interactionSource = MutableInteractionSource()
composeRule.setContent {
    AfternoteTheme {
        MemoriesSectionContent(interactionSource = interactionSource, ...)
    }
}
// (b) 컴포지션 안이라면 remember 로 감싼다
interactionSource = remember { MutableInteractionSource() },

이 한 건 때문에 Lint found errors in the project; aborting build 로 잡 전체가 중단된다 — 고치면 red 도 함께 풀린다.

MemoriesCard(
question = question,
answer = answer,
onReadAgainClick = onMemoriesSectionClick,

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] 이 인자를 비워 둔 것은 실수가 아니라 PR #721 리뷰에서 합의한 결정이었고, 이슈 #793 이 그 이유를 «확정 전까지 임의 목적지를 붙이지 않는 이유는 미확정 탐색 동작이 제품 동작으로 굳는 것을 막기 위해서다» 라고 명시적으로 기록해 두었다.

이 PR 의 논리(죽은 영역 > 미확정 보류, 그리고 새 목적지가 아니라 컨테이너 목적지의 승계)에는 동의한다. 다만 결정이 뒤집힌 사실이 이슈에는 남지 않는다 — 지금 상태로 머지되면 #793 은 여전히 «목적지 미확정, 무동작» 이라고 적혀 있고, 나중에 이 코드를 보는 사람은 종전 합의를 모른 채 되돌릴 수 있다.

같은 저자의 #1103 은 이 상황을 docs/qa/assumptions.md 에 «판단 보류» 로 남겨 처리했다. 여기도 같은 방식이면 충분하다 — 이슈 코멘트든 assumptions.md 항목이든, «최종 목적지는 여전히 미확정이며 그때까지 카드 목적지를 승계한다» 가 코드 밖에 한 줄 남으면 된다.


@Composable
private fun MemoriesSectionContent(
internal fun MemoriesSectionContent(

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] private → internal 완화의 유일한 이유가 테스트 접근으로 보인다. 이 정도는 이 레포에서 흔한 타협이라 blocking 으로 올리지 않지만, 모듈 공개 표면이 한 칸 넓어지는 것은 사실이다.

homeTabMindRecordMemoriesSection(이미 public 인 LazyListScope 확장) 쪽에서 같은 클릭 경로를 태울 수 있다면 가시성을 되돌릴 수 있고, 그게 어렵다면 @VisibleForTesting 을 붙여 의도를 표시해 두는 편이 낫다.

@Sadturtleman

Copy link
Copy Markdown
Collaborator Author

실기동 확인 — 서버가 복구돼 태워 봤습니다.

홈 → MEMORIES 카드 → 「그날의 기록 다시 읽기」 탭 → 추억 공간(MEMORY SPACE) 진입을 확인했습니다.

(탭 전) MEMORIES / 그날의 기록 다시 읽기 [140,1949][448,1998]
(탭 후) MEMORY SPACE · 「돌아가기」 · 「화면을 드래그해 공간을 둘러보세요」

수정 전에는 이 버튼만 클릭을 삼켜 아무 일도 일어나지 않았고, 카드 여백을 눌러야 같은 화면이 열렸습니다. 이제 둘이 같은 곳으로 갑니다.

develop 을 물려 최신 상태로 맞춰 두었습니다.

MutableInteractionSource 는 @RememberInComposition 이라 컴포지션 안에서 맨손으로
만들 수 없다 — 컴포지션 밖에서 만들어 넘긴다.

테스트 접근을 위해 넓힌 가시성에는 @VisibleForTesting 을 붙여 의도를 표시하고,
목적지 승계가 «종전 결정을 뒤집은 것이 아니라 구멍을 메운 것» 이라는 근거를
docs/qa/assumptions.md 에 남긴다.

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

Copy link
Copy Markdown
Collaborator Author

지적 3건 모두 반영했습니다.

1. Android Lint red — 원인 특정이 정확했습니다. MutableInteractionSource() 를 컴포지션 밖에서 만들어 넘기도록 고쳤습니다.

// MutableInteractionSource 는 @RememberInComposition 이라 컴포지션 안에서 맨손으로
// 만들 수 없다 — 컴포지션마다 새 인스턴스가 생겨 상호작용 상태가 유실된다.
val interactionSource = MutableInteractionSource()
composeRule.setContent { ... interactionSource = interactionSource ... }

:feature:mindrecord:presentation:lintDebug BUILD SUCCESSFUL 확인했습니다.

2. 결정이 뒤집힌 사실이 코드 밖에 안 남는다 — docs/qa/assumptions.md 에 남겼습니다. 표현은 «뒤집었다» 가 아니라 실제 성격에 맞췄습니다 — 최종 목적지는 여전히 미확정이고, 새 목적지를 만들지 않는 선에서 구멍만 메운 것입니다. 「정리 Screen Design」 페이지에도 이 버튼의 목적지를 정해 주는 프레임이 없다는 것까지 함께 적었습니다.

3. 테스트를 위한 가시성 완화 — @VisibleForTesting 을 붙였습니다. homeTabMindRecordMemoriesSection(public LazyListScope 확장) 쪽에서 태우는 것도 검토했는데, 그러려면 테스트가 LazyColumn 을 세우고 hiltViewModel() 을 타는 HomeTabMindRecordMemoriesItem 을 거쳐야 해서 클릭 전달 경로보다 Hilt 배선을 더 많이 태우게 됩니다. 의도 표시로 남겨 두었습니다.

추가로 실기동 확인도 했습니다 (서버 복구 후):

홈 → MEMORIES 카드 → 「그날의 기록 다시 읽기」 탭
→ MEMORY SPACE · 「돌아가기」 · 「화면을 드래그해 공간을 둘러보세요」

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

지적 3건 모두 닫혔고 red 도 함께 풀렸습니다. 승인합니다.

지적 처리

# 지적 처리
1 [blocking] 새 테스트가 Android Lint 를 깨뜨린다 닫힘 — CI green
2 [normal] 결정이 뒤집힌 사실이 코드 밖에 안 남는다 닫힘 — docs/qa/assumptions.md
3 [normal] 테스트를 위한 가시성 완화 닫힘 — @VisibleForTesting

1번 — MutableInteractionSource() 를 setContent 밖으로 꺼냈습니다. 예상대로 이 한 건이 aborting build 를 걸고 있었고, 지금은 Check Project Issues (Android Lint) 를 포함해 필수 체크 전부 green 입니다. 주석에 «왜 컴포지션 안에서 못 만드는가» 를 적어 둔 것도 좋습니다 — 다음 사람이 한 줄로 되돌리기 쉬운 자리라서요.

2번 — 표현을 «뒤집었다» 가 아니라 «새 목적지를 만들지 않는 선에서 구멍만 메웠다» 로 잡은 것이 실제 성격에 더 맞습니다. 제가 지적한 것보다 정확합니다. 「정리 Screen Design」 페이지에도 이 버튼의 목적지를 정하는 프레임이 없다는 것까지 적혀 있어, 확정이 왔을 때 무엇을 바꾸면 되는지(호출부 인자 하나)가 문서만 봐도 서 있습니다.

3번 — homeTabMindRecordMemoriesSection 쪽으로 태우지 않은 이유에 동의합니다. LazyColumn 을 세우고 hiltViewModel() 을 타는 HomeTabMindRecordMemoriesItem 을 거치면 «클릭이 전달되는가» 를 확인하는 데 Hilt 배선을 더 많이 태우게 되고, 그건 테스트가 검증하려는 것과 다른 것을 깨지게 만듭니다. 의도 표시로 충분합니다.

검증

  • CI: 필수 체크 전부 green (head 1ca279c7). 앞 리뷰에서 red 였던 Run Android Lint 스텝 포함.
  • develop 병합 실측: git merge-tree --write-tree origin/develop <head> → 충돌 0건. develop 428022ff 기준.
  • 가시성 점검: @VisibleForTesting 기본값은 otherwise = PRIVATE 이라 모듈 밖 호출이면 lint 가 잡는데, 프로덕션 호출부(HomeTabMindRecordMemoriesItem)가 같은 파일 안이라 걸리지 않습니다 — lint green 이 그걸 실증합니다.
  • 실기동 QA: 저자 실측으로 갈음합니다. 「그날의 기록 다시 읽기」 탭 → MEMORY SPACE 진입이 확인됐고, 수정 전에는 이 버튼만 클릭을 삼켰다는 대조까지 붙어 있어 이 PR 이 없애려던 죽은 영역이 실제로 없어진 것이 확인됩니다.
  • 시안 대조: 앞 리뷰와 같습니다 — Figma 카드 노드 109:15028 의 이 버튼에 프로토타입 연결이 없어 시안이 목적지를 판정해 주지 못합니다. 그래서 «미확정» 이 문서에 남는 쪽이 맞는 처리입니다.

@1hyok
1hyok requested a review from koongmai August 25, 2026 11:24
@github-actions github-actions Bot added the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Aug 25, 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 를 병합해 충돌을 풀면 검증이 다시 돈다. 해소되면 이 라벨은 자동으로 떨어진다.

assumptions.md 는 문서 끝에 각자 절을 덧붙인 인접 추가라 둘 다 살렸다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Sadturtleman
Sadturtleman merged commit 4d2c6d1 into develop Aug 25, 2026
14 checks passed
@Sadturtleman
Sadturtleman deleted the fix/793-read-again-destination branch August 25, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants