Repository navigation
fix(timeletter): 목록·상세 ViewModel 저장소 조회 6곳의 취소 전파를 보존한다 (#1501 분리) #2041
Description
작업 유형
bug — 버그·오동작
주 담당 모듈
timeletter — 타임레터
개요
#1501 합본에서 타임레터 presentation의 저장소 suspend 조회를 stdlib runCatching으로 감싸 취소를 일반 실패로 소비하는 잔여를 분리한다. 설정은 #1501에서 완료한다. 이미 해결된 호출과 동기 코드까지 다시 치환하지 않는다.
현재 잔여 6곳
실측 기준 develop의 93e983fe1ea9c813a8673a7c59e401a5ea40aabc. 경로 접두사는 feature/timeletter/presentation/src/main/kotlin/com/afternote/feature/timeletter/presentation/viewmodel/다.
| 파일 | 현재 줄 | 저장소 호출 |
|---|---|---|
TimeletterViewModel.kt |
63·64 | getReceivers(async)·getTimeLetters |
TimeLetterDetailViewModel.kt |
51·52 | getReceivers(async)·getTimeLetter |
RecipientTimeletterViewModel.kt |
40 | getReceivedTimeLetters |
RecipientTimeLetterDetailViewModel.kt |
47 | getReceivedTimeLetterDetail |
원래 #1501의 4f5e54689 표에는 임시보관함 3곳까지 타임레터 9곳이었다. 현재 DraftLetterViewModel의 조회·선택 삭제·전체 삭제는 명시적인 CancellationException 재던짐을 이미 사용한다. TimeletterViewModel.deleteTimeLetter와 작성기의 조회/수정도 현재 취소 보존 helper를 사용하므로 이 수정의 추가 대상이 아니다. 기존 임시보관함 finally의 busy 정리는 유지한다.
현재 PR과의 관계
- fix(timeletter): 타임레터 하위 화면 5종의 재진입 갱신 (#703 분리) #1921 / PR #1905의 최신 HEAD
f0cfec520bb5665359c6a168c8cae603e4a04600는 하위 화면 재진입과 loadJob/상태 보존을 추가하지만 발신 상세·수신 목록·수신 상세의 위 4개 호출은 여전히 stdlibrunCatching이다. 해당 PR은 이 결함의 중복 완료물이 아니다. 기존 PR에 수정과 회귀를 함께 반영하거나 최신 load 경계를 base로 잔여만 수정할 수 있다. 재진입·상태 보존을 별도 구현으로 다시 작성하지 않는다. - 메인
TimeletterViewModel의 2곳은 fix(timeletter): 타임레터 하위 화면 5종의 재진입 갱신 (#1921) #1905 변경 대상이 아니므로 독립 수정 가능하다. 파일 겹침만으로 전체 6곳을 차단하지 않는다. - 서버 실패가 빈 정상 화면·무음 상태로 수렴 — 목록·수정·임시저장·수신인 선택 복구 수단 부재 #714 / PR #1365의 현재 변경은 편집기/수신자 목록 오류 계약이며 위 6곳을 고치지 않는다. #1692의 작성기 저장 UseCase 수렴도 위 목록·상세 조회를 이동하지 않는다. 두 이슈를 이 잔여의 무조건적인 선행으로 두지 않는다.
보존할 동작과 완료 조건
취소 이후 onFailure/비-suspend 후속 처리가 실행될 수 있다는 #1501의 문제 정의를 유지한다. 보조 수신자 조회의 일반 실패 fallback은 계속 허용하되 취소를 빈 이름 맵·UI Error로 바꾸지 않는다. 기존 재조회·성공·실패 정책을 새로 결정하지 않는다.
- 위 6개 실제 저장소 suspend 호출에 core:common의 기존
runCatchingCancellable또는 동등한 취소 재던짐 계약을 적용한다. 착수 시 최신 PR에서 선반영됐으면 그 부분은 재작성하지 않는다. - 대상 목록·상세 ViewModel에서 취소된 로드가 실패 상태·진단·이벤트 등 일반 실패 후속 처리를 실행하지 않는 회귀 테스트를 둔다. async 보조 조회의 취소와 일반 조회 실패 fallback을 구분한다.
- 해당 변경이 닿는 기존 성공/일반 실패·재진입 상태 보존 테스트를 유지하고 관련 단위 테스트·ktlint·앱 소비 컴파일을 통과한다.
-
LocalDate.parse, ContentResolver 조회, JSON 직렬화 등 동기 runCatching은 기계적으로 바꾸지 않는다. 데이터/첨부 IO 계층·새 retry 정책·새 상태 계층은 범위 밖이다.
참고
- 분리 원본 fix(setting): ViewModel 저장소 호출의 취소 전파 잔여를 마무리한다 (#661 후속) #1501. 역사적 data 계층 선행 fix(core): suspend 를 감싼 runCatching 이 CancellationException 을 Result 로 소비 — 취소 전파 보존 전수 정정 #661·fix(mindrecord): data 계층 runCatching 이 CancellationException 을 Result 로 소비 — 취소 전파 보존 #670·#671은 CLOSED이며 현재 blocked_by를 새로 만들 이유는 없다.
- 타임레터 계측 분리: refactor(timeletter): handled 실패를 분류하고 필요한 ErrorReporter 계측을 추가한다 (#963 분리) #2040. 계측 여부와 별개로 취소를 정상 실패로 소비하지 않는 계약을 유지한다.
- refactor(timeletter): 생성·수정·등록 게이트를 SaveTimeLetterUseCase로 수렴한다 #1692 본문이 인용하던 #1501의 타임레터 관련 참고 링크는 이 이슈로 교체할 수 있으나, 이 6곳은 작성기 저장 경계 이동과 중복되지 않는다.
- 인접 #1108은 첨부·업로드 내부 IO/dispatcher 경계로 다른 대상이다.
- 원문 근거: Android 코루틴 모범 사례, Kotlin runCatching.
Activity
github-actions commentedon Sep 13, 2026
bug 이슈는 사이드바 Fields > Priority 로 심각도를 지정해야 합니다. 판정 기준:
값이 비어 있어도 이슈를 닫지는 않습니다. 이 안내는 이슈당 한 번만 답니다.