Repository navigation
fix(setting): 프로필 수정에서 갤러리 사진을 골라 저장한다 (#1438) - #2152
Conversation
뉴스레터·마음의 기록·애프터노트 토글의 공통 저장 경로에서 실패를 recordFailure 로 남긴다. 속성은 stage=push_setting_update 와 push_setting 토글 이름뿐이다. 조회 실패는 Logcat 에 두고, 롤백·실패 안내·재시도 동작은 바꾸지 않는다.
배지를 core ProfileImagePicker 로 바꿔 갤러리(PickVisualMedia ImageOnly)를 연다. 고른 사진은 저장할 때 profiles 로 올리고 받은 fileKey 로 PATCH users/me 를 보낸다. 업로드가 실패하면 수정 요청을 보내지 않는다. 서버 profileImageUrl 을 아바타에 표시하고, 선택 취소는 기존 사진을 지우지 않는다. 조회 중이거나 저장 중에 돌아온 선택은 받을 수 있을 때까지 보관한다.
실서버 확인 (2026-09-23)Android 16 에뮬레이터, dev 서버, QA 계정으로 이 PR 빌드를 확인했습니다.
이름과 연락처는 바뀌지 않았습니다. QA 계정의 프로필 사진은 테스트 이미지로 남아 있습니다. 서버의 |
|
형제 충돌이 해소됐다 — 지금은 이 PR 과 같은 파일을 서로 다르게 고친 열린 PR 이 없다 (#1750). |
…ent-setting-cancellation
|
이 PR 은
|
…3-current-push-save-reporting
…ent-setting-cancellation
…3-current-push-save-reporting
…-profile-photo-edit
ConnectedAccountsViewModel 과 PushNotificationViewModel 을 MviViewModel 로 전환한다. 화면 입력은 onIntent 하나로 모으고, 비동기 결과는 dispatch 로 내며 전이는 reduce 에서만 한다. - 연결 계정: Channel 을 걷고 연결 요청을 UiState.pendingLinkProvider 신호로 둔다. 소비는 ConsumeLinkRequest(provider) 로 받아, 늦게 도착한 소비가 그사이 올라온 다른 제공자 요청을 지우지 않는다. 카카오·구글 인증과 실패 스낵바는 화면 코루틴에서 잠금으로 하나씩 처리해 예전 이벤트 수집의 순서를 지킨다. - 알림 설정: MutableSharedFlow 를 걷고 마케팅 동의 저장 실패를 isMarketingConsentSaveFailed 신호로 둔다. 화면이 STARTED 인 동안만 신호를 올리도록 MarketingFeedbackStarted·Stopped Intent 를 두어, 화면이 없는 동안의 실패를 재진입에 재생하지 않는 #558 계약을 보존한다. 값 롤백과 ErrorReporter 진단 단계(sms·email·push_consent_update)는 그대로다. - 푸시 토글 재시도 대상(failedUpdate)을 VM 필드에서 UiState 로 옮겨 실패 안내와 같은 전이에서 채우고 비운다. 저장 중 중복 제출 가드와 성공·실패·재시도·취소 동작은 그대로다. - UiState 가 내부 타입을 품게 되어 PushNotificationUiState 와 그 상태를 받는 두 컴포넌트를 모듈 안으로 좁힌다. 알림 설정 화면은 Screen/Content 2단으로 나눈다. - konsist 규칙 D: 아직 전환하지 않은 feature presentation ViewModel 도 이벤트용 Channel·MutableSharedFlow 를 새로 선언하지 못하게 한다. 기존 선언은 LEGACY_EVENT_STREAMS 기준선(ReceiverRegisterViewModel._events)으로 인정한다. 두 VM 은 PENDING_MVI_MIGRATION 에서 뺀다. ReceiverRegisterViewModel 은 교체 대상(receiver-register-post)이라 #944 전환이 가져간다. 폼 값과 이메일 등록 버튼이 PM 결정 대기다.
Sadturtleman
left a comment
There was a problem hiding this comment.
사진 선택의 SelectPhoto Intent·PhotoSelected reducer 전환과 업로드 실패 시 선택 보존, 저장 중 선택 무시를 확인했습니다. 최신 head 기준으로 다시 승인합니다.
…hoto-edit # Conflicts: # feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/ConnectedAccountsViewModel.kt # feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/notification/PushNotificationViewModel.kt # feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/profile/ProfileEditUiState.kt # feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/profile/ProfileEditViewModel.kt # feature/setting/presentation/src/test/kotlin/com/afternote/feature/setting/presentation/SettingCoroutineCancellationTest.kt




먼저 볼 파일:
ProfileEditContract.kt,ProfileEditViewModel.kt,ProfileEditScreen.kt,ProfileEditPhotoPickerTest.kt.Closes #1438
Refs #2003
프로필 사진 배지를 갤러리 피커와 연결하고 조회·선택·업로드를 구현했습니다. 실제 base는 #2147의
refactor/963-current-push-save-reporting이며 #2180에서 도입한 MVI·UiState 신호 계약을 그대로 확장합니다. 사진 선택은 SelectPhoto Intent와 PhotoSelected reducer event로 처리합니다. 화면과 테스트에 옛 updateProfile/selectProfileImage 직접 호출이나 Channel 소비를 남기지 않습니다.profiles디렉터리로 업로드한 뒤fileKey를 PATCH 사진 필드로 보냅니다. BE UserService의 키 승격 계약을 소비합니다. 선택이 없으면 null로 기존 사진을 유지합니다.설정 프로필 수정 시안과 기존 온보딩 화면의 갤러리 경로를 기준으로 ImageOnly를 사용합니다. 이 화면의 카메라 제공 근거는 확인되지 않았으므로 촬영 경로를 추가하지 않습니다. core ProfileImagePicker 배지의 시안 대응은 #2003의 확인을 소비합니다.
검증: Nav3·MVI 선행을 통합한 설정 단위 테스트 전체, Konsist 54개, 설정 ktlint와 앱 main/androidTest 컴파일을 통과했습니다. 사진 업로드 순서·키·실패 보존·저장 중 선택 무시 6개, 피커 선택·취소·조회 전 및 저장 중 결과 보관 4개, 업로드 중 ViewModelStore 종료 회귀가 포함됩니다. 선행의 프로필 신호 테스트도 PhotoUploadRepository의 strict fixture를 주입해 그대로 통과했습니다.
추가 회귀 검증: 실제 SettingNavHost에서 저장 중 system back으로 요청이 취소되고, 홈 복귀 후 새 화면에서 서버 프로필을 다시 읽는 계측 테스트를 API 35에서 통과했습니다. 늦은 완료가 새 화면을 닫지 않는 것도 확인합니다. Activity·ActivityResultRegistry를 Parcel로 저장하고 새 Activity·ViewModel로 두 번 복원하는 Robolectric 테스트도 통과했습니다. 실제 OS 프로세스 종료, 기기 피커 UI와 URI 권한 지속, 서버가 이미 받은 PATCH의 취소는 이 테스트로 검증하지 않습니다. 두 새 테스트의 ktlint, 앱 빌드와 계측 테스트 APK 빌드도 통과했습니다.
기존 PR에서 검증한 표시와 선택 동작을 보존합니다. 실서버 업로드·재진입 QA는 아직 하지 않았습니다. 로컬 screenshot 12개 차이는 종전 base에서도 동일하게 발생했으며 이번 구조 적응에서는 UI 배치·golden을 바꾸지 않았습니다.
수정 전후는 Robolectric 렌더이고, 이름·연락처·이메일은 테스트 값이다.
선행 순서: #2123 → #2173 → #2180 → #2124 → #2128 → #2147 → 이 PR. 설정 첫 화면의 작은 아바타 표시와 #2143의 공통 이미지 비율은 이 PR의 완료 범위 밖입니다.
🧪 CI Test Plan
{ "androidTest": { "mode": "selected", "reason": "프로필 저장 중 system back과 재진입의 실제 Nav3 entry·ViewModel 수명을 계측 테스트로 확인합니다. 피커 결과의 Activity/Registry 저장 복원은 Robolectric 단위 테스트로 검증합니다.", "tests": [ { "path": "app/src/androidTest/java/com/afternote/afternote_fe/ProfileSaveBackAndroidTest.kt", "selector": "com.afternote.afternote_fe.ProfileSaveBackAndroidTest#systemBackDuringPendingSaveCancelsRequestAndReentryLoadsServerProfile", "device": "api34" } ] } }