Skip to content

Create Week9 Misssion 1#42

Open
umckee5696 wants to merge 1 commit into
mainfrom
Week09/Yido
Open

Create Week9 Misssion 1#42
umckee5696 wants to merge 1 commit into
mainfrom
Week09/Yido

Conversation

@umckee5696
Copy link
Copy Markdown
Contributor

@umckee5696 umckee5696 commented May 24, 2026

📌 미션 번호

9주차 실전 미션


📋 구현 사항

  • 마이페이지 UI 전체 구현
  • ReqRes API를 활용하여 1번 유저 프로필 정보 서버 연동
  • 팔로잉 리스트를 HorizontalPager로 구현

📷 스크린샷

image

✅ 체크리스트

  • Merge 하려는 브랜치가 올바르게 설정되어 있나요?
  • 에뮬레이터 또는 실제 기기에서 정상 동작하나요?
  • 불필요한 주석 및 Log가 제거되었나요?

🔥 질문 사항

@umckee5696 umckee5696 requested a review from Dawon-Y May 24, 2026 08:06
@umckee5696 umckee5696 self-assigned this May 24, 2026
@umckee5696 umckee5696 changed the title 9week Misssion 1,2,3 Create Week9 Misssion 1 May 24, 2026
@Dawon-Y
Copy link
Copy Markdown
Contributor

Dawon-Y commented Jun 1, 2026

안녕하세요! 9주차 과제 진행하시느라 고생 많으셨습니다💚


1) 요구사항 체크리스트 기준 리뷰

1-1) 마이페이지 UI 구현

  • 프로필 이미지/이름/이메일/메뉴(주문/패스/이벤트/설정)/멤버 혜택/팔로잉 섹션/가입일 영역까지 구성되어 있어 UI는 전반적으로 잘 구현되었습니다.
  • 컴포저블 분리(ProfileMenuItem, VerticalDivider, FollowingUserItem)도 좋습니다.

1-2) 서버 데이터 받아서 연결 (userId = 1)

  • ProfileViewModel에서 myUserId = 1로 고정하고 getUserProfile(1) 호출 → uiState.myProfile에 반영하고 화면에서 표시 → 요구사항 충족입니다.

1-3) 팔로잉 리스트 HorizontalPager로 구현

  • followingList가 비어있지 않을 때 rememberPagerState(pageCount = { size }) + HorizontalPager로 구현 → 요구사항 충족입니다.
  • pageSize = PageSize.Fixed(96.dp)로 카드 폭을 고정한 것도 “팔로잉 카드 캐러셀” 느낌을 잘 냈습니다.

2) ProfileScreen.kt 리뷰 (Compose/UI)

잘한 점

  • followingList.isNotEmpty() 조건부로 pager 생성 → 불필요한 pagerState 생성 방지 좋습니다.
  • 메뉴 Row에서 Modifier.weight(1f)로 균등 배치 + Divider 구성도 좋습니다.
  • AsyncImage + CircleShape + placeholder 배경색으로 로딩 시에도 UI가 안정적입니다.

개선/제안 포인트

(1) collectAsState()collectAsStateWithLifecycle() 권장

지금은:

val uiState by viewModel.uiState.collectAsState()
  • 동작은 하지만, Fragment/Activity 라이프사이클에 맞춰 안정적으로 수집하려면
    androidx.lifecycle.compose:compose-runtimecollectAsStateWithLifecycle()가 더 안전합니다.

(2) pager key / 안정성

HorizontalPager에서 page index로 followingList[page]를 접근하는 방식은 기본적으로 OK인데,
리스트가 갱신될 때(네트워크 재호출/리스트 변경) page 인덱스와 데이터가 바뀌면 “보이던 유저가 순간 바뀌는” 문제가 생길 수 있습니다.

  • 가능하면 pager 항목을 id 기반으로 안정화하고 싶다면,
    • pager 자체는 인덱스 기반이라도
    • FollowingUserItem 쪽에 최소한 key 개념을 맞춰주기 위해 remember/상태를 얹을 일이 없도록 유지하거나,
    • “데이터가 변경되는 타이밍”을 ViewModel에서 제어(초기 1회 로딩)하는 게 안전합니다.

(3) 문자열 하드코딩 → stringResource로 분리 추천

예: "프로필 수정", "나이키 멤버 혜택", "회원 가입일: 2025년 9월" 등.

  • 과제 단계에서는 괜찮지만, 실무/팀 코드 기준에서는 리소스 분리가 일반적입니다.

(4) 가입일 하드코딩

text = "회원 가입일: 2025년 9월"
  • 서버에서 값이 없다면 “더미”로 두는 건 가능하지만, 고정 텍스트는 리뷰에서 지적받기 쉬워요.
  • 서버 응답에 가입일이 없다면(ReqRes에는 없음) 차라리:
    • UI 스펙상 필요한 영역이면 “가입일 정보 없음” 같은 형태로 처리하거나
    • 주석으로 “ReqRes API에서 제공되지 않아 임시 고정”을 명시하는 편이 낫습니다.

3) ProfileViewModel.kt 리뷰 (상태/네트워크)

잘한 점

  • ProfileUiState로 화면 상태 묶고 StateFlow로 노출한 구조 Good.
  • init { fetchMyProfile(); fetchFollowingList() }로 초기 로딩 수행 Good.
  • followingList에서 자기 자신 제외(filter { it.id != myUserId }) 처리 Good.

개선/제안 포인트

(1) getUserList(1) 하드코딩 vs 인자 일관성

현재:

val response = userRepository.getUserList(1)
  • myUserId와는 별개로 “페이지=1”이라 문제는 없지만, 의미가 헷갈릴 수 있습니다.
  • 가독성을 위해 아래처럼 명시 추천:
userRepository.getUserList(page = 1)

(2) 에러 메시지 상태가 “한 번 뜨면 계속 남는” 문제

errorMessage를 uiState에 넣어두면, 화면 회전/재수집 시 Toast가 반복될 수 있습니다(현재는 collect 타이밍에 따라).

  • Compose/MVI에서 흔한 패턴:
    • errorMessage는 “이벤트(1회성)”로 분리하거나
    • 처리 후 errorMessage = null로 지우는 액션을 두는 방식이 더 안전합니다.

(3) Retrofit Response를 ViewModel에서 직접 다루는 구조

지금은 ViewModel이 Response<T>를 받아서 isSuccessful, code()까지 봅니다.

  • 과제에서는 OK지만, 계층 분리 관점에서는 Repository가
    • 성공/실패를 Result나 sealed class로 감싸서
    • ViewModel은 “도메인 결과”만 받게 하면 더 깔끔합니다.

(4) 로딩 상태가 없음

현재 UI는 네트워크 중 로딩 표시가 없습니다.

  • isLoading: BooleanProfileUiState에 추가하고,
  • AsyncImage placeholder 외에도 skeleton/indicator를 주면 UX가 좋아집니다.

4) UserRepository.kt, ReqResService.kt, UserData.kt 리뷰

전반적으로 OK

  • ReqRes API 스펙에 맞춰 @SerializedName("first_name"), @SerializedName("last_name") 매핑한 점 Good.
  • UserRepository는 “추상화 레이어” 역할을 잘 하고 있습니다.

제안(선택)

  • UserResponse(val data: UserData)는 단순 구조라 괜찮지만, API가 에러 바디/추가 필드를 줄 수 있으면 확장 대비가 필요할 수 있습니다(과제에서는 과함).

5) 최종 결론

개선 우선순위 TOP 3

  1. collectAsStateWithLifecycle()로 변경
  2. errorMessage를 1회성 이벤트로 처리(Toast 반복 방지)
  3. 가입일/문구 하드코딩 정리(stringResource + 주석/대체 UI)

워크북 진행하시느라 수고하셨습니다! 궁금한 점 있으시면 언제든 말씀해 주세요!

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.

2 participants