Repository navigation
[fix] #702 임시저장·수신 목록에 재진입 갱신을 연결한다 - #900
Conversation
두 화면 모두 재조회 함수는 있는데 화면이 부르지 않아, 떠났다 돌아와도 목록이 낡은 채로 남았다. 마인드레코드 홈이 쓰는 결선(ON_RESUME → refreshOnReturn)을 그대로 붙인다. `refresh() = load()` 를 그대로 부르지 않는다 — ON_RESUME 은 화면이 살아 있는 채로 발화하므로 로딩을 방출하면 보고 있던 목록·선택·필터가 스피너로 갈아치워진다. 홈과 같이 로딩을 방출하지 않고 실패해도 기존 화면을 유지하는 `refreshOnReturn()` 을 따로 둔다. 수신 화면의 `refresh()` 는 오류 화면 재시도 버튼이 쓰고 있어 남긴다 — 그쪽은 보고 있던 것이 오류 문구뿐이라 로딩을 보여도 잃을 게 없다. (이슈는 호출부 0건으로 적었지만 `ReceiverMindRecordScreen.kt:71` 에서 쓰고 있었다.) 최초 진입의 중복 호출은 VM 이 든 `loadJob` 으로 막는다. Closes #702 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
OnlyReadOneBook
left a comment
There was a problem hiding this comment.
머지 전 1건 수정이 필요합니다. 결선 방식 자체(ON_RESUME + refreshOnReturn(), 홈과 동일 패턴)는 정확합니다.
- [blocking] 재진입 갱신 '성공'이 수신 화면의 필터·정렬을 소리 없이 초기화합니다.
ReceiverMindRecordViewModel.load()onSuccess(:74-81)가Success(dailyQuestions = emptyList(), diaries = emptyList()).withDerived()로 상태를 새로 만드는데, 이때filter는 기본값ReceiverMindRecordFilter()(NEWEST·기간 없음)이고withDerived()는this.filter로 파생합니다. develop에서는load()가 init·Error 재시도에서만 돌아 필터 적용된 Success 위에서 실행될 경로가 없었고, 이 PR의refreshOnReturn()이 그 경로를 처음 만듭니다.- 시나리오: 수신자가 정렬=과거순·기간 필터 적용 → 홈 버튼/화면 off → 복귀 → ON_RESUME → getAll 성공 → 필터·정렬·헤더 인디케이터가 조용히 초기화. 본문("
load()는 보고 있던 목록·선택·필터를 갈아치웁니다")이 밝힌 설계 목적과 정면 충돌합니다 — 로딩만 안 보일 뿐 필터는 여전히 사라집니다. onSuccess에서 기존 Success의 filter를 승계해 주세요.
- [normal] DraftList 선택 모드 중 재진입 갱신이 유령 선택을 만들 수 있습니다.
selectedKeys는 Screen의 remember(DraftListScreen.kt:74)라 갱신에도 유지됩니다 — 백그라운드 중 서버에서 지워진 항목의 키가 남아 "총 N개 선택"이 과대 표시되고, 선택 삭제 시 잔존 키가 필터링돼 빈 리스트로delete()가 불리는데 base 동작상 무조건 "삭제되었습니다" 토스트가 뜹니다(#896 머지 시 빈 리스트 가드로 절반 완화, 카운트 과대 표시는 남음). 갱신 성공 시selectedKeys를 현재 목록 키와 교집합으로 정리하기를 권합니다.
검증:
- CI 전 체크 green
LifecycleEventEffect(ON_RESUME)결선이 developHomeScreen.kt:95-110의 기존 패턴·주석 근거(rememberUpdatedState 캡처)와 동일함을 확인 — 네비 복귀·백그라운드 복귀 모두 발화- 최초 진입 중복 로드는
loadJob?.isActive가드로 차단,showsLoading=false라 로딩 미방출(깜빡임 없음), 갱신 실패 시 기존 목록 유지(두 VM 대칭) 확인 - 기존
refresh()(오류 재시도, 로딩 방출) 보존 확인 — 본문 "이슈와 다른 점" 서술과 일치, 추억 공간 보류는 이슈 노트(#559 선행)와 정합 - 참고: #904(
retry()리네임)·#896(deleteOutcome 개편)과 DraftListViewModel·Screen을 공유해 머지 순서상 리베이스 충돌이 예상됩니다
|
리뷰 [blocking]의 에뮬레이터 재현을 시도한 경과를 남깁니다 — 재현은 미완이고, blocking 판정은 코드 근거(develop에는 filter 적용된 Success 위에서 수신자 화면 도달 경로(Welcome → 받은 기록함 → 발신자 등록 → 수신자 인증)를 이메일 인증(수신자 레코드 등록 이메일 실측 확인)·마스터 키까지 통과했으나, 증빙 서류 업로드 단계가 develop의 별개 실버그로 원천 불가라 화면에 도달하지 못했습니다:
실측 기록: |
load() 는 새 Success 를 만들어 상태를 갈아치우는데, 종전에는 init·오류 재시도에서만 돌아 «필터가 적용된 Success 위에서 실행될» 경로가 없었다. 이 PR 의 refreshOnReturn() 이 그 경로를 처음 만들어, 화면 off/on 한 번에 정렬·기간 필터가 조용히 초기화됐다. 로딩을 안 보이게 한 것만으로는 «갈아치우지 않는다» 가 되지 않는다. 임시저장 선택 모드의 유령 선택도 정리한다. selectedKeys 는 화면 remember 라 갱신에도 살아남아, 그 사이 서버에서 지워진 항목의 키가 남으면 «총 N개 선택» 이 과대 표시되고 선택 삭제가 빈 목록으로 나간다. 갱신된 목록과의 교집합으로 좁힌다. 리뷰 지적. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
지적 2건 고쳤습니다. 1번은 정확합니다 — 본문이 « 1. [blocking] 재진입 갱신이 필터·정렬을 초기화 — val currentFilter =
(_uiState.value as? ReceiverMindRecordUiState.Success)?.filter
?: ReceiverMindRecordFilter()
2. [normal] 선택 모드의 유령 선택 — 갱신된 목록 키와의 교집합으로 좁히고, 남는 게 없으면 선택 모드도 해제합니다. val visibleKeys = (uiState as? DraftListUiState.Success)?.items?.map { it.key() }?.toSet()
LaunchedEffect(visibleKeys) {
if (visibleKeys != null) {
selectedKeys = selectedKeys intersect visibleKeys
if (selectedKeys.isEmpty()) selectionMode = false
}
}카운트 과대 표시와 빈 목록 머지 순서 참고 — 지적하신 대로 #904·#896 과 |
# Conflicts: # feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/screen/receiver/ReceiverMindRecordScreen.kt # feature/mindrecord/presentation/src/main/kotlin/com/afternote/feature/mindrecord/presentation/screen/sender/DraftListScreen.kt
OnlyReadOneBook
left a comment
There was a problem hiding this comment.
승인 — 지적 2건 해소
88b5271c 로 재확인했다.
1. [blocking] 재진입 갱신이 필터·정렬을 초기화 — 해소. onSuccess 가 기존 Success.filter 를 승계한다. 가드가 실제로 잡는 것도 직접 확인했다.
필터 승계 두 줄 제거
→ ReceiverRefreshFilterTest > 재진입 갱신이 정렬과 기간 필터를 지운다면 안 된다 FAILED
원복 → 통과
테스트가 «필터를 적용한 뒤 refreshOnReturn() 을 호출한다» 는 실제 순서를 그대로 재현하고, KDoc 이 «종전에는 load() 가 init·재시도에서만 돌아 이 경로가 없었고 이 PR 이 그 경로를 처음 만든다» 는 배경까지 남긴 것이 좋다. 다음에 load() 를 만지는 사람이 왜 승계가 필요한지 되짚지 않아도 된다.
2. [normal] 선택 모드의 유령 선택 — 해소. 갱신된 목록 키와의 교집합으로 좁혀 카운트 과대 표시와 빈 목록 delete() 가 함께 닫혔다.
승인에 지장 없는 것 하나
선택 모드 해제 조건이 «교집합 결과가 비었을 때» 라, 선택이 원래 없던 경우까지 모드가 풀린다. 목록 내용이 바뀔 때만 LaunchedEffect 가 다시 도니 자주 나타나진 않지만, 「선택 모드로 들어가고 아직 아무것도 고르지 않은 채 화면을 다녀오는 사이 항목이 하나 늘어난」 경우가 그 자리다.
val hadSelection = selectedKeys.isNotEmpty()
selectedKeys = selectedKeys intersect visibleKeys
// 고른 것이 전부 사라졌을 때만 모드를 푼다 — 원래 안 고른 상태는 사용자가 만든 것이다.
if (hadSelection && selectedKeys.isEmpty()) selectionMode = false검증
로컬 워크트리, head 88b5271c.
:feature:mindrecord:presentation:testDebugUnitTest → 46 tests, 0 failures
:app:compileDebugUnitTestKotlin
ktlintCheck (전 모듈)
→ BUILD SUCCESSFUL
CI 전부 success — Ktlint · Unit Tests · Android Lint · Validate Compose Preview Screenshots · CodeQL · dependency-review · guard. develop 과 충돌도 없다(merge-tree 실측).
머지 순서 — 직전 리뷰에서 걱정했던 3파전은 정리됐다. #896 은 머지됐고, 이 브랜치는 develop 을 다시 받아 «오류 재시도(로딩 방출)» 와 «재진입 갱신(미방출)» 이 둘 다 살아 있다. 남은 것은 #904 인데 그쪽은 DraftListScreen 을 건드리지 않아(리스트 화면 2종과 두 ViewModel 만) 이 PR 과 겹치지 않는다.
QA · 시안 대조 — 시각 변경이 없는 결선 수정이고, 이번 두 수정은 상태 승계·집합 연산이라 단위 테스트로 판정 가능한 성질이다. 앞선 리뷰에서 수신 화면 실기동은 열람 승인(관리자 전용) 때문에 막혀 있었고 그 사정은 그대로다 — .codex/qa-evidence/emulator/ae2e0926….json 에 기록해 둔 그대로다. 시안 대조 해당 없음.
🚀 QA 배포 권장
QA 포인트
|
📌𝘐𝘴𝘴𝘶𝘦𝘴
Closes #702
📎𝘞𝘰𝘳𝘬 𝘋𝘦𝘴𝘤𝘳𝘪𝘱𝘵𝘪𝘰𝘯
이슈가 지목한 두 화면에
ON_RESUME결선을 붙였습니다. 마인드레코드 홈(HomeScreen.kt:108)이 쓰는 패턴 그대로입니다.refresh() = load()를 그대로 부르지 않았습니다. ON_RESUME 은 화면 off/on·홈 버튼 복귀에서도 발화하는데,load()는 로딩을 방출해 보고 있던 목록·선택·필터를 스피너로 갈아치웁니다. 홈이refreshOnReturn()을 따로 둔 이유와 같아서 두 VM 에도 같은 규칙을 넣었습니다.loadJob으로 차단이슈는 두 화면 모두 "호출부 0건" 으로 적었는데, 수신 화면은
refresh()를 쓰고 있었습니다 —ReceiverMindRecordScreen.kt:71의 오류 화면 재시도 버튼입니다. 그래서refresh()를 지우지 않고 남겼습니다(재시도는 로딩을 보여야 맞습니다). 실제로 빠진 것은 재진입 결선뿐입니다.📷𝘚𝘤𝘳𝘦𝘦𝘯𝘴𝘩𝘰𝘵 · 실측
임시저장 목록을 연 상태로 앱을 백그라운드에 보내고, 서버에서 직접 그 draft 를 지운 뒤 복귀했습니다.
재조회가 실제로 나가고 서버 상태가 반영되는 것이 확인됩니다. 수정 전에는
총 1개가 그대로 남았습니다.💬𝘛𝘰 𝘙𝘦𝘷𝘪𝘦𝘸𝘦𝘳𝘴
:feature:mindrecord:presentation:testDebugUnitTest+ktlintCheck+:app:assembleDebugBUILD SUCCESSFUL, 위 실측.X-Auth-Code)이 없어 런타임 진입을 못 했습니다. 임시저장 목록과 동일한 결선·동일한 VM 규칙입니다.