Skip to content

fix(timeletter): 작성 중 뒤로가기 시 임시저장 여부를 묻는다 (#576) - #2317

Open
koongmai wants to merge 2 commits into
developfrom
fix/576
Open

koongmai wants to merge 2 commits into
developfrom
fix/576

Conversation

@koongmai

Copy link
Copy Markdown
Collaborator

📌𝘐𝘴𝘴𝘶𝘦𝘴

📎𝘞𝘰𝘳𝘬 𝘋𝘦𝘴𝘤𝘳𝘪𝘱𝘵𝘪𝘰𝘯

이슈 요구사항 대응

#576 에서 요구한 것 이 PR
기대 동작 (팀 QA 문서 › 타임레터 › 임시저장, 2차 QA 25번): 작성 중 뒤로가기 → 임시저장 팝업, 「예」면 임시저장 탭에 데이터 보존 「작성 중인 내용을 임시저장할까요?」 팝업을 띄우고, 「예」는 하단 «임시저장» 버튼과 같은 경로(saveDraft → savedAsDraft → 임시저장 탭 갱신 후 이탈)로 저장한다
원인: feature/timeletter/presentation 에 BackHandler 0건 → 시스템 기본 pop 으로 즉시 이탈 작성 화면에 BackHandler 를 두고, 상단바 뒤로가기도 같은 onBackAttempt 로 묶는다
빈 폼에서 팝업이 뜨지 않게: 애프터노트 에디터(#503)의 상태 지문 방식 진입 시점 스냅샷 대비 변경이 있을 때만 가로챈다(writeContentSignature: 제목·블록·수신인·발송 날짜/시간). 변경이 없으면 기본 뒤로가기(predictive back)를 그대로 둔다. 수정 모드는 불러온 내용을 채운 뒤 스냅샷을 잡아 무변경 이탈에 경고하지 않는다
#638 과 해결 방향이 갈림 (임시저장 팝업 vs 나가기 시 폐기 확인) — 한쪽으로 합쳐야 함 둘을 순서대로 합쳤다: 임시저장을 먼저 묻고, 거절하면 「작성한 내용을 삭제할까요?」로 폐기를 한 번 더 확인한다. #638 은 이미 닫혀 있어 이 PR 이 대표 이슈 #576 하나로 이중 트래킹을 정리한다
임시저장 필수값 (#570 · BE#83) 현재 FE 는 임시저장에도 수신인을 요구한다(save() 공통 검증). 수신인 없이 「예」를 누르면 기존 «임시저장» 버튼과 똑같이 「수신자를 선택해주세요」 스낵바를 띄우고 작성 화면에 남는다 — 내용은 사라지지 않는다
#569 (수신인 선택 왕복 소실)과는 경로가 다름 범위 밖. 지문 스냅샷은 수신인 선택 왕복·프로세스 복원에도 유지되도록 rememberSaveable 로 둔다

이탈 흐름

뒤로가기 (진입 이후 변경이 있을 때만)
├ 새 편지 · 임시저장 편지
│  └ 「작성 중인 내용을 임시저장할까요?」 [아니요] [예]
│      ├ 예    → 임시저장 → 임시저장 탭으로 이탈 (수신인 누락·저장 실패면 오류 스낵바 + 잔류)
│      └ 아니요 → 「작성한 내용을 삭제할까요?」 [취소] [예]
│                  ├ 예   → 저장 없이 이탈
│                  └ 취소 → 작성 화면 잔류, 내용 유지
└ 예약 편지 수정
   └ 「수정한 내용을 취소할까요?」 [취소] [예]
       ├ 예   → 수정 내용 버리고 이탈
       └ 취소 → 잔류
  • 예약 편지 수정은 임시저장을 묻지 않는다. 같은 작성 화면이 수정에도 쓰이는데, 여기서 임시저장하면 save(status = DRAFT) 가 그 편지를 갱신해 예약이 DRAFT 로 풀린다. 이를 가르기 위해 TimeLetterWriteUiState.isEditingScheduledLetter 를 추가했다(불러온 편지가 SCHEDULED 일 때만 true). 임시저장 편지 수정은 DRAFT 를 유지하므로 새 편지와 같은 흐름을 탄다.
  • 두 팝업 모두 공용 Popup(PopupType.Variant2) 를 쓴다. 문구는 리소스 4개(timeletter_write_exit_*)로 두고, 이전 커밋의 「나가기/계속 작성」 문구는 걷었다.

변경 파일 (모두 feature/timeletter/presentation, core 변경 없음)

  • TimeLetterWriteScreen.kt — BackHandler, 변경 지문, ExitConfirmStep(SaveDraft / Discard / DiscardEdit) 팝업 단계
  • TimeLetterWriteUiState.kt · TimeLetterWriteViewModel.kt — isEditingScheduledLetter
  • strings.xml — 이탈 팝업 문구
  • 테스트: TimeLetterWriteScreenTest, TimeLetterWriteViewModelTest

검증 (로컬, develop 818f5f562 기준)

  • :feature:timeletter:presentation:testDebugUnitTest 통과
    • TimeLetterWriteScreenTest 8건 — 변경 없을 때 즉시 이탈 / 임시저장 「예」가 작성 내용으로 저장 요청하고 직접 이탈하지 않음 / 「아니요」→ 삭제 「예」면 저장 없이 이탈 / 삭제 「취소」면 잔류·내용 유지 / 예약 수정은 수정 취소만 묻고 임시저장 미노출 / 수정 모드 무변경 이탈 시 팝업 없음 등
    • TimeLetterWriteViewModelTest 9건 — SCHEDULED 만 isEditingScheduledLetter 로 판별
  • :feature:timeletter:presentation:ktlintCheck, :app:compileDebugKotlin 통과
  • 실기기·에뮬레이터 수동 확인은 하지 않았다.

🧪 CI Test Plan

{
  "androidTest": {
    "mode": "none",
    "reason": "변경이 feature/timeletter/presentation 의 작성 화면 Compose 상태·팝업과 UI 상태 필드에 한정되고 app 내비게이션·manifest·DI 경계를 바꾸지 않는다. 이탈 흐름은 Robolectric Compose 테스트(TimeLetterWriteScreenTest)로 시스템 뒤로가기(OnBackPressedDispatcher)와 상단바 뒤로가기 모두 검증한다.",
    "tests": []
  }
}

📷𝘚𝘤𝘳𝘦𝘦𝘯𝘴𝘩𝘰𝘵

없음 (공용 Popup 그대로 사용, 문구만 다름)

💬𝘛𝘰 𝘙𝘦𝘷𝘪𝘦𝘸𝘦𝘳𝘴

  • 공용 Popup 은 바깥 탭·팝업 위 뒤로가기를 「아니요」와 같은 onDismiss 로 보낸다(Popup.kt onDismissRequest). 그래서 첫 팝업 바깥을 탭하면 삭제 확인으로 넘어간다. 두 번째 확인이 남아 있어 내용이 바로 사라지지는 않으며, 이를 가르려면 core/ui 계약 변경이 필요해 이 PR 범위에서 제외했다.
  • 커밋 2개: 첫 커밋(변경 감지 + 단일 이탈 확인)을 둘째 커밋이 QA 문서 기대 동작(임시저장 우선)으로 확장한다.

🤖 Generated with Claude Code

koongmai and others added 2 commits October 26, 2026 11:28
작성 화면에 BackHandler 가 없어 뒤로가기가 확인 없이 화면을 닫고 작성 내용을 잃었다. 애프터노트
에디터(#503)와 같이 진입 시점 스냅샷 대비 변경이 있을 때만 시스템·상단바 뒤로가기를 가로채 이탈
확인 팝업을 띄운다. 변경이 없으면 기본 뒤로가기(predictive back)를 그대로 둔다. 수정 모드는 불러온
제목을 채운 뒤 스냅샷을 잡아 무변경 이탈을 변경으로 읽지 않는다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
팀 QA 문서의 기대 동작(작성 중 뒤로가기 → 임시저장 팝업, 「예」면 임시저장 탭에 보존)에 맞춰
이탈 확인을 단계로 나눈다.

- 새 편지·임시저장 편지: 「작성 중인 내용을 임시저장할까요?」 예 → 하단 임시저장과 같은 경로로
  저장(수신인 누락·실패면 오류 스낵바를 띄우고 머문다), 아니요 → 「작성한 내용을 삭제할까요?」
  예 → 저장 없이 나간다, 취소 → 작성 화면에 남는다.
- 예약 편지 수정: 임시저장을 권하면 예약이 DRAFT 로 풀리므로 「수정한 내용을 취소할까요?」만 묻는다.
  이를 위해 UI 상태에 isEditingScheduledLetter 를 둔다.

공용 Popup 은 바깥 탭·뒤로가기를 「아니요」와 같은 onDismiss 로 보내므로 첫 팝업 바깥 탭은 삭제
확인으로 넘어간다. 두 번째 확인이 남아 있어 내용이 바로 사라지지는 않는다.

Closes #576

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@koongmai koongmai added bug Something isn't working area:timeletter 타임레터 기능 영역 labels Oct 26, 2026
@github-actions github-actions Bot added the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

열린 PR 중 이 PR 과 같은 파일을 서로 다르게 고친 것이 있다. 어느 쪽이 먼저 머지되든 나중 쪽은 base 를 병합해야 통과한다 (#1750).

머지 자체는 GitHub 이 막는다 — 한쪽이 들어가면 다른 쪽은 CONFLICTING 이 되어 merge queue 에 못 들어간다.
이 라벨은 그 전에 순서를 정하라는 신호다. 먼저 갈 쪽을 정하고, 나중 쪽은 그 머지 뒤에 base 를 병합한다.
상대 PR 에도 같은 안내가 달린다.

이 코멘트는 리컨사일러가 매 실행 현재 상태로 고쳐 쓴다. 해소되면 sibling-conflict 라벨은 자동으로 떨어진다.

@Sadturtleman Sadturtleman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

최신 head 6bd1b91ad5916e5969e1122b2dd25daba9e7ea2a의 전체 변경 및 관련 작성 화면·저장/내비게이션·Popup 경로와 기존 리뷰를 확인했습니다. 변경 감지, 임시저장 성공 후 이탈/실패 시 잔류, 예약 편집의 폐기 확인 흐름에서 차단할 결함을 발견하지 못했습니다. 해당 SHA의 필수 CI 결과를 확인했습니다(Unit Test, Ktlint, Android Lint, Screenshot, Repository Quality, Kotlin CodeQL 등 성공). Actions CodeQL은 변경 범위상 skipped이며, managed-device 게이트는 성공했지만 androidTest mode=none으로 기기 테스트 자체는 실행되지 않았습니다. 별도 로컬 실행·실기기 수동 검증은 수행하지 않았습니다.

This branch has not been deployed

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

Labels

area:timeletter 타임레터 기능 영역 bug Something isn't working sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다

Projects

None yet

Development

Successfully merging this pull request may close these issues.

작성 중 뒤로가기 시 임시저장 팝업 부재 — 확인 없이 내용 소실

2 participants