Skip to content

[FEAT] 프로필 화면 구현 및 프로필 수정/출발지 설정/로그아웃 기능 제공 - #114

Merged
khyeji98 merged 10 commits into
developfrom
feature/#113
Jul 21, 2026
Merged

[FEAT] 프로필 화면 구현 및 프로필 수정/출발지 설정/로그아웃 기능 제공#114
khyeji98 merged 10 commits into
developfrom
feature/#113

Conversation

@khyeji98

Copy link
Copy Markdown
Collaborator

🧐 개요

마이페이지 성격의 프로필 화면을 신설하고, 프로필 수정 · 기본 출발지 설정 · 로그아웃을 한 화면에서 처리할 수 있도록 구현했습니다. 온보딩 전용으로 묶여 있던 프로필 입력 화면(닉네임/프로필 이미지)을 ProfileInputFeature 모듈로 분리해 온보딩과 프로필 수정 양쪽에서 재사용합니다.

Closes #113

✨ 작업 내용

도메인 · 데이터 계층 (Member)

  • MemberProfile Entity 및 FetchMemberProfileUseCase / UpdateMemberNicknameUseCase / UpdateMemberProfileImageUseCase / LogoutUseCase 프로토콜 정의
  • Member API Endpoint · DTO · Repository · UseCase 구현체 작성
  • MemberProfileClient / SessionClient 정의 후 MemberProfileFactory / SessionFactory로 조립, BangawoApp에서 주입

프로필 입력 모듈 분리

  • AuthFlowFeature 내부의 프로필 입력 화면을 ProfileInputFeature 공용 모듈로 분리 (닉네임 입력 · 프로필 이미지 피커)

프로필 화면

  • ProfileFeature / ProfileView 구현 — 프로필 조회, 로딩/에러 처리
  • 프로필 수정 진입 및 수정 결과 반영, 홈/루트 연동
  • 출발지 편집 시트를 store 비의존 뷰로 분리
  • 로그아웃 처리 및 세션 종료 후 루트 라우팅 전환

약관

  • 약관 뷰어를 PDF에서 URL 웹뷰(TermWebView)로 전환

테스트

  • ProfileFeature / ProfileInputFeature / RootFeature 로그아웃 플로우 단위 테스트 작성

✅ 체크리스트

  • MemberProfile Entity 및 Member 도메인 UseCase 프로토콜 정의
  • Member API Endpoint · DTO · Repository · UseCase 구현체 작성
  • MemberProfileClient / SessionClient 정의 및 Factory 조립 후 BangawoApp에서 주입
  • ProfileInputFeature 모듈 분리
  • ProfileFeature / ProfileView 구현
  • 프로필 수정 진입 및 수정 결과 화면 반영
  • 로그아웃 처리 및 세션 종료 후 루트 라우팅 전환
  • ProfileFeature / RootFeature 단위 테스트 작성

🙏 To Reviewer

변경 규모가 다소 큽니다. 도메인/데이터 계층 → ProfileInputFeature 모듈 분리 → ProfileFeature 화면 순으로 보시면 흐름을 따라가기 수월합니다.

기본 출발지 설정 시트의 목록 조회/기본값 변경 연동은 이번 PR 범위에서 화면 골격 위주로 반영했고, 약관 뷰어는 임시로 항목을 고정해 두었습니다.

khyeji98 added 8 commits July 16, 2026 16:51
MemberProfile 엔티티와 프로필 조회/닉네임·이미지 수정/로그아웃
UseCase, Repository·Session 인터페이스 프로토콜을 추가
MemberProfile DTO·Endpoint와 Repository·UseCase 구현체를 추가해
프로필 조회/수정 및 로그아웃 네트워크 흐름을 연결
MemberProfileClient·SessionClient를 정의하고 Factory 조립 후
BangawoApp prepareDependencies에서 live 주입
AuthFlow에 있던 프로필 입력 화면을 ProfileInputFeature 모듈로
분리하고, 온보딩·프로필 수정 양쪽에서 재사용하도록 수정 모드와
원격 이미지 표현을 지원
MyDeparturePlaceEditSheetContent를 store 의존을 제거한
DeparturePlaceEditSheetContent로 바꿔 재사용 가능하게 수정
ProfileFeature/ProfileView로 프로필 조회·수정·출발지 설정·
로그아웃을 제공하고, 홈 마이페이지 진입과 로그아웃 시
루트 인증 화면 전환을 연결
프로필 화면·입력 화면 로직과 홈 네비게이션, 루트 로그아웃
라우팅에 대한 단위 테스트를 추가
@khyeji98 khyeji98 self-assigned this Jul 21, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

마이페이지 프로필 화면 구현 및 공용 프로필 입력 모듈 분리 작업이 깔끔하게 잘 진행되었습니다. TCA Convention과 단방향 데이터 흐름을 충실히 준수하고 있으나, 성능 상 메인 스레드를 블로킹할 수 있는 무거운 연산(스냅샷 이미지 PNG 압축) 및 SwiftUI 뷰 선언 방식에 대해 일부 개선이 필요합니다.

}

@MainActor
public var profilePNGData: Data? {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 [P2] Major

image.snapshot().pngData()는 뷰 계층을 UIImage로 렌더링한 후 고부하의 CPU 연산이 수반되는 PNG 데이터 인코딩을 동기적으로 수행합니다.

이 연산이 @MainActor 프로퍼티에서 동기 처리되므로, 프로필 이미지 수정 버튼 탭 시 메인 스레드가 일시적으로 블로킹되어 UI 버벅임(Jank)이 발생할 위험이 큽니다. 스냅샷 추출은 메인 스레드에서 하되, PNG 인코딩 변환 작업은 Task.detached 등을 통해 협력 스레드 풀에서 비동기 처리할 것을 강력히 권장합니다.

Suggested change
public var profilePNGData: Data? {
@MainActor
public var profileSnapshot: UIImage {
image.snapshot()
}

이후 호출부(ProfileInputFeature.swift 등)에서 아래처럼 비동기 헬퍼를 도입해 메인 스레드를 보호해 주십시오.

func makePNGData(from image: UIImage) async -> Data? {
    await Task.detached(priority: .userInitiated) {
        image.pngData()
    }.value
}

}

@ViewBuilder
private var profileMenu: some View {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 [P3] Minor

SwiftUI 선언 규칙에 따라 복잡한 분기나 별도 역할을 가진 하위 영역은 부모 뷰 내부의 @ViewBuilder 연산자 프로퍼티 대신 전용 struct 형태로 독립시키는 것이 좋습니다.

뷰 모델 격인 Store 상태 변화에 따른 Invalidation 범위를 줄여 효율적인 부분 렌더링을 유도할 수 있도록 profileMenu를 별도 SubView struct로 전환해 주세요.

Suggested change
private var profileMenu: some View {
private struct ProfileMenu: View {
@Binding var isShowingMenu: Bool
@Binding var isPhotosPickerPresented: Bool
let store: StoreOf<ProfileInputFeature>
var body: some View {
if isShowingMenu {
GeometryReader { geo in
DesignSystem.Menu(items: [
.init(
label: "프로필 설정하기",
icon: .Asset.icStar24,
iconColor: Colors.gray600
) {
store.send(.avatarMenuProfileSetupTapped)
isShowingMenu = false
},
.init(
label: "앨범에서 선택하기",
icon: .Asset.icAlbum24,
iconColor: Colors.gray600
) {
isPhotosPickerPresented = true
isShowingMenu = false
}
])
.frame(width: Metric.menuWidth)
.offset(
x: (geo.size.width + Metric.avatarSize) / 2 - Metric.menuWidth,
y: Spacing.spacing400 + Metric.avatarSize + Spacing.spacing200
)
}
}
}
}

guard let httpResponse = response as? HTTPURLResponse else {
throw ProfileImageUploadError.invalidResponse
}
guard (200..<300).contains(httpResponse.statusCode) else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 [P4] Readability

Swift 스타일 가이드에 따라 가독성 향상을 위해 guard 조기 리턴(early return) 구문 블록이 끝난 이후에는 개행(blank line)을 두는 것을 규칙으로 삼고 있습니다. 이전 guard 블록의 닫는 괄호 다음에 빈 줄을 삽입해 주세요.

Suggested change
guard (200..<300).contains(httpResponse.statusCode) else {
}
guard (200..<300).contains(httpResponse.statusCode) else {

khyeji98 added 2 commits July 21, 2026 16:31
메인 액터에서 pngData 인코딩까지 동기 수행하던 것을
snapshot만 메인에서 얻고 인코딩은 Task.detached로 분리해
프로필 저장 시 메인 스레드 블로킹 방지
@khyeji98
khyeji98 merged commit c2f249b into develop Jul 21, 2026
1 check failed
@khyeji98
khyeji98 deleted the feature/#113 branch July 21, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant