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
한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨 #598
유족이 한 기기를 함께 쓰는 상황이 이 도메인에서 예외적이지 않습니다. 대응 방향은 제품 결정이 필요합니다.
컨텍스트 전환 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 이전)이라 현재 계획을 반영하지 않습니다.
따라서 이 이슈는 "기획에 없는 상황" 이 아니라 시안에 있는 화면의 다중 사용자 규칙이 아직 정해지지 않은 것입니다. 한 기기를 여러 수신자가 쓸 때 카드·마스터 키·본인 확인을 어떻게 다룰지가 시안·기획 어디에도 없습니다.
changed the title [-]fix(afternote): 한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨[/-][+]한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨[/+]on Aug 28, 2026
코드로 뗄 독립 범위가 없다고 판정해 구현은 내지 않는다. 대신 다음 착수자가 낡은 전제로 시작하거나 곧 폐기될 코드를 쓰는 일을 막을 만큼만 남긴다.
판정 경로는 Afternote-FE origin/develop7d87aa3 와 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 정리 경로에 닿지 않는다.
대신 refactor(receiver): 구 수신자 흐름 MVI 전환 철회 (#1803) #2039(open, base develop, refactor/1803-receiver-mvi)가 SenderDetailViewModel·ReceivedRecordsViewModel·SenderRegistrationViewModel·MasterKeyViewModel·DeliveryVerificationFlowViewModel 을 전부 MVI 로 재작성 중이다. 지금 그 패키지에 격리 코드를 쓰면 충돌한다.
겹침 2, 곧 "clearAuthCode() 호출처가 0건이라 이전 사람의 마스터 키가 계속 남음 (fix(core): 계정 전환 시 이전 사용자의 수신자 목록이 그대로 노출 — 로그아웃에 receiverCache 정리 없음 #588)". clearAuthCode 는 origin/develop 전체 검색에서 선언 포함 0건이라 심볼 자체가 없다. 키가 남는 증상은 유효하지만 인용한 심볼과 메커니즘을 갈아야 한다. 실제 저장소는 ReceiverMasterKeyDataSource 이고, 지우는 지점은 3번의 SESSION 정리뿐이다.
본문이 전제하는 그림, 곧 서버가 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 가 같은 필드를 고정한다. 서버가 그 필드를 끊는 순간 목록 파싱이 예외로 떨어진다.
Activity
[-]fix(afternote): 한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨[/-][+]한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨[/+]OnlyReadOneBook commentedon Sep 13, 2026
실측 기록 (2026-09-13): 본문 전제가 서버에서 바뀌었다
코드로 뗄 독립 범위가 없다고 판정해 구현은 내지 않는다. 대신 다음 착수자가 낡은 전제로 시작하거나 곧 폐기될 코드를 쓰는 일을 막을 만큼만 남긴다.
판정 경로는 Afternote-FE
origin/develop7d87aa3 와 Afternote-BEorigin/main3593aa3 실코드다. BE 축의 상세 근거는 #1949 의 완료 조건 대조 코멘트에 있어 여기서 되풀이하지 않는다.1. 수신자 식별이 마스터 키에서 회원 계정으로 바뀌었다
본문 첫 단락의 전제, 곧 "수신자는 회원 계정이 아니라 마스터 키(UUID)로만 식별되는 구조라 앱에 이 기기의 수신자가 누구인가 라는 개념이 없다" 는 Afternote/Afternote-BE#289(2026-09-08 머지) 이후 성립하지 않는다.
ReceivedRecordService.java:42의findAllByAcceptedUserIdOrderByIdDesc(userId)다.:133-135의findByIdAndAcceptedUserId를 먼저 타고, 비면RECEIVER_NOT_FOUND로 끊는다.ReceivedRecordController.java의 목록 핸들러 인자가@UserId Long userId하나이고 헤더 파라미터가 없다.accessCode와ReceiverAuthController는 BE 소스에서 사라졌다.ReceivedRecordBoxResponse.java의 필드에도accessCode가 없다.격리를 서버가 계정 단위로 보장한다. 다만 이건 코드 경로를 읽은 결과이지 실서버 HTTP 응답을 받아 본 결과가 아니고, 운영 DB 실측도 아니다. Afternote/Afternote-BE#93 은 아직 OPEN 이다.
2. develop 에 남는 결함은 FE 가 구 계약을 붙들고 있는 것 하나로 수렴한다
SenderRegistry.kt:23-28이@Singleton+MutableStateFlow다.ReceiverAuthInterceptor.kt:25,32가/receiver-auth/로 나가는 요청에 저장된 코드를X-Auth-Code로 붙인다.둘 다 #607 의 record-boxes 전환이 통째로 걷어내는 대상이다. 전환 전에 카드에 소유자를 붙이거나, 컨텍스트 전환 UI 를 세우거나,
SenderRegistry를 영속화하면 전환 때 다시 뜯는다. 본문 Note 가 이미 셋 중 어느 것도 구현 계획으로 확정하지 않는다고 적었는데, 그 결론이 이제 서버 계약으로도 뒷받침된다.3. 전환 뒤 이 이슈에 남을 일
새 계약에는 기기에 공유될 마스터 키 자체가 없고, "이 기기의 수신자" 는 로그인 계정이 된다. 계정 전환 시 격리 수단은 develop 에 이미 있다.
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. 착수 지형
Closes를 직접 검색한 결과 한 기기를 여러 수신자가 쓸 때 받은 기록함이 격리되지 않음 — 카드·마스터 키·본인 확인이 공유됨 #598·받은 기록함이 서버 목록 API 를 쓰지 않고 로컬 stub 으로 동작 — record-boxes 미연동 #607·security(receiver): 기록함 접근 자격·마스터키 비노출 서버 계약을 확정하고 검증한다 #1949·security(receiver): 다중 수신자 컨텍스트·키 보관 정책을 서버 기록함 기준으로 분리 확정한다 #1950·받은 기록함 발신자 카드가 앱 재시작 시 소실 — SenderRegistry 가 메모리 전용 #599·refactor(receiver): 수신자 인증 검증·세션 활성화 정책을 UseCase로 분리 #1690 을 무는 PR 은 없다(라벨이 아니라 본문 기준).develop,refactor/1803-receiver-mvi)가SenderDetailViewModel·ReceivedRecordsViewModel·SenderRegistrationViewModel·MasterKeyViewModel·DeliveryVerificationFlowViewModel을 전부 MVI 로 재작성 중이다. 지금 그 패키지에 격리 코드를 쓰면 충돌한다.POST /api/v1/receiver-invitations/{token}/accept)이 FE 에 없다.receiver-invitations도invitation도 origin/develop 검색 0건이고, 그 구현은 받은 기록함이 서버 목록 API 를 쓰지 않고 로컬 stub 으로 동작 — record-boxes 미연동 #607 몫이다.본문이 develop 실태와 어긋나는 항목
본문은 고치지 않고 정정만 여기 남긴다.
SenderEntry.authCode필드명. origin/develop 의 필드는masterKey다(SenderEntry.kt:24). 소유자 구분 필드가 없다는 지적 자체는SenderEntry.kt:21-28기준 그대로 유효하다.IdentityVerificationRepositoryImpl.kt:42-54가 senderId 단위Set으로 격리하고,DeliveryVerificationFlowViewModel.kt:45,55가 자기 senderId 의 인증만 읽는다. 전역 boolean 도 디스크 잔존값 조회도 없다.clearAuthCode()호출처가 0건이라 이전 사람의 마스터 키가 계속 남음 (fix(core): 계정 전환 시 이전 사용자의 수신자 목록이 그대로 노출 — 로그아웃에 receiverCache 정리 없음 #588)".clearAuthCode는 origin/develop 전체 검색에서 선언 포함 0건이라 심볼 자체가 없다. 키가 남는 증상은 유효하지만 인용한 심볼과 메커니즘을 갈아야 한다. 실제 저장소는ReceiverMasterKeyDataSource이고, 지우는 지점은 3번의 SESSION 정리뿐이다.X-Auth-Code를 이메일로 확장해 같은 이메일의 기록함을 내려준다는 서술(ReceiverAuthApiService.kt:61-64KDoc). 새 계약에는 receiver-auth 매핑도accessCode도 없다. FE 는 아직ReceiverAuthApiService.kt:65의receiver-auth/record-boxes와ReceiverAuthDto.kt:132의 non-nullaccessCode를 붙들고 있고, 회귀 가드ReceivedRecordBoxContractTest가 같은 필드를 고정한다. 서버가 그 필드를 끊는 순간 목록 파싱이 예외로 떨어진다.OnlyReadOneBook commentedon Sep 19, 2026
2026-09-19 정리로 닫는다. 이 이슈의 완료 조건은 PM의 결정에 달려 있어 결정 전에는 FE 가 무엇을 만들지 쓸 수 없다. 그런 이슈는 FE 저장소에 열어 두지 않고 결정이 나면 그때 새 이슈로 다시 연다.
기다리는 결정: 다중 수신자 격리 방향(#1950).
PM 질문은 창구가 정해지는 대로 보낸다(일혁이 목록 보관).