Skip to content

서버 실패가 무음·빈 정상 화면으로 수렴 — 목록·상세·에디터 복구 수단 부재 #705

Closed

Description

@1hyok

📜 Overview (Required)

애프터노트 발신 흐름의 서버 실패·무응답이 화면별로 무음 실패, 복구 불가, 빈 정상 상태로 수렴한다.

1. 목록 새로고침 실패가 무음이다

  • 기존 목록이 있는 상태에서 Paging refresh가 실패하면 표시만 사라지고 기존 목록만 남는다.
  • AfternoteHomeScreen은 itemCount > 0이면 LoadState.Error를 사용자에게 전달하지 않는다.
  • AfternotePagingSource의 실패가 ErrorReporter에도 기록되지 않는다.

2. 발신 상세 조회 실패에 재시도가 없다

  • DetailLoadErrorContent는 뒤로가기만 제공한다.
  • AfternoteDetailViewModel의 재조회 진입점과 화면 배선이 없다.

3. 수정 prefill 조회 실패가 빈 정상 에디터로 보인다

  • loadExistingAfternoteForEdit 실패 시 isPrefillLoading=false만 설정한다.
  • 오류 상태·재시도·저장 차단 없이 빈 작성 폼이 노출돼 기존 기록을 덮어쓸 수 있다.

4. 저장·곡 검색 진행 상태가 화면에 전달되지 않는다

  • ViewModel의 isSaving이 Screen에 전달되지 않아 요청 중에도 유휴 상태처럼 보인다.
  • AddSongViewModel.isLoading도 검색 화면에서 소비하지 않는다.

5. 추모 영상 썸네일 업로드 실패가 저장을 막지 않는다

2026-08-24 추가. #938 작업 중 발견.

  • uploadMemorialThumbnail 실패 시 thumbnailUploadFailed=true 만 세우고, 화면은 스낵바 1회 만 띄운다. 저장 버튼은 그대로 눌린다.
  • 저장되면 BE 가 MemorialVideo(videoUrl, null) 로 객체 통째 교체하므로 썸네일 없는 영상이 확정된다. 수신자 화면은 ReceiverMemorialVideoThumbnail 에서 AsyncImage 를 건너뛰고 회색 그라디언트 + 재생 아이콘만 그린다 — 깨지진 않지만 시안에 정의된 상태가 아니다(시안은 썸네일 위 추모 영상 / 2분 34초 오버레이가 전제 — 추억 노트 상세 시안 결손 — 하단 고지·영상 재생·오버레이·타이틀 개행 #463 3번 항목).
  • 복구 경로가 없다. 썸네일만 재시도하는 수단이 없고, withMemorialVideo 가 영상 교체 시 썸네일을 리셋하므로 영상을 처음부터 다시 골라야 썸네일이 생긴다.
  • 3번(prefill 실패)과 같은 계열이다 — 실패를 오류 상태로 유지하지 않고 저장으로 흘려보낸다. 애프터노트는 사후 전달이라 잘못 확정된 결과를 되돌릴 기회가 제한적이다.

처방 선택지 (착수 시 결정)

  • (a) 썸네일 재시도 액션 노출 — 영상 재선택 없이 추출·업로드만 다시. 영상 업로드 성공분을 버리지 않아 손실이 가장 적다
  • (b) 저장 시 확인 다이얼로그 — "썸네일 없이 저장하면 수신자에게 회색 카드로 보입니다"
  • (c) 저장 차단 — 3번과 같은 처방이지만, 영상 업로드까지 성공한 상태를 버리게 되므로 단독으로는 과하다

✅ 시안 확인 완료 (2026-08-24) — 시안에 정의된 상태는 두 가지뿐이다.

  1. 영상 있음 (2667:14467 · 2667:15440) — 썸네일이 채워지고 중앙 재생 버튼 + 좌하단 추모 영상 / 2분 34초 오버레이
  2. 영상 없음 (2672:16029) — 빈 카드에 + 아이콘만 (첨부 유도)

"영상은 있는데 썸네일만 없는" 제3의 상태는 시안에 없다. 현재 코드가 만들어 내는 화면(회색 그라디언트 + 재생 아이콘, 썸네일 없음)은 1번도 2번도 아닌 어중간한 상태 — 영상이 있다고 주장하면서 미리보기를 못 보여준다. 따라서 (a) 재시도로 그 상태 자체를 만들지 않는 쪽이 맞다. (b)는 시안에 없는 상태를 사용자 동의로 확정하게 만들어 차선이다.

필요한 동작

  1. 기존 목록과 함께 refresh 실패를 알리고 재시도를 제공한다.
  2. 발신 상세 Error 화면에 재조회 액션을 연결한다.
  3. prefill 실패를 빈 폼으로 전환하지 말고 오류·재시도 상태로 유지하며 저장을 막는다.
  4. 저장·곡 검색의 진행 상태를 화면에 전달한다.
  5. 목록 LoadState.Error를 기록하되 반복 retry의 중복 계측을 억제한다.
  6. 썸네일 업로드 실패를 스낵바 1회로 끝내지 말고, 재시도 수단을 주거나 저장 전에 확인을 받는다.

📌 Child Issue(Optional)

No response

📍 Note (Optional)

Activity

  1. self-assigned this
    on Aug 3, 2026
  2. added
    bugSomething isn't working
    on Aug 3, 2026
  3. added theissue type on Aug 3, 2026
  4. 1hyok commented on Aug 3, 2026

    @1hyok
    CollaboratorAuthor

    develop d313c06f에서 곡 검색 실패 경로도 실기기로 확인했습니다.

    • 정상 검색 결과가 표시된 상태에서 검색어를 Beatles로 바꾸고 응답을 보류했습니다.
    • 새 GET 요청이 진행되는 동안 입력값만 바뀌고, 직전 Imagine Dragons 검색 결과가 그대로 남았습니다. 진행 표시나 입력 잠금은 없었습니다.
    • 연결 실패가 확정된 뒤에는 직전 결과까지 조용히 사라져 빈 결과 화면이 됐고, 화면 안 재시도 수단은 없었습니다.
    곡 검색 요청 중 직전 결과가 남고 로딩 표시가 없는 화면
  5. 1hyok commented on Aug 8, 2026

    @1hyok
    CollaboratorAuthor

    구현 기준값 확보 — 2026-08-09 새벽 류하늬 님 회신 2건 + 시안 실측.

    1. 목록 로딩 실패 표시 방식 확정: "문구나오다가 오류 팝업 띄우면 될 거 같습니다" — 자리 문구 + 오류 팝업 병행(질의 스레드).
    2. 복구 안내 팝업 3종 신설 — 재입력 거절 / 삭제된 항목([목록으로 돌아가기] 버튼 — 상세 화면 복구 수단) / 로그인 만료([재로그인하기]). 문구 전문은 공통 오류 안내의 상황별 문구·액션·적용 범위를 PM에게 확인한다 #446 코멘트 참조.

    목록·상세·에디터 복구 UI를 위 기준으로 착수할 수 있습니다.

  6. 1hyok commented on Aug 24, 2026

    @1hyok
    CollaboratorAuthor

    부분 해소 관찰 (develop 4e0a648eb, 2026-08-25 04:21 KST)

    목록에 한해서는 실패 시 "애프터노트 목록을 불러오지 못했습니다." + "다시 시도" 가 정상 노출됩니다. 이슈 본문이 말하는 "빈 정상 화면으로 수렴" 은 목록에서는 재현되지 않았습니다.

    상세·에디터 경로는 dev 서버 폐쇄 시간대라 도달하지 못해 미검증입니다. 남은 범위가 상세·에디터뿐이라면 이슈 제목·본문을 그쪽으로 좁히는 게 현재 상태에 맞을 것 같습니다.

    참고로 같은 실패 조건에서 타임레터(#714)는 빈 목록으로 위장하고, 설정(#708)은 로그아웃 항목만 남으며, 마음의 기록(#716)은 예외 원문을 그대로 출력합니다. 애프터노트 목록이 네 기능 중 유일하게 제대로 처리되고 있습니다.

  7. 1hyok commented on Aug 25, 2026

    @1hyok
    CollaboratorAuthor

    1번 항목 — 앞선 «부분 해소» 관찰의 사각지대

    08-24 코멘트에서 "목록에 한해서는 실패 시 문구 + 다시 시도가 정상 노출된다" 고 적었는데, 그건 itemCount == 0 일 때뿐입니다. 이 이슈 본문이 지목한 조건(itemCount > 0)은 그대로 남아 있습니다.

    // AfternoteHomeScreen
    refreshState is LoadState.Error && items.itemCount == 0 -> ErrorListBody(...)
    items.itemCount > 0 || selectedCategory != null       -> InfiniteListBody(...)   // ← 실패해도 여기로

    목록을 유지하는 판단 자체는 맞습니다(전면 교체하면 보고 있던 것이 사라짐). 문제는 그 실패를 알리는 경로가 하나도 없다는 것입니다. PullToRefreshBox 인디케이터만 사라지고 옛 목록이 그대로라, 사용자에게는 «새로고침 성공» 과 구분되지 않습니다.

    수신자 목록은 사각지대가 더 넓습니다

    이 이슈는 발신 흐름 기준인데, 같은 화면을 재사용하는 수신자 목록(ReceiverAfternoteHomeEntry)은 조건이 하나 더 나쁩니다.

    작성자 수신자
    append 실패 안내 스낵바 있음 (AfternoteHomeEntry) 경로 자체가 무효 — 서버가 페이지네이션 미지원이라 단일 페이지(nextKey = null)로 append 가 발생하지 않음
    snackbarHostState Entry 가 만들어 넘김 안 넘김 — AfternoteHomeScreen 기본값이라 띄울 주체가 없음
    refresh 실패(목록 있음) 무음 무음

    즉 수신자 목록에서 실패는 refresh 하나뿐인데, 그 하나가 itemCount > 0 이면 아무 데도 안 나타납니다.

    참고 — #611 에서 다룬 범위

    #1171 은 itemCount == 0 갈래만 손댔습니다. 전달 조건 미충족(403/2009)과 연결 실패를 갈라 각각 다른 안내를 그리고, 재시도가 무의미한 쪽은 버튼을 주지 않습니다. itemCount > 0 은 손대지 않았고 이 이슈 범위로 남깁니다.

    남은 결정

    디자이너 확정값은 "자리 문구 + 오류 팝업 병행"(08-09 류하늬 님 회신)인데, 지금 붙은 건 자리 문구 쪽뿐입니다. itemCount > 0 실패를 팝업으로 알릴지 스낵바로 알릴지가 이 항목의 실제 결정 지점입니다 — 팝업이면 core:ui 의 NetworkErrorPopup 이 이미 있습니다.

  8. changed the title [-]fix(afternote): 서버 실패가 무음·빈 정상 화면으로 수렴 — 목록·상세·에디터 복구 수단 부재[/-] [+]서버 실패가 무음·빈 정상 화면으로 수렴 — 목록·상세·에디터 복구 수단 부재[/+] on Aug 28, 2026
  9. added
    area:afternote애프터노트 기능 영역
    and removed on Aug 28, 2026
  10. 1hyok commented on Aug 30, 2026

    @1hyok
    CollaboratorAuthor

    5번(추모 영상 썸네일 업로드 실패가 저장을 막지 않는다)을 착수 단위로 #1550 에 분리했습니다. 나머지 1~4번은 여기 남습니다 — 2026-08-30 실측으로 넷 다 아직 유효합니다(홈은 refreshState is LoadState.Error && items.itemCount == 0 이라 기존 목록이 있으면 여전히 무음, prefill 실패는 isPrefillLoading = false 만).

    분리하면서 본문 5번이 현행 코드와 어긋나는 부분 두 가지를 확인했습니다.

    1. thumbnailUploadFailed 플래그는 더 이상 없다

    #831(에디터 오류 상태 통합)로 AfternoteEditorError.Upload(Target.THUMBNAIL) 오류 이벤트에 흡수됐습니다. 화면 동작(액션 없는 스낵바 한 줄)은 그대로지만, 재시도를 붙일 자리가 플래그가 아니라 그 오류 채널이라 처방 설계가 달라집니다.

    2. 추출 실패 갈래가 빠져 있다

    본문 5번은 업로드 실패만 다루는데, 프레임 추출 실패(onMemorialThumbnailExtractionFailed → ErrorReporter/MEMORIAL_THUMBNAIL_EXTRACT)는 스낵바조차 없이 완전 무음이면서 결과는 같은 «영상은 있는데 썸네일만 없는» 제3의 상태입니다. 재시도 수단을 만들면 두 갈래를 같은 액션에 태워야 하는데, 추출 실패는 손에 바이트가 없어 재추출부터 돌아야 해 트리거가 다릅니다.

    덧붙여 서버 몫을 실측했습니다(Afternote-BE origin/main). AfternotePlaylist.MemorialVideo 는 videoUrl·thumbnailUrl 두 칸을 nullable 로 저장만 하고 영상에서 프레임을 뽑는 로직이 없습니다 — 즉 FE 가 못 채우면 영구 결손이고, 재편집으로도 못 고칩니다(prefill 된 원격 URL 은 isLocalContentUri() 검사에서 추출을 건너뜀). 5번의 "복구 경로가 없다" 는 판단은 그대로 유효합니다.

  11. 1hyok commented on Aug 30, 2026

    @1hyok
    CollaboratorAuthor

    3번(수정 prefill 실패가 빈 정상 에디터로 보인다)의 폭발 반경이 넓어졌다 — #1596 / PR #1600 이후 실측 기준으로 남깁니다.

    종전에는 prefill 실패 뒤 빈 폼에 제목만 채워 저장하면 수신자·남길 말씀이 덮였습니다(미디어는 요청 바디에서 키가 빠져 서버 값이 살아남았습니다). PR #1600 이 AfternotePlaylistRequestDto 의 미디어 기본값을 떼면서 빈 폼이 곧 "memorialPhotoUrl": null · "memorialVideo": null 로 나갑니다. BE 는 이제 그 명시적 null 을 삭제로 읽어 DB 참조를 끊고 S3 객체까지 회수합니다(Afternote-BE 72fee63).

    즉 영정사진과 장례식 영상이 실제로 지워집니다. 되돌릴 수 없는 축이 하나 늘었습니다.

    지금 저장을 막고 있는 것은 AfternoteEditorValidator 의 제목 필수 검사뿐이고, 그건 사용자가 제목을 한 글자 치면 통과합니다 — 우연한 가드라 근거로 삼을 수 없습니다.

    이 이슈의 처방 3번("prefill 실패를 빈 폼으로 전환하지 말고 오류·재시도 상태로 유지하며 저장을 막는다")이 그대로 답입니다. 우선순위 판단에 참고하시라고 덧붙입니다.

  12. added
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Aug 30, 2026
  13. 1hyok commented on Sep 2, 2026

    @1hyok
    CollaboratorAuthor

    구현은 PR #1608 에 들어갔습니다. 다만 목록이 남은 새로고침 실패의 표시 갈림(배너 · 전면 오류 · 미표시) 은 회귀 테스트로 잠그지 못했습니다.

    판정 함수를 화면 파일의 private 로 두어야 해서(#1678) 테스트가 직접 부를 수 없고, 화면을 그려서 확인하는 방식은 PR 안에서 두 번 시도해 두 번 다 CI 에서 무너졌습니다. 기전은 #1443 이 실측해 둔 Robolectric 실행 순번 문제와 같습니다.

    검증 보강은 #1790 으로 분리했습니다. 계측 테스트로 올리는 안과 본문 컴포저블을 갈라 결정적으로 그리는 안 두 가지를 담아 뒀습니다.

  14. removed
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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