Skip to content

받은 기록함 발신자 카드가 앱 재시작 시 소실 — SenderRegistry 가 메모리 전용 #599

Closed

Description

@OnlyReadOneBook

2026-09-08 범위 분리·착수 조건

로컬 저장 필요성·SESSION 수명·암호화/키 보관 선택을 #1950으로 분리했다. #607 서버 정본 연동으로 증상이 해소되는지 확인한 뒤 남는 복원 요구만 구현한다. PR #1652는 미확정 로컬 영속화 제안을 철회하여 종료했다(2026-09-08). 기존 댓글의 구현 완료 표현은 배포/머지 완료나 저장 정책 확정을 의미하지 않는다. #1950과 #607을 실제 blocked_by로 등록했다.


📜 Overview (Required)

받은 기록함의 발신자 카드가 앱 재시작 시 전부 사라집니다.

SenderRegistry 가 메모리에만 보관합니다.

private val _senders = MutableStateFlow<List<SenderEntry>>(emptyList())

카드에는 마스터 키(authCode)·발신자 이름·열람 인증 상태가 담겨 있는데, 프로세스가 죽으면 통째로 초기화됩니다.

그래서 이렇게 됩니다.

  • 마스터 키 입력 → 이메일 인증 → 서류 제출 → 승인까지 마쳐도, 앱을 껐다 켜면 목록이 비어 그 발신자에 다시 닿을 수 없음
  • 서버에는 delivery-verification 승인 기록이 남아 있는데 로컬에 진입 수단이 없어 무의미해짐
  • 다시 보려면 마스터 키부터 재입력해 카드를 새로 만들어야 함. 마스터 키를 분실했다면 발신자에게 다시 받아야 하는데, 이 서비스 특성상 발신자에게 물을 수 없는 상황이 전제입니다

실기기 재현 — 앱 재시작 직후:

Image

📌 Child Issue(Optional)

No response

📍 Note (Optional)

로컬 DataStore·SESSION·평문 또는 암호화 저장 구현안은 제거했다. #1950의 정책과 #607의 서버 정본 결과를 확인해 재시작 복원 문제가 남는지 먼저 검증한다. 남는 요구가 확인될 때만 확정 계약에 따른 작업 범위를 정한다.

Activity

  1. OnlyReadOneBook commented on Aug 25, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    재현 확인 (develop 4e0a648eb, 2026-08-25 15:12 KST, 서버 정상 응답 상태)

    1. Welcome → 전달 받은 기록 확인하기 → + → 이름 QA Sender 입력 → 발신자 등록하기
    2. 목록에 카드 정상 추가, 상세 진입도 정상 (기록: 보관 중 / 상태: 열람 불가 / 신청일·승인일 안내 문구 표시)
    3. 앱 재시작 → "아직 등록된 발신자가 없습니다." 로 복귀. 등록분 소실.

    새벽 순회(서버 폐쇄 시간대)와 달리 이번에는 서버가 정상 응답하는 상태에서 재현했으므로, 네트워크 실패로 인한 소실이 아님이 확인됩니다.

    함께 확인된 것 — #607

    등록 시점 전후로 HTTP 요청이 한 건도 나가지 않았습니다. OkHttp 로그의 마지막 항목이 15:10:28(앱 시작 시 조회)이고 15:12 등록에 대응하는 요청이 없습니다. #607(서버 목록 API 미사용·로컬 stub)과 같은 뿌리로 보입니다 — 메모리에만 쓰니 영속도 안 되고 서버에도 안 갑니다.

    참고

    수신자 인증 4단계 진입(1단계 "수신자 본인 확인")까지는 #919 의 :feature:receiver 대규모 이전 이후에도 정상 도달합니다.

  2. changed the title [-]fix(afternote): 받은 기록함 발신자 카드가 앱 재시작 시 소실 — SenderRegistry 가 메모리 전용[/-] [+]받은 기록함 발신자 카드가 앱 재시작 시 소실 — SenderRegistry 가 메모리 전용[/+] on Aug 28, 2026
  3. added
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Aug 30, 2026
  4. OnlyReadOneBook commented on Aug 30, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    영속화는 PR #1652 로 닫았습니다 — 다만 이 이슈는 열어 둡니다

    PR #1652 가 발신자 카드를 버전 있는 JSON 스냅샷으로 SESSION DataStore 에 저장하고, 재기동 뒤 순서·마스터 키·신원·열람 인증 상태를 복원합니다. 본문이 든 재현 경로(등록 → 인증 → 승인까지 마치고 앱 재시작하면 목록이 빔)는 사라집니다.

    닫는 참조를 걸지 않았습니다. 이 이슈가 열려 있는 #607(받은 기록함이 서버 목록 API 를 안 쓰고 로컬 stub)에 blocked_by 로 걸려 있어 merge-order guard 가 자동 종료를 막습니다.

    관계를 임의로 해제하지 않은 이유는 #607 이 지금 다른 세션에서 작업 중이라, 「서버 목록이 오면 로컬 영속화가 어떤 의미를 갖는가」를 그쪽 결과 없이 판정할 수 없기 때문입니다. 발신자 카드(SenderEntry, 마스터 키로 등록하는 로컬 개념)와 record-boxes 목록이 같은 것으로 수렴하는지에 따라 이 저장소의 역할이 달라집니다.

    → #607 이 닫힌 뒤 이 이슈를 다시 봐 주세요. 그때 (a) 로컬 스냅샷이 서버 목록의 캐시로 남는지 (b) 통째로 걷히는지가 갈리고, 그 판정이 나면 이 이슈도 함께 닫힙니다.

    본문이 남긴 결정 사항 — 암호화는 하지 않았습니다

    본문이 「마스터 키가 평문으로 남는 문제가 있어 암호화 여부를 함께 정해야 한다」로 남긴 자리입니다.

    • ReceiverAuthCodeDataSource 가 같은 종류의 비밀(마스터 키) 을 이미 평문 SESSION Preferences DataStore 에 둡니다.
    • 토큰 저장소도 같습니다.
    • 저장소 어디에도 암호화 DataStore·Tink 관례가 없습니다.

    여기만 암호화하면 같은 성격의 비밀이 두 방식으로 저장되어 수명·키 관리·마이그레이션 계약이 이원화됩니다. 평문 하나로 일관되게 두고, 암호화 전환은 저장소 전체를 아우르는 별도 마이그레이션으로 다루는 편이 낫다고 봤습니다. 근거는 구현 KDoc 에도 남겼습니다.

    #598 의 설계는 앞지르지 않았습니다

    본문이 「#598(목록 주인 구분)의 선행 조건이기도 하다」고 적은 대로, 카드에 수신자 식별자를 붙이면 사람별로 나눌 수 있습니다. 다만 #598 은 제품 결정이 미정이라(기기당 1명 전제 + 전환 진입점 / 카드에 receiverId) 스키마에 schemaVersion 과 ignoreUnknownKeys 로 자리만 열어 두고 굳히지 않았습니다.

  5. OnlyReadOneBook commented on Sep 2, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    #607 과의 blocked_by 를 해제합니다. PR #1652 의 영속화는 서버 목록 API 없이 로컬 DataStore 만으로 이 이슈의 완료 조건(재기동 뒤 카드·마스터 키·열람 인증 상태 복원, 암호화 여부 결정)을 전부 채우므로 #607 은 이 이슈의 선행이 아닙니다. #607 은 그대로 열려 있고 범위도 줄지 않았습니다.

    #607 이 record-boxes 를 목록의 원본으로 삼게 되면 이 저장소가 서버 목록의 캐시로 남을지 걷힐지는 #607 의 범위입니다. 그 판단은 이 이슈의 완료 여부와 별개입니다.

  6. removed
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Sep 8, 2026
  7. OnlyReadOneBook commented on Sep 13, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    착수 전 실측 (origin/develop 7d87aa3f4, 2026-09-13)

    코드로 뗄 독립 범위가 없어 구현 대신 기록만 남깁니다. 본문 2026-09-08 블록의 완료 조건이 "#607 서버 정본 연동으로 증상이 해소되는지 확인한 뒤 남는 복원 요구만 구현한다" 한 줄이라 쪼갤 체크리스트가 없고, 그 한 항목이 통째로 #607·#1950 에 걸려 있습니다. 다만 착수 조건이 미뤄 둔 "해소되는가" 의 답과 본문 전제 두 개는 지금 확정되므로 적어 둡니다.

    1. 증상은 그대로다

    SenderRegistry 는 여전히 in-memory @Singleton 이고(SenderRegistry.kt:23-28, _senders = MutableStateFlow(emptyList())), 목록 화면이 그걸 그대로 흘려보냅니다(ReceivedRecordsViewModel.kt:20). 머지된 것 중 이 증상을 건드린 건 없습니다. 철회된 구현은 브랜치 fix/599-sender-registry-persistence(193b91c1e, origin 에도 남아 있음)에 있고 PR #1652 는 머지 없이 닫혔습니다(2026-09-08).

    2. 본문 정정: 마스터 키는 재시작으로 사라지지 않는다

    본문의 "마스터 키를 분실했다면 발신자에게 다시 받아야" 는 develop 과 어긋납니다. ReceiverMasterKeyDataSource.saveCode 가 Preferences DataStore 파일 afternote_receiver_auth 에 쓰고(ReceiverMasterKeyDataSource.kt:42-51, ReceiverMasterKeyDataStoreModule.kt:22), 그 scope 는 StoreScope.SESSION 이라 프로세스 사망이 아니라 로그아웃·탈퇴에서만 비워집니다(StoreScope.kt:10-14). 재시작 뒤에도 ReceiverAuthInterceptor 가 그 값을 X-Auth-Code 로 계속 붙입니다(ReceiverAuthInterceptor.kt:25-34).

    실제로 잃는 것은 마지막으로 검증한 발신자의 키가 아니라 그 이전 발신자들의 키입니다. 슬롯이 하나뿐이라 saveMasterKey 가 덮어씁니다(ReceiverRepositoryImpl.kt:54-56).

    같은 이유로 본문의 "서버에는 delivery-verification 승인 기록이 남아 있는데 로컬에 진입 수단이 없어 무의미해짐" 도 절반만 맞습니다. 헤더 자격 자체는 디스크에 살아 있고, 없어지는 것은 그 헤더를 쓸 화면(카드)입니다. 단 3번 때문에 지금은 그 헤더가 갈 곳이 없습니다.

    3. 본문 정정: 서버 정본 쪽 전제가 바뀌었다

    본문과 #607 이 전제한 GET /api/v1/receiver-auth/record-boxes 는 BE main 에 더 이상 없습니다. BE PR #289(머지 2026-09-08, 커밋 7009eed2b)가 ReceiverAuthController 와 ReceiverAuthService 를 통째로 삭제했습니다. 목록은 ReceivedRecordController 의 /api/v1/received-records 로 옮겼고, X-Auth-Code 가 아니라 로그인 userId 로 고릅니다(ReceivedRecordService.getRecordBoxes → findAllByAcceptedUserIdOrderByIdDesc).

    FE 에 닿는 결과는 셋입니다.

    전환 자체와 2026-09-08 이전 수신자 데이터 문제는 #2079 가 소유합니다. 여기서는 이 이슈의 착수 조건에 닿는 부분만 적었습니다.

    참고로 #607 본문의 "record-boxes·RecordBox 전체 검색 결과 호출 0건" 도 develop 에서는 사실이 아닙니다. SenderDetailViewModel.kt:178-179 가 승인 상태에서 호출합니다.

    4. 그래서 이 이슈에 남는 복원 요구

    "#607 로 해소되는가" 는 #607 완료를 기다리지 않고 지금 답이 나옵니다. 서버 목록이 어느 경로로 오든, 카드가 쥔 값 중 서버가 돌려줄 수 없는 것은 이렇습니다.

    • 별칭 SenderEntry.name. 사용자가 입력하는 값이고 서버에는 없습니다. 목록과 상세가 찍는 이름이 실명이 아니라 이것입니다(ReceivedRecordsScreen.kt:169, SenderDetailViewModel.kt:188).
    • 로컬 id. SenderRegistry.kt:37 의 UUID 이고 상세 라우트 키가 이 값입니다(ReceiverRoute.kt:41-47).
    • 마스터 키. 3번대로 새 응답에 accessCode 가 없습니다.

    앞의 둘은 순수한 로컬 복원 요구라 이 이슈에 남고, PR #1652 가 철회된 이유였던 마스터 키 평문 보관과는 다른 질문입니다. 셋째는 "복원하는가" 가 아니라 "새 모델에서 필요한가" 부터 물어야 합니다. 새 계약에서 기록함 진입 수단은 마스터 키가 아니라 로그인 계정이기 때문입니다. 그 판단은 #1949(접근 자격 계약)·#1950(키 보관 정책) 범위이므로 여기서 정하지 않습니다.

    5. 선후와 충돌

    blocked_by 실조회(2026-09-13):

    사슬이 BE#93 에서 바닥나 FE 안에서 끊을 수 있는 선행이 없습니다.

    열린 PR 62건 본문의 닫는 참조를 전수 검색한 결과 #599·#607·#1950·#1949 를 닫는 PR 은 0건입니다. 다만 PR #2039(open, base develop, Closes #1803)가 receiver presentation 의 ViewModel 7개를 MVI 로 옮깁니다. SenderRegistry.kt 자체는 한 줄도 안 건드리지만 ReceivedRecordsViewModel·ReceivedRecordsScreen·SenderDetailViewModel 이 전부 그 PR 안에서 바뀌고, ReceivedRecordsViewModel 은 internal + MviViewModel 로 이동하면서 상태가 ReceivedRecordsUiState 로 감싸집니다. 이 이슈 착수자는 #2039 위에서 시작해야 합니다.

    본문은 고치지 않았습니다

    위 정정 셋 다 완료 조건을 좁히는 게 아니라 전제를 바꾸는 내용이라, 본문 편집 대신 이 코멘트로 남깁니다.

  8. OnlyReadOneBook commented on Sep 19, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    2026-09-19 정리로 닫는다. 이 이슈의 완료 조건은 PM의 결정에 달려 있어 결정 전에는 FE 가 무엇을 만들지 쓸 수 없다. 그런 이슈는 FE 저장소에 열어 두지 않고 결정이 나면 그때 새 이슈로 다시 연다.

    기다리는 결정: 로컬 저장 정책(#1950)과 서버 정본(#607).

    PM 질문은 창구가 정해지는 대로 보낸다(일혁이 목록 보관).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:afternote애프터노트 기능 영역bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions