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(afternote): 수정 모드 서버 영상→로컬 교체 후 삭제는 여전히 거짓 삭제 — 폼은 비지만 저장 후 서버 영상 잔존 #1406
#1375 는 추억 노트 미디어 시트의 "삭제" 를 이 폼에서 새로 붙인 로컬 첨부(content://)에만 노출해(removableMemorialMediaTargets) 서버 미디어의 거짓 삭제를 막았다. 그러나 다음 교차 경로는 그 가드를 지나쳐 여전히 거짓 삭제다.
재현 경로 (장례식에 남길 영상):
서버에 영상이 저장된 애프터노트를 수정 모드로 연다 — memorialVideoUrl 에 원격 URL prefill.
슬롯을 탭해 로컬 영상으로 교체한다 — memorialVideoUrl 이 content:// 로 바뀌므로 이제 삭제 가능 슬롯으로 판정된다.
시트의 "영상 삭제" 로 그 로컬 영상을 지운다 — memorialVideoUrl = null, 폼은 빈 슬롯.
저장한다.
서버 영상이 그대로 남는다. 재진입하면 지웠던 슬롯에 서버 영상이 되살아난다.
원인: 3 이후 저장 시 AfternoteEditorViewModel.videoMediaInput(null) 은 MediaInput.None 이 되고, 수정(PATCH) 계약은 삭제를 표현할 수 없다 — BE AfternotePlaylist.update 가 null 필드를 "기존 값 유지" 로 해석한다. 단계 2 에서 원격 URL 이 로컬 URI 로 덮여 "서버 영상이 아직 존재한다" 는 사실이 폼에서 사라지므로, 로컬 전용 삭제 가드가 이 경로를 가르지 못한다.
완료 조건:
위 경로에서 저장하면 서버 영상이 실제로 제거되거나, 제거가 불가능한 동안에는 폼이 거짓 빈 슬롯을 보여주지 않는다(예: 삭제 시 서버 영상 표시로 복귀).
근본 해소가 선행이라 적힌 BE 계약 확장을 이슈로 세워 blocked_by 로 걸었다 — Afternote-BE#259. 본문 참고에만 있던 "BE 계약 확장이 선행"을 관계로 바꿔 둔 것이고, BE 현 origin/main(aba24df) 재확인 결과 AfternotePlaylist.update()·PlaylistRelationStrategy.update() 어디에도 삭제 의사가 지나갈 통로가 없다는 게 확정이다.
그래서 단계 2(로컬 교체)에서 잃는 것은 원격 videoUrl 하나가 아니라 서버 영상 URL + 서버 썸네일 URL 쌍이다. 완료 조건의 "삭제 시 서버 영상 표시로 복귀"를 만족하려면 둘 다 되돌려야 하고, 썸네일만 빠지면 복귀한 슬롯이 썸네일 없는 카드로 보인다.
2. 지금 코드에 복구원이 없다 — 상태를 새로 들여야 한다
서버 원본이 남아 있을 만한 유일한 후보가 prefill 인데, AfternoteEditorViewModel.onPrefillConsumed() 가 반영 직후 pendingPrefill = null 로 버린다. 즉 단계 2 이후 서버 원본은 FE 어디에도 남지 않으므로, 어떤 처방이든 상태를 새로 추가하는 것이 전제다. "기존 값에서 되살리는" 싼 우회는 없다.
3. 처방 크기 — 두 판이 있고, 완료 조건은 싼 판으로 닫힌다
싼 판(권장):AfternoteTypeForm.Memorial 에 서버 원본 쌍(예: serverVideoUrl·serverThumbnailUrl)을 두고, withPrefillApplied 에서 채운 뒤 삭제 경로에서 videoUrl·thumbnailUrl 로 되돌린다. 표시·저장 경로는 그대로 두므로 AfternoteTypeForm.kt + EditorFormMutations.kt + feat(afternote): 추억 노트 영정사진·영상 소스 시트에 삭제 항목 추가 (#1114) #1375 의 삭제 조건(removableMemorialMediaTargets) 정도로 닫힌다.
비싼 판:videoUrl 을 사진처럼 pickedVideoUri/서버 URL 로 쪼개고 소비처마다 폴백을 심는 방식. feature/afternote/presentation main 13 파일 + 테스트 7 파일이 걸린다. 완료 조건을 넘어서는 범위다.
4. 사진과 갈린 건 계약 탓이 아니라 최초 설계 선택이다
사진은 pickedPhotoUri(로컬)/photoUrl(서버) 두 필드라 이 결함이 없고, 영상은 한 필드다. 이 비대칭은 최초 구현 커밋 860dba8c5(2026-03-29)부터 있었고 이후 리팩터링들은 이름·위치만 옮겼다. 계약이 강제한 구조가 아니므로 FE 재량으로 바꿀 수 있다.
5. 다만 최종 모델을 지금 확정하지는 말 것
BE#259 가 삭제 계약을 열면 폼이 표현해야 할 것은 유지·교체·삭제 3상태가 된다. 지금 사진의 nullable 2개 구조를 그대로 미러링해 두면 그때 다시 손대야 할 수 있다. 이 이슈에서는 완료 조건대로 "거짓 빈 슬롯을 보여주지 않는다"까지만 닫고, 삭제 자체의 모델은 BE 결정 뒤로 미루는 편이 안전하다.
PR #1527 로 완료 조건의 후자를 처리했습니다 — 「제거가 불가능한 동안에는 폼이 거짓 빈 슬롯을 보여주지 않는다」.
서버 영상을 로컬로 교체한 뒤 지우면 null 이 아니라 서버 원본 표시로 되돌아갑니다. 되돌아간 뒤에는 #1375 의 규칙이 다시 적용돼 삭제 항목도 스스로 감춰집니다. 프로세스 재생성 스냅샷에도 원본을 실어, 복원 뒤 삭제가 다시 거짓이 되지 않게 했습니다.
원인은 영상 칸 하나가 로컬 픽과 원격 prefill 을 겸하고 있던 것이었습니다 — 영정 사진은 pickedPhotoUri/photoUrl 이 갈려 있어 같은 문제가 없었고, 영상만 대칭이 깨져 있었습니다.
전자(서버 영상의 실제 제거)는 이 PR 범위 밖이고, FE 가 표현할 수단이 없습니다. BE AfternotePlaylist.update 가 생략·null 을 모두 「기존 값 유지」로 흡수하는 한 어떤 페이로드로도 삭제를 말할 수 없습니다 — 블로커인 #259 의 계약 확장이 선행돼야 합니다.
그래서 이 이슈는 #259 해소 뒤 다시 열어 볼 갈래가 남습니다: 계약이 삭제를 표현하게 되면 「삭제 = 교체 취소」인 현재 동작을 실제 삭제로 승격할지 판단해야 합니다. 그때까지는 지울 수 없는 것을 지운 척하지 않는 쪽을 택했습니다 — 애프터노트는 사후 전달이라, 사용자가 지웠다고 믿은 채 장례식 영상이 남는 편이 훨씬 나쁩니다.
Activity
refactor(afternote): content:// 판정을 헬퍼 하나로 모으고 거짓 삭제 교차 경로를 #1406 으로 …
OnlyReadOneBook commentedon Aug 28, 2026
근본 해소가 선행이라 적힌 BE 계약 확장을 이슈로 세워 blocked_by 로 걸었다 — Afternote-BE#259. 본문 참고에만 있던 "BE 계약 확장이 선행"을 관계로 바꿔 둔 것이고, BE 현
origin/main(aba24df) 재확인 결과AfternotePlaylist.update()·PlaylistRelationStrategy.update()어디에도 삭제 의사가 지나갈 통로가 없다는 게 확정이다.착수할 사람이 본문만 보면 놓칠 재료 몇 가지를 실측해 남긴다.
1. 썸네일이 같은 축에 걸린다 — 복원은 쌍으로 해야 한다
EditorFormMutations.kt:37이 영상 갱신 시 썸네일을 함께 비운다.그래서 단계 2(로컬 교체)에서 잃는 것은 원격
videoUrl하나가 아니라 서버 영상 URL + 서버 썸네일 URL 쌍이다. 완료 조건의 "삭제 시 서버 영상 표시로 복귀"를 만족하려면 둘 다 되돌려야 하고, 썸네일만 빠지면 복귀한 슬롯이 썸네일 없는 카드로 보인다.2. 지금 코드에 복구원이 없다 — 상태를 새로 들여야 한다
서버 원본이 남아 있을 만한 유일한 후보가 prefill 인데,
AfternoteEditorViewModel.onPrefillConsumed()가 반영 직후pendingPrefill = null로 버린다. 즉 단계 2 이후 서버 원본은 FE 어디에도 남지 않으므로, 어떤 처방이든 상태를 새로 추가하는 것이 전제다. "기존 값에서 되살리는" 싼 우회는 없다.3. 처방 크기 — 두 판이 있고, 완료 조건은 싼 판으로 닫힌다
AfternoteTypeForm.Memorial에 서버 원본 쌍(예:serverVideoUrl·serverThumbnailUrl)을 두고,withPrefillApplied에서 채운 뒤 삭제 경로에서videoUrl·thumbnailUrl로 되돌린다. 표시·저장 경로는 그대로 두므로AfternoteTypeForm.kt+EditorFormMutations.kt+ feat(afternote): 추억 노트 영정사진·영상 소스 시트에 삭제 항목 추가 (#1114) #1375 의 삭제 조건(removableMemorialMediaTargets) 정도로 닫힌다.videoUrl을 사진처럼pickedVideoUri/서버 URL 로 쪼개고 소비처마다 폴백을 심는 방식.feature/afternote/presentationmain 13 파일 + 테스트 7 파일이 걸린다. 완료 조건을 넘어서는 범위다.4. 사진과 갈린 건 계약 탓이 아니라 최초 설계 선택이다
사진은
pickedPhotoUri(로컬)/photoUrl(서버) 두 필드라 이 결함이 없고, 영상은 한 필드다. 이 비대칭은 최초 구현 커밋860dba8c5(2026-03-29)부터 있었고 이후 리팩터링들은 이름·위치만 옮겼다. 계약이 강제한 구조가 아니므로 FE 재량으로 바꿀 수 있다.5. 다만 최종 모델을 지금 확정하지는 말 것
BE#259 가 삭제 계약을 열면 폼이 표현해야 할 것은 유지·교체·삭제 3상태가 된다. 지금 사진의 nullable 2개 구조를 그대로 미러링해 두면 그때 다시 손대야 할 수 있다. 이 이슈에서는 완료 조건대로 "거짓 빈 슬롯을 보여주지 않는다"까지만 닫고, 삭제 자체의 모델은 BE 결정 뒤로 미루는 편이 안전하다.
OnlyReadOneBook commentedon Aug 29, 2026
PR #1527 로 완료 조건의 후자를 처리했습니다 — 「제거가 불가능한 동안에는 폼이 거짓 빈 슬롯을 보여주지 않는다」.
서버 영상을 로컬로 교체한 뒤 지우면
null이 아니라 서버 원본 표시로 되돌아갑니다. 되돌아간 뒤에는 #1375 의 규칙이 다시 적용돼 삭제 항목도 스스로 감춰집니다. 프로세스 재생성 스냅샷에도 원본을 실어, 복원 뒤 삭제가 다시 거짓이 되지 않게 했습니다.원인은 영상 칸 하나가 로컬 픽과 원격 prefill 을 겸하고 있던 것이었습니다 — 영정 사진은
pickedPhotoUri/photoUrl이 갈려 있어 같은 문제가 없었고, 영상만 대칭이 깨져 있었습니다.전자(서버 영상의 실제 제거)는 이 PR 범위 밖이고, FE 가 표현할 수단이 없습니다. BE
AfternotePlaylist.update가 생략·null 을 모두 「기존 값 유지」로 흡수하는 한 어떤 페이로드로도 삭제를 말할 수 없습니다 — 블로커인 #259 의 계약 확장이 선행돼야 합니다.그래서 이 이슈는 #259 해소 뒤 다시 열어 볼 갈래가 남습니다: 계약이 삭제를 표현하게 되면 「삭제 = 교체 취소」인 현재 동작을 실제 삭제로 승격할지 판단해야 합니다. 그때까지는 지울 수 없는 것을 지운 척하지 않는 쪽을 택했습니다 — 애프터노트는 사후 전달이라, 사용자가 지웠다고 믿은 채 장례식 영상이 남는 편이 훨씬 나쁩니다.
test(afternote): 지문에서 서버 원본을 빼는 규칙을 카나리로 고정한다 (#1406)
feat(afternote): 미디어 삭제는 슬롯을 비운다 — 교체분과 서버 원본을 함께 걷고 서버 삭제는 저장이 한다 (#…