Skip to content

fix(setting): 프로필 수정에서 갤러리 사진을 골라 저장한다 (#1438) - #2152

Merged
1hyok merged 30 commits into
developfrom
fix/1438-profile-photo-edit
Oct 6, 2026
Merged

1hyok merged 30 commits into
developfrom
fix/1438-profile-photo-edit

Conversation

@1hyok

@1hyok 1hyok commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

먼저 볼 파일: 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로 기존 사진을 유지합니다.
  • 업로드가 실패하면 PATCH를 보내지 않습니다. 선택은 남아 재시도할 수 있고, 저장 중 입력이나 성공 후 재제출은 MVI 선행의 잠금을 유지합니다.
  • 프로필 조회 전이나 저장 중 도착한 갤러리 결과는 rememberSaveable에 보관했다가 받을 수 있을 때 Intent로 넘깁니다. 저장 중 화면 종료는 업로드 취소를 전파하며 PATCH·실패 신호를 만들지 않습니다.

설정 프로필 수정 시안과 기존 온보딩 화면의 갤러리 경로를 기준으로 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을 바꾸지 않았습니다.

시안(4327:71106) 수정 전(develop) 수정 후
프로필 설정 시안 수정 전 프로필 설정 Robolectric 렌더 수정 후 프로필 설정 Robolectric 렌더

수정 전후는 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"
      }
    ]
  }
}

뉴스레터·마음의 기록·애프터노트 토글의 공통 저장 경로에서 실패를 recordFailure 로 남긴다.
속성은 stage=push_setting_update 와 push_setting 토글 이름뿐이다. 조회 실패는 Logcat 에 두고,
롤백·실패 안내·재시도 동작은 바꾸지 않는다.
배지를 core ProfileImagePicker 로 바꿔 갤러리(PickVisualMedia ImageOnly)를 연다. 고른 사진은
저장할 때 profiles 로 올리고 받은 fileKey 로 PATCH users/me 를 보낸다. 업로드가 실패하면 수정
요청을 보내지 않는다. 서버 profileImageUrl 을 아바타에 표시하고, 선택 취소는 기존 사진을 지우지
않는다. 조회 중이거나 저장 중에 돌아온 선택은 받을 수 있을 때까지 보관한다.
@1hyok

1hyok commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator Author

실서버 확인 (2026-09-23)

Android 16 에뮬레이터, dev 서버, QA 계정으로 이 PR 빌드를 확인했습니다.

4:3 큰 사진 저장 뒤 재진입 작은 사진 저장 뒤 앱 재시작 오프라인 저장 10MiB 초과 사진
큰 사진 재진입 작은 사진 재시작 오프라인 저장 10MiB 초과

이름과 연락처는 바뀌지 않았습니다. QA 계정의 프로필 사진은 테스트 이미지로 남아 있습니다. 서버의 updateProfile 이 빈 profileImageUrl 을 무시해 사진 없음 상태로 되돌릴 수 없습니다.

@github-actions github-actions Bot added the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Sep 27, 2026
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

형제 충돌이 해소됐다 — 지금은 이 PR 과 같은 파일을 서로 다르게 고친 열린 PR 이 없다 (#1750).
다시 생기면 이 코멘트를 현재 상태로 고쳐 쓴다.

@github-actions github-actions Bot added the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Sep 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

이 PR 은 refactor/963-current-push-save-reporting 와 충돌 상태이고, 그래서 CI 가 돌지 않는다.

pull_request 워크플로는 base 와 head 를 합친 가상 머지 커밋(refs/pull/<N>/merge)에서
실행되는데, 충돌이면 GitHub 이 그 ref 를 만들지 못해 워크플로가 트리거조차 되지 않는다.
체크가 실패하는 것이 아니라 아예 생성되지 않으므로, 새 커밋을 올려도 no checks reported
인 채 최신 커밋이 한 번도 검증되지 않은 상태로 남는다 (#1028).

refactor/963-current-push-save-reporting 를 병합해 충돌을 풀면 검증이 다시 돈다. 해소되면 이 라벨은 자동으로 떨어진다.

@1hyok
1hyok removed this pull request from stack #2125 September 27, 2026 04:44
@1hyok
1hyok added this pull request to stack #2181 September 27, 2026 04:44
@github-actions github-actions Bot removed the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Sep 27, 2026
@github-actions github-actions Bot removed the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Sep 27, 2026
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 결정 대기다.
@1hyok
1hyok changed the base branch from refactor/963-current-push-save-reporting to refactor/1502-setting-event-stream-mvi October 1, 2026 11:22
@1hyok
1hyok added this pull request to stack #2228 October 1, 2026 11:22
@github-actions github-actions Bot removed the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Oct 1, 2026

@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.

사진 선택의 SelectPhoto Intent·PhotoSelected reducer 전환과 업로드 실패 시 선택 보존, 저장 중 선택 무시를 확인했습니다. 최신 head 기준으로 다시 승인합니다.

@github-actions github-actions Bot added the conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 label Oct 4, 2026
@github-actions github-actions Bot added the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Oct 5, 2026
@github-actions github-actions Bot removed the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Oct 5, 2026
Base automatically changed from refactor/1502-setting-event-stream-mvi to develop October 5, 2026 12:15
@github-actions github-actions Bot added the sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 label Oct 5, 2026
…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
@github-actions github-actions Bot removed conflict base 와 충돌 — CI 가 트리거되지 않는다. base 를 병합하면 자동으로 떨어진다 sibling-conflict 열린 형제 PR 과 같은 파일에서 충돌 — 나중에 머지되는 쪽이 base 를 병합해야 한다. 해소되면 자동으로 떨어진다 labels Oct 5, 2026
@1hyok
1hyok added this pull request to the merge queue Oct 6, 2026
Merged via the queue into develop with commit 137089c Oct 6, 2026
21 checks passed
@1hyok
1hyok deleted the fix/1438-profile-photo-edit branch October 6, 2026 04:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

android-test PR head에서 Android Managed Device 계측 테스트 실행

Projects

None yet

2 participants