You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(core): 수신자 목록의 로딩·실패 결과를 보존한다 (#1281 분리) #2045
#1281의 설정 수신자 목록에서 최초 로딩·조회 실패·실제 0건·마지막 성공 목록을 유지한 갱신 실패를 구분하려면, 현재 receiverListFlow: Flow<List<Receiver>>가 버리는 조회 결과를 보존해야 합니다. 이 이슈는 #1281에서 요청한 공통 저장소 계약의 담당 범위를 분리합니다.
완료 조건:
수신자 목록 조회의 로딩·성공·실패를 공통 도메인 결과로 전달합니다.
기존 목록 전용 구독자의 동작과 공개 계약은 유지합니다.
세션 격리 수정은 #2135에서 독립 완료합니다. 이 이슈의 결과 상태와 UI 소비는 그 격리 계약을 그대로 보존합니다.
최초 실패·실제 0건·성공 뒤 갱신 실패·재시도·취소 전파·새 결과 상태와 기존 세션 격리의 통합을 공개 저장소 계약 테스트로 검증합니다.
결과의 첫 로딩에 세션 식별을 전달하고, 인증 실패가 이전 목록을 폐기했는지 명시합니다. 화면은 새 구독과 401을 null 목록으로 추측하지 않습니다.
#1281의 실제 설정 소비자와 함께 구현하여 사용되지 않는 미래 API를 남기지 않습니다.
두 이슈는 같은 조회 결과와 소비를 완성하는 PR에서 함께 검증합니다. 빈 상태의 제품 문구·새 등록 흐름(#556)은 포함하지 않습니다.
화면 증거
보존해야 할 결과 화면이다. 최초 로딩, 최초 조회 실패, 재조회 실패에서 기존 목록을 유지한 상태다.
독립 수정 분리 (2026-09-21)
#2135는 실제 DataStore에서 중간 로그아웃 방출을 놓치면 이전 계정의 수신자 캐시를 재사용하는 저장소 결함만 고칩니다. 로딩·오류 UI와 MVI 전환 없이 현재 공개 계약으로 검증하고 완료할 수 있습니다. 화면에서 다른 계정 목록이 보이는 경로까지 재현한 것은 아닙니다. #2135의 완료 조건을 이 이슈의 별도 미완료 구현으로 중복 추적하지 않습니다. 기존 #1281의 공통 결과 계약 연계는 유지합니다.
구현 순서
#1695와 #1805가 PR #2173에서 완성한 Nav3 entry 및 MVI 목록 소비를 선행합니다. #2135의 세션 격리와 #2127의 수정 후 목록 갱신은 독립 코어 PR #2137·#2130에서 먼저 머지합니다. #1281과 이 결과 계약은 PR #2154에서 최종 Nav3 소비까지 함께 검증합니다. 선행 코어 PR의 base를 설정 구조 아래로 옮기지 않습니다.
착수 전 실측입니다. 이 이슈의 완료 조건은 이미 열린 PR #2051 에 전량 구현돼 있고, 남은 건 코드가 아니라 스택 머지 순서입니다. 라벨러의 한계 때문에 이 이슈만 미착수처럼 보입니다.
1. 완료 조건 5개가 이미 구현된 위치
PR #2051 (fix/1281-receiver-list-state, 구현 커밋 81ccfc4a0, 현재 head 37be93e1d). 항목별 대응입니다.
완료 조건
구현
공통 도메인 결과
core/domain/src/main/java/com/afternote/core/domain/model/ReceiverListState.kt 신규 22줄. Loading(previousReceivers) / Success(receivers) / Failure(previousReceivers) / SignedOut
공개 계약 추가
core/domain/.../repository/UserReceiverRepository.kt +5/-1 에 receiverListStateFlow
기존 구독자 동작 유지
core/data/.../repoimpl/UserReceiverRepositoryImpl.kt +32/-23 에서 receiverListFlow 를 receiverListStateFlow.mapNotNull 로 파생. Loading 은 null 로 걸러 방출하지 않고, Failure 는 previousReceivers.orEmpty(), SignedOut 은 emptyList()
세션 경계와 401
같은 파일의 flatMapLatest 비로그인 분기가 SignedOut, 401 에서 lastKnownReceivers = null 로 캐시 폐기
추가된 receiverListStateFlow 계약 테스트는 6개이고, 본문의 검증 항목과 그대로 대응합니다.
receiverListStateFlow - 로딩 뒤 성공한 빈 목록을 명시한다
receiverListStateFlow - 첫 실패는 빈 목록 성공과 구별되고 같은 구독에서 재시도한다
receiverListStateFlow - 갱신 로딩과 실패에 마지막 성공 목록을 보존한다
receiverListStateFlow - 401 뒤에는 다음 로딩과 일시 실패에도 폐기한 캐시를 내지 않는다
receiverListStateFlow - 로그아웃과 새 로그인 사이에 이전 계정 캐시를 격리한다
receiverListStateFlow - 수집 취소를 API에 전파하고 실패 상태와 리포트를 만들지 않는다
2. 이 이슈가 미착수로 보이는 이유
라벨러가 PR 당 이슈 1건만 잡습니다. .github/scripts/label-issues-with-open-prs.mjs:70 이 extractTitleIssueNumber(pullRequest.title) 를 쓰고, .github/scripts/validate-pr-issue-link.mjs:58 의 그 함수는 제목 끝의 참조가 정확히 하나일 때만 그 번호를 돌려줍니다. 본문은 보지 않습니다.
#2051 제목이 fix(setting): 수신자 목록 조회 상태와 재시도 연결 (#1281) 이라 #1281 만 pr-open 라벨과 issue-pr-open:2051 링크 코멘트를 받았고, 본문 두 번째 줄의 Closes #2045 는 라벨도 코멘트도 받지 못했습니다. base 가 develop 이 아니어서 GraphQL closingIssuesReferences 도 빈 배열이라 Development 칸 역시 비어 있습니다.
#2051 의 체크는 guard 하나만 red 입니다. Unit Test, Static Analysis(Ktlint, Android Lint), Screenshot, Pixel 2 네 종, CodeQL, Repository Quality 는 전부 pass 이고, 나머지는 skipping 입니다. guard 가 낸 red 는 의도된 차단입니다.
::error::#1281 이 아직 열린 #579 에 blocked — 머지 순서 위반. 선행 이슈를 먼저 완료할 것 (base: fix/579-receiver-reentry)
::warning::base 가 'fix/579-receiver-reentry' — 스택 PR 은 base 를 develop 로 PATCH 한 뒤 머지할 것
base 가 develop 이 아닌 것은 warning 이고, red 를 만든 것은 열린 선행 이슈 #579 입니다. 이 이슈도 GitHub blocked_by 로 #579 에 걸려 있습니다.
4. 다음에 여는 사람에게
지금 develop 위에서 같은 범위를 다시 쓰지 마십시오. 세 가지가 걸립니다.
첫째, 중복입니다. UserReceiverRepositoryImpl.kt 와 UserReceiverRepository.kt 는 #2051 이 이미 고쳐 둔 파일이라, 스택이 내려올 때 정면 충돌합니다.
둘째, 계약만 떼면 소비처가 없습니다. origin/develop(7d87aa3f4)에서 receiverListFlow 를 읽는 프로덕션 코드는 두 곳뿐입니다. feature/setting/presentation/.../viewmodel/ReceiverListViewModel.kt:21 과 feature/timeletter/presentation/.../viewmodel/RecipientListViewModel.kt:21 이고, 뒤쪽은 timeletter 담당 모듈입니다. 소비자 없이 receiverListStateFlow 만 올리면 완료 조건 5번이 직접 금지한 미사용 미래 API 가 됩니다.
셋째, 남은 한 소비처는 갈아엎힐 코드입니다. develop 의 ReceiverListViewModel.kt 는 stateIn 하나짜리 28줄인데, #2018 과 #2034 가 이를 MVI 로 재작성하고 #2051 이 거기에 +54/-9 를 얹습니다. 지금 그 파일에 상태 소비를 배선하면 스택이 내려온 뒤 통째로 다시 고쳐야 합니다.
그래서 이 이슈에서 할 일은 새 코드가 아니라 #1962 부터 아래에서 위로 순서대로 머지하는 것입니다. #2051 이 머지되면 이 이슈는 본문의 Closes #2045 로 함께 닫힙니다.
이 이슈를 맡은 PR 은 #2051 이다 (base fix/579-receiver-reentry). 본문 첫 절에 Closes #1281 과 Closes #2045 가 나란히 있고, 저장소가 로딩·성공·실패 결과를 보존하도록 바꾸는 공통 계약 쪽이 이 이슈의 몫이다. 갱신 실패는 마지막 성공 목록을 유지하되 401·로그아웃·새 로그인 구간에서는 이전 목록을 버린다.
pr-open 라벨을 손으로 붙였다. 라벨러가 PR 제목의 (#1281) 만 읽어 #1281 에는 라벨이 붙고 이 이슈에는 붙지 않았다. 이 이슈가 #2082 을 연 계기다. 미착수 조사에서 구현이 이미 있는데도 미착수로 보였고 중복 착수로 이어질 뻔했다.
base 가 기본 브랜치가 아니라서 GitHub 이 closing 참조를 승격하지 않아 Development 칸도 비어 있다. 라벨이 유일한 신호인데 그 신호가 절반만 나온 자리다.
이 코멘트의 마커는 라벨러가 쓰는 것과 같아서, #2082 수정이 머지돼도 같은 내용이 다시 달리지 않는다. 라벨은 그 전까지 배포본 리컨사일러가 도로 떼어 간다.
Activity
github-actions commentedon Sep 13, 2026
bug 이슈는 사이드바 Fields > Priority 로 심각도를 지정해야 합니다. 판정 기준:
값이 비어 있어도 이슈를 닫지는 않습니다. 이 안내는 이슈당 한 번만 답니다.
OnlyReadOneBook commentedon Sep 13, 2026
착수 전 실측입니다. 이 이슈의 완료 조건은 이미 열린 PR #2051 에 전량 구현돼 있고, 남은 건 코드가 아니라 스택 머지 순서입니다. 라벨러의 한계 때문에 이 이슈만 미착수처럼 보입니다.
1. 완료 조건 5개가 이미 구현된 위치
PR #2051 (
fix/1281-receiver-list-state, 구현 커밋81ccfc4a0, 현재 head37be93e1d). 항목별 대응입니다.core/domain/src/main/java/com/afternote/core/domain/model/ReceiverListState.kt신규 22줄.Loading(previousReceivers)/Success(receivers)/Failure(previousReceivers)/SignedOutcore/domain/.../repository/UserReceiverRepository.kt+5/-1 에receiverListStateFlowcore/data/.../repoimpl/UserReceiverRepositoryImpl.kt+32/-23 에서receiverListFlow를receiverListStateFlow.mapNotNull로 파생.Loading은 null 로 걸러 방출하지 않고,Failure는previousReceivers.orEmpty(),SignedOut은emptyList()flatMapLatest비로그인 분기가SignedOut, 401 에서lastKnownReceivers = null로 캐시 폐기core/data/src/test/.../UserRepositoryImplTest.kt+222줄feature/setting/.../screen/ReceiverListRouteContent.kt신규 94줄,ReceiverListViewModel.kt+54/-9,ReceiverListContract.kt+10/-2,ReceiverListStateScreenshotTest.kt112줄과 baseline 8장추가된
receiverListStateFlow계약 테스트는 6개이고, 본문의 검증 항목과 그대로 대응합니다.2. 이 이슈가 미착수로 보이는 이유
라벨러가 PR 당 이슈 1건만 잡습니다.
.github/scripts/label-issues-with-open-prs.mjs:70이extractTitleIssueNumber(pullRequest.title)를 쓰고,.github/scripts/validate-pr-issue-link.mjs:58의 그 함수는 제목 끝의 참조가 정확히 하나일 때만 그 번호를 돌려줍니다. 본문은 보지 않습니다.#2051 제목이
fix(setting): 수신자 목록 조회 상태와 재시도 연결 (#1281)이라 #1281 만pr-open라벨과issue-pr-open:2051링크 코멘트를 받았고, 본문 두 번째 줄의Closes #2045는 라벨도 코멘트도 받지 못했습니다. base 가 develop 이 아니어서 GraphQLclosingIssuesReferences도 빈 배열이라 Development 칸 역시 비어 있습니다.실측 대조입니다. #1281, #579, #1805, #1502, #1695 는 전부
pr-open을 갖고 있고, 이 이슈만bug,area:core둘뿐입니다.3. 남은 장애물은 선행 산출물이 아니라 머지 순서
스택은 아래부터 이렇습니다. 다섯 PR 전부 열려 있습니다.
#2051 의 체크는
guard하나만 red 입니다. Unit Test, Static Analysis(Ktlint, Android Lint), Screenshot, Pixel 2 네 종, CodeQL, Repository Quality 는 전부 pass 이고, 나머지는 skipping 입니다. guard 가 낸 red 는 의도된 차단입니다.base 가 develop 이 아닌 것은 warning 이고, red 를 만든 것은 열린 선행 이슈 #579 입니다. 이 이슈도 GitHub blocked_by 로 #579 에 걸려 있습니다.
4. 다음에 여는 사람에게
지금 develop 위에서 같은 범위를 다시 쓰지 마십시오. 세 가지가 걸립니다.
첫째, 중복입니다.
UserReceiverRepositoryImpl.kt와UserReceiverRepository.kt는 #2051 이 이미 고쳐 둔 파일이라, 스택이 내려올 때 정면 충돌합니다.둘째, 계약만 떼면 소비처가 없습니다. origin/develop(
7d87aa3f4)에서receiverListFlow를 읽는 프로덕션 코드는 두 곳뿐입니다.feature/setting/presentation/.../viewmodel/ReceiverListViewModel.kt:21과feature/timeletter/presentation/.../viewmodel/RecipientListViewModel.kt:21이고, 뒤쪽은 timeletter 담당 모듈입니다. 소비자 없이receiverListStateFlow만 올리면 완료 조건 5번이 직접 금지한 미사용 미래 API 가 됩니다.셋째, 남은 한 소비처는 갈아엎힐 코드입니다. develop 의
ReceiverListViewModel.kt는stateIn하나짜리 28줄인데, #2018 과 #2034 가 이를 MVI 로 재작성하고 #2051 이 거기에 +54/-9 를 얹습니다. 지금 그 파일에 상태 소비를 배선하면 스택이 내려온 뒤 통째로 다시 고쳐야 합니다.그래서 이 이슈에서 할 일은 새 코드가 아니라 #1962 부터 아래에서 위로 순서대로 머지하는 것입니다. #2051 이 머지되면 이 이슈는 본문의
Closes #2045로 함께 닫힙니다.OnlyReadOneBook commentedon Sep 13, 2026
이 이슈를 맡은 PR 은 #2051 이다 (base
fix/579-receiver-reentry). 본문 첫 절에Closes #1281과Closes #2045가 나란히 있고, 저장소가 로딩·성공·실패 결과를 보존하도록 바꾸는 공통 계약 쪽이 이 이슈의 몫이다. 갱신 실패는 마지막 성공 목록을 유지하되 401·로그아웃·새 로그인 구간에서는 이전 목록을 버린다.pr-open라벨을 손으로 붙였다. 라벨러가 PR 제목의(#1281)만 읽어 #1281 에는 라벨이 붙고 이 이슈에는 붙지 않았다. 이 이슈가 #2082 을 연 계기다. 미착수 조사에서 구현이 이미 있는데도 미착수로 보였고 중복 착수로 이어질 뻔했다.base 가 기본 브랜치가 아니라서 GitHub 이 closing 참조를 승격하지 않아 Development 칸도 비어 있다. 라벨이 유일한 신호인데 그 신호가 절반만 나온 자리다.
이 코멘트의 마커는 라벨러가 쓰는 것과 같아서, #2082 수정이 머지돼도 같은 내용이 다시 달리지 않는다. 라벨은 그 전까지 배포본 리컨사일러가 도로 떼어 간다.
github-actions commentedon Sep 23, 2026
이 이슈를 맡은 PR 은 #2154 다 (base
fix/2135-receiver-session-cache).base 가 기본 브랜치(
develop)가 아니라서 GitHub 은 이 이슈의 Development 칸에아무것도 표시하지 않는다. 본문에
Closes #N이 있어도 마찬가지다 — 그 링크는 기본 브랜치를target 하는 PR 에서만 만들어진다.
PR 이 닫히거나 머지되면 이 이슈의
pr-open라벨은 다음 리컨사일에서 떨어진다.fix(setting): 다시 구독한 첫 조회가 실패해도 보이던 수신자 행을 남긴다 (#2045)
fix(core): 수신자 목록 조회의 로딩·실패를 상태 Flow 로 보존한다 (#2045)
fix(setting): 다시 구독한 첫 조회가 실패해도 보이던 수신자 행을 남긴다 (#2045)
test(core,setting): 목록 모델에서 빠진 Receiver.authCode 인자를 테스트에서 걷어낸다 (#2045)
fix(core,setting): Nav3 목록 조회 상태를 MVI 재시도로 연결한다 (#2045)
refactor(core,setting,afternote): 수신자 목록 조회 실패 안내와 갱신 실패 배너를 core:ui …
Merge remote-tracking branch 'origin/develop' into fix/714
fix(timeletter): 수신인 선택을 receiverListStateFlow 로 옮겨 실패를 빈 목록과 가른다 (#714)