Skip to content

한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨 #598

Closed
1 of 1 issue completed

Description

@OnlyReadOneBook

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

수신자 식별·전환 UX·컨텍스트 수명·키 보관 정책을 #1950으로 분리했다. 이 이슈는 확정 정책의 격리 구현과 회귀 검증을 소유하며, #607 선행을 유지한다. 로컬 카드 영속화가 반드시 먼저라는 기존 구현 가정은 #1950에서 재판정한다.


📜 Overview (Required)

한 기기를 여러 수신자가 사용할 때 서로의 기록이 격리되지 않습니다.

수신자는 회원 계정이 아니라 마스터 키(UUID)로만 식별되는 구조라, 앱에 "이 기기의 수신자가 누구인가" 라는 개념이 없습니다. 그 결과 받은 기록함이 사람별로 나뉘지 않고 하나로 합쳐집니다.

SenderEntry 에 소유자를 구분하는 필드가 없습니다.

data class SenderEntry(
    val id: String,          // 등록 시 만든 로컬 UUID — 수신자 식별자 아님
    val name: String,        // 사용자가 붙인 별칭
    val authCode: String?,   // 마스터 키가 카드에 함께 저장됨
    ...
)

재현

  1. 수신자 A 가 발신자 X 의 마스터 키로 카드 등록
  2. 같은 기기에서 수신자 B 가 발신자 Y 의 카드 등록
  3. 두 카드가 같은 목록에 노출됨. B 가 A 의 카드를 눌러도 SenderDetailViewModel 이 카드에 붙은 authCode 를 헤더에 복원하므로 그대로 열람됨

여기에 두 건이 겹쳐 격리가 더 무너집니다.

현재 유일한 격리 수단은 앱 재시작입니다. SenderRegistry 가 in-memory(MutableStateFlow) 라 껐다 켜면 목록이 비는데, 이는 사용자 입장에서는 데이터 소실이지 격리 기능이 아닙니다.

📌 Child Issue(Optional)

📍 Note (Optional)

유족이 한 기기를 함께 쓰는 상황이 이 도메인에서 예외적이지 않습니다. 대응 방향은 제품 결정이 필요합니다.

컨텍스트 전환 UI, 카드에 receiverId 추가, SenderRegistry 영속화 중 어느 것도 구현 계획으로 확정하지 않는다. 선택과 근거는 #1950에서 관리하고, 확정 정책과 서버 목록 계약이 준비된 뒤 남는 격리 결함만 구현 범위로 정한다.

07-29 시안 실측 — 두 방향 어느 쪽에도 해당하는 화면이 없습니다. "전환" 으로 검색되는 프레임은 전부 "추모 계정으로 전환"·"사후 전달 조건"(자동 전달/특정 날짜) 맥락이고, 수신자↔발신자 전환이나 수신자 컨텍스트 초기화 UI 는 존재하지 않습니다. 관련 사용자 전환·초기화 흐름은 PM에게 확인합니다. 디자이너에게 별도로 요청하지 않습니다.

📍 근거 정리 (2026-07-29 실측)

받은 기록함과 발신자 카드 목록은 시안에 존재합니다(1726:19601·1726:19803·1726:24632·2372:23931). 카드 목록 구조 자체는 정상이며 구현이 임의로 만든 화면이 아닙니다.

노션 「IA」에는 이 화면이 없고 진입이 "보안링크 접속" 으로만 돼 있으나, 그 문서는 2025-12-04 최종 편집(팀 리빌딩 2026-03 이전)이라 현재 계획을 반영하지 않습니다.

따라서 이 이슈는 "기획에 없는 상황" 이 아니라 시안에 있는 화면의 다중 사용자 규칙이 아직 정해지지 않은 것입니다. 한 기기를 여러 수신자가 쓸 때 카드·마스터 키·본인 확인을 어떻게 다룰지가 시안·기획 어디에도 없습니다.

Activity

  1. changed the issue type fromtoon Jul 29, 2026
  2. changed the title [-]fix(afternote): 한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨[/-] [+]한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨[/+] on Aug 28, 2026
  3. added
    bugSomething isn't working
    area:afternote애프터노트 기능 영역
    and removed
    enhancementNew feature or request
    on Aug 28, 2026
  4. OnlyReadOneBook commented on Sep 13, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

    실측 기록 (2026-09-13): 본문 전제가 서버에서 바뀌었다

    코드로 뗄 독립 범위가 없다고 판정해 구현은 내지 않는다. 대신 다음 착수자가 낡은 전제로 시작하거나 곧 폐기될 코드를 쓰는 일을 막을 만큼만 남긴다.

    판정 경로는 Afternote-FE origin/develop 7d87aa3 와 Afternote-BE origin/main 3593aa3 실코드다. BE 축의 상세 근거는 #1949 의 완료 조건 대조 코멘트에 있어 여기서 되풀이하지 않는다.

    1. 수신자 식별이 마스터 키에서 회원 계정으로 바뀌었다

    본문 첫 단락의 전제, 곧 "수신자는 회원 계정이 아니라 마스터 키(UUID)로만 식별되는 구조라 앱에 이 기기의 수신자가 누구인가 라는 개념이 없다" 는 Afternote/Afternote-BE#289(2026-09-08 머지) 이후 성립하지 않는다.

    • 목록은 ReceivedRecordService.java:42 의 findAllByAcceptedUserIdOrderByIdDesc(userId) 다.
    • 상세와 하위 경로는 같은 파일 :133-135 의 findByIdAndAcceptedUserId 를 먼저 타고, 비면 RECEIVER_NOT_FOUND 로 끊는다.
    • 자격은 JWT 다. ReceivedRecordController.java 의 목록 핸들러 인자가 @UserId Long userId 하나이고 헤더 파라미터가 없다.
    • accessCode 와 ReceiverAuthController 는 BE 소스에서 사라졌다. ReceivedRecordBoxResponse.java 의 필드에도 accessCode 가 없다.

    격리를 서버가 계정 단위로 보장한다. 다만 이건 코드 경로를 읽은 결과이지 실서버 HTTP 응답을 받아 본 결과가 아니고, 운영 DB 실측도 아니다. Afternote/Afternote-BE#93 은 아직 OPEN 이다.

    2. develop 에 남는 결함은 FE 가 구 계약을 붙들고 있는 것 하나로 수렴한다

    • 카드 목록이 로컬 stub 이다. SenderRegistry.kt:23-28 이 @Singleton + MutableStateFlow 다.
    • 열람 컨텍스트가 전역 헤더다. ReceiverAuthInterceptor.kt:25,32 가 /receiver-auth/ 로 나가는 요청에 저장된 코드를 X-Auth-Code 로 붙인다.

    둘 다 #607 의 record-boxes 전환이 통째로 걷어내는 대상이다. 전환 전에 카드에 소유자를 붙이거나, 컨텍스트 전환 UI 를 세우거나, SenderRegistry 를 영속화하면 전환 때 다시 뜯는다. 본문 Note 가 이미 셋 중 어느 것도 구현 계획으로 확정하지 않는다고 적었는데, 그 결론이 이제 서버 계약으로도 뒷받침된다.

    3. 전환 뒤 이 이슈에 남을 일

    새 계약에는 기기에 공유될 마스터 키 자체가 없고, "이 기기의 수신자" 는 로그인 계정이 된다. 계정 전환 시 격리 수단은 develop 에 이미 있다.

    • 수신자 마스터 키 저장소가 SESSION scope 로 등록돼 있다(ReceiverMasterKeyDataStoreModule.kt:22, 저장 파일 afternote_receiver_auth).
    • AuthRepositoryImpl.kt:148 의 logout() 과 :43 의 clearSession() 이 clearScope(StoreScope.SESSION) 을 부른다. clearSession() 은 탈퇴(UserRepositoryImpl.kt:56)와 토큰 재발급 실패 경로에서 불린다.

    따라서 #598 의 잔여 범위는 별도 격리 구현보다 전환 뒤 회귀 검증 쪽으로 기운다. 관측이고, 정책 확정은 #1950, 자격과 응답 계약은 #1949 소관이라 여기서 확정하지 않는다.

    4. 재현 절차의 항목별 생사

    3단계(카드에 붙은 키를 헤더에 복원해 그대로 열람)는 여전히 살아 있다. SenderDetailViewModel.kt:96 과 :200 이 receiverRepository.saveMasterKey(...) 를 부른다.

    "현재 유일한 격리 수단은 앱 재시작" 은 절반만 맞다. SenderRegistry 가 in-memory 라 카드 목록은 비지만(SenderRegistry.kt:27), 마스터 키는 DataStore 에 남아 재시작을 넘긴다(ReceiverMasterKeyDataSource.kt:17,42). 수신자 흐름에는 로그인이 없어 3번의 SESSION 정리 경로에 닿지 않는다.

    겹침 두 건은 아래 정정에 적었다.

    5. 착수 지형

    본문이 develop 실태와 어긋나는 항목

    본문은 고치지 않고 정정만 여기 남긴다.

    1. 전제. 위 1번 그대로다. 마스터 키 기반 식별은 서버 계약에서 사라졌다.
    2. 코드 블록의 SenderEntry.authCode 필드명. origin/develop 의 필드는 masterKey 다(SenderEntry.kt:24). 소유자 구분 필드가 없다는 지적 자체는 SenderEntry.kt:21-28 기준 그대로 유효하다.
    3. 겹침 1, 곧 "본인 확인이 전역 boolean 이라 A 가 통과해 두면 B 는 이메일 인증 없이 마스터 키 단계로 직행 (수신자 본인 확인 캐시가 발신자 구분 없이 전역 저장 — 첫 인증 후 이메일 관문 무력화 #597)". 수신자 본인 확인 캐시가 발신자 구분 없이 전역 저장 — 첫 인증 후 이메일 관문 무력화 #597 은 CLOSED COMPLETED 이고 develop 에서 고쳐져 있다. IdentityVerificationRepositoryImpl.kt:42-54 가 senderId 단위 Set 으로 격리하고, DeliveryVerificationFlowViewModel.kt:45,55 가 자기 senderId 의 인증만 읽는다. 전역 boolean 도 디스크 잔존값 조회도 없다.
    4. 겹침 2, 곧 "clearAuthCode() 호출처가 0건이라 이전 사람의 마스터 키가 계속 남음 (fix(core): 계정 전환 시 이전 사용자의 수신자 목록이 그대로 노출 — 로그아웃에 receiverCache 정리 없음 #588)". clearAuthCode 는 origin/develop 전체 검색에서 선언 포함 0건이라 심볼 자체가 없다. 키가 남는 증상은 유효하지만 인용한 심볼과 메커니즘을 갈아야 한다. 실제 저장소는 ReceiverMasterKeyDataSource 이고, 지우는 지점은 3번의 SESSION 정리뿐이다.
    5. 자식 이슈로 건 fix(core): 계정 전환 시 이전 사용자의 수신자 목록이 그대로 노출 — 로그아웃에 receiverCache 정리 없음 #588 과 수신자 본인 확인 캐시가 발신자 구분 없이 전역 저장 — 첫 인증 후 이메일 관문 무력화 #597 이 둘 다 CLOSED COMPLETED 인데 본문은 아직 진행 중 결함으로 서술한다. 게다가 fix(core): 계정 전환 시 이전 사용자의 수신자 목록이 그대로 노출 — 로그아웃에 receiverCache 정리 없음 #588 은 core 의 receiverCache(계정 전환 시 이전 사용자의 수신자 목록 노출) 건이라, 본문이 인용한 마스터 키 잔존과는 다른 캐시다.
    6. 본문이 전제하는 그림, 곧 서버가 X-Auth-Code 를 이메일로 확장해 같은 이메일의 기록함을 내려준다는 서술(ReceiverAuthApiService.kt:61-64 KDoc). 새 계약에는 receiver-auth 매핑도 accessCode 도 없다. FE 는 아직 ReceiverAuthApiService.kt:65 의 receiver-auth/record-boxes 와 ReceiverAuthDto.kt:132 의 non-null accessCode 를 붙들고 있고, 회귀 가드 ReceivedRecordBoxContractTest 가 같은 필드를 고정한다. 서버가 그 필드를 끊는 순간 목록 파싱이 예외로 떨어진다.
  5. OnlyReadOneBook commented on Sep 19, 2026

    @OnlyReadOneBook
    CollaboratorAuthor

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

    기다리는 결정: 다중 수신자 격리 방향(#1950).

    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