Skip to content

[Feat/#23] 프로필 화면 UI 구현#24

Merged
sonyerim merged 18 commits intodevelopfrom
feat/#23-profile-ui
Jan 16, 2026
Merged

[Feat/#23] 프로필 화면 UI 구현#24
sonyerim merged 18 commits intodevelopfrom
feat/#23-profile-ui

Conversation

@sonyerim
Copy link
Copy Markdown
Contributor

@sonyerim sonyerim commented Jan 13, 2026

Related issue 🛠️

Work Description ✏️

  • 프로필 화면 컴포넌트와 스크린 구현을 진행했습니다.

Screenshot 📸

스크린샷
스크린샷 2026-01-13 오후 10 11 29

Uncompleted Tasks 😅

N/A

To Reviewers 📢

  • UserProfile 이미지에 임시로 배경색 넣어뒀습니다.
  • HistorySummaryCard에서 PotiDivider를 사용하려 했는데 가로 구분선만 지원하더라구요! 세로 구분선도 지원 가능하게 코드 수정하고 공통 컴포넌트로 변경하면 좋을 것 같은데 어떻게 생각하시나욥
image

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 사용자 프로필 화면 추가: 프로필 이미지, 닉네임, 이메일, 평점, 활동 정보 및 히스토리 요약 표시
    • 히스토리 요약 카드, 평점 배지, 배지 버튼 등 UI 컴포넌트 추가
  • Refactor

    • 평점 컴포넌트 색상 커스터마이징 지원
    • 사용자 정보 구성 개선 및 뒤로가기 네비게이션 처리 보완
  • Style

    • 디바이더 등 일부 컴포넌트 스타일 개선
  • 문구

    • 히스토리 관련 표시문구(전체/진행중/종료/모집/참여) 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@sonyerim sonyerim self-assigned this Jan 13, 2026
@sonyerim sonyerim added 🌟 Feat 새로운 기능 구현 🔎 UI UI 작업 💕 예림 labels Jan 13, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

프로필 화면 UI와 관련 도메인/계약/뷰모델을 추가하고, 여러 재사용 UI 컴포넌트(유저 프로필, 평점 배지, 유저 정보, 이력 카드 등)를 구현하며 네비게이션에 뒤로가기 콜백(onPopBackStack)을 와이어링했습니다.

Changes

코호트 / 파일(들) 변경 요약
프로필 화면 및 네비게이션
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt, app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt, app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
ProfileScreenRoute 공개 컴포저블 추가(인자로 onPopBackStack), profileNavGraph 시그니처에 onPopBackStack 추가 및 호출부에 navigator.navController::popBackStack 전달
ViewModel 및 UI 계약
app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt, app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt
ProfileViewModel이 BaseViewModel 상속으로 변경(초기 상태 주입), processIntent/사이드이펙트 처리 추가, ProfileUiState/ProfileUiIntent/ProfileUiEffect 계약 추가
도메인 모델
app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt
UserProfile 및 HistorySummary 데이터 클래스 추가
사용자 프로필 UI 컴포넌트
app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt, app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt, app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt
UserProfile, RatingBadge, UserInfo 컴포저블 추가(이미지·닉네임·이메일, 원형 평점 배지, 활동 메시지·가입일 포맷)
이력 및 배지 컴포넌트
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt, app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt
HistorySummaryCard(ALL/IN_PROGRESS/FINISHED 탭, HistorySummaryType enum) 및 BadgeButton 추가(클릭 핸들러 포함)
디자인 시스템 변경
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt, app/src/main/java/com/poti/android/core/designsystem/component/display/PotiDivider.kt
PotiRating에 public 파라미터 iconTint: Color, textColor: Color 추가(Gray800 의존 제거). PotiDivider에 CircleShape 클립 조건부 적용(내부 modifier 변경)
문자열 리소스
app/src/main/res/values/strings.xml
사용자 이력 관련 문자열 5개 추가(user_history_recruit, user_history_participate, user_history_all, user_history_ongoing, user_history_ended)

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant NavHost as PotiNavHost
    participant Route as ProfileScreenRoute
    participant VM as ProfileViewModel
    participant UI as ProfileScreen

    User->>NavHost: 프로필 화면 요청
    NavHost->>Route: composable 호출 (onPopBackStack 제공)
    Route->>VM: ViewModel 인스턴스화 / 초기화
    VM->>VM: loadUserProfile() 실행 -> userProfileLoadState: Success
    VM->>Route: 상태 업데이트 (ProfileUiState)
    Route->>UI: ProfileScreen 렌더링 (UserProfile, RatingBadge, UserInfo, HistorySummaryCard)
    User->>UI: 뒤로가기 클릭
    UI->>VM: ProfileUiIntent.OnBackClick 전송
    VM->>Route: ProfileUiEffect.NavigateBack 발생
    Route->>NavHost: onPopBackStack() 호출
    NavHost->>User: 네비게이션 팝 수행
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 분

Possibly related PRs

Suggested reviewers

  • cmj7271
  • jyvnee
  • doyeon0307
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 프로필 화면 UI 구현이라는 주요 변경사항을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿의 필수 섹션(관련 이슈, 작업 설명, 스크린샷, 검토자 노트)을 대부분 완성했습니다.
Linked Issues check ✅ Passed 모든 링크된 이슈 #23의 요구사항(history component, user-profile component, user-info component, rating-badge component, ProfileScreen)을 코드 변경으로 충족했습니다.
Out of Scope Changes check ✅ Passed PotiDivider의 CircleShape 추가는 HistorySummaryCard에서 필요한 세로 구분선 지원을 위한 범위 내 변경입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01c815c and 6452dd8.

📒 Files selected for processing (2)
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/res/values/strings.xml
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (6)
app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt (1)

36-47: 접근성: contentDescription에 의미 있는 값을 제공하세요.

contentDescription = null로 설정되어 있어 스크린 리더 사용자가 프로필 이미지를 인식할 수 없습니다. 사용자 프로필 이미지임을 알 수 있도록 설명을 추가하는 것이 좋습니다.

또한, 이미지 로딩 실패 시 사용자에게 피드백을 제공하기 위해 placeholdererror 파라미터를 고려해 주세요.

♻️ 제안하는 수정 사항
         AsyncImage(
             model = ImageRequest.Builder(LocalContext.current)
                 .data(imageUrl)
                 .crossfade(true)
                 .build(),
-            contentDescription = null,
+            contentDescription = "$nickname 프로필 이미지",
             contentScale = ContentScale.Crop,
             modifier = Modifier
                 .size(98.dp)
                 .clip(CircleShape)
                 .background(PotiTheme.colors.gray100),
         )
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (1)

56-61: 불필요한 modifier 파라미터 제거 권장.

Icon의 modifier 파라미터는 기본값이 Modifier이므로 명시적으로 지정할 필요가 없습니다.

♻️ 제안된 수정
         Icon(
             imageVector = ImageVector.vectorResource(R.drawable.ic_bullet),
             contentDescription = null,
-            modifier = Modifier,
             tint = PotiTheme.colors.gray800,
         )
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (2)

22-22: 사용되지 않는 import 제거 필요

setValue가 import되어 있지만 파일 내에서 사용되지 않습니다.

🧹 수정 제안
-import androidx.compose.runtime.setValue

146-156: TODO 항목 확인

PR 설명에서 언급하신 대로 PotiDivider가 수평 divider만 지원하여 별도의 DividerSm을 구현하셨네요. 공통 컴포넌트로 전환 계획이 있다면 별도 이슈로 트래킹하는 것을 권장드립니다.

이 TODO를 추적하기 위한 이슈를 생성해 드릴까요?

app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt (1)

27-42: 구현이 깔끔합니다

PotiRating의 새로운 색상 커스터마이징 파라미터를 잘 활용하고 있습니다. 한 가지 사소한 제안으로, Row 내에 자식이 PotiRating 하나뿐이므로 horizontalArrangement = Arrangement.Start는 실질적인 효과가 없어 제거해도 됩니다.

♻️ 선택적 수정
     Row(
         modifier = modifier
             .heightIn(min = 40.dp)
             .clip(CircleShape)
             .background(PotiTheme.colors.black)
             .noRippleClickable(onClick)
             .padding(horizontal = 12.dp, vertical = 8.dp),
-        horizontalArrangement = Arrangement.Start,
         verticalAlignment = Alignment.CenterVertically,
     ) {
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (1)

119-142: Preview 데이터 중복 개선 제안

ProfileScreenPreviewProfileScreenRoute에서 동일한 샘플 데이터가 중복되어 있습니다. 유지보수성을 위해 공통 preview 데이터를 추출하거나, ProfileScreenRoute를 직접 preview하는 방식을 고려해 보세요.

♻️ 선택적 개선 방안
@Preview(showBackground = true)
@Composable
private fun ProfileScreenPreview() {
    PotiTheme {
        ProfileScreenRoute(
            onBackClick = {},
        )
    }
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 614af57 and 6d11f67.

📒 Files selected for processing (8)
  • app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt
  • app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt
  • app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt
  • app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
🧰 Additional context used
🧬 Code graph analysis (6)
app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt (2)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt (1)
  • PotiRating (19-43)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (1)
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (1)
  • ProfileScreenRoute (32-55)
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (5)
app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt (1)
  • UserProfile (24-63)
app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt (1)
  • RatingBadge (21-43)
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (1)
  • UserInfo (24-44)
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (1)
  • HistorySummaryCard (40-105)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: PR CI Check (Lint & Build)
🔇 Additional comments (11)
app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt (2)

24-63: 컴포넌트 구조가 잘 구성되어 있습니다.

UserProfile 컴포넌트가 깔끔하게 구현되어 있고, PotiTheme의 색상과 타이포그래피를 일관되게 사용하고 있습니다. PR 설명에 언급된 대로 gray100 배경색이 임시로 적용되어 있는 점 확인했습니다.


65-81: Preview 구현이 적절합니다.

PotiTheme으로 감싸고 다양한 케이스를 테스트할 수 있도록 구성되어 있습니다. private 접근 제한자를 사용한 것도 좋습니다.

app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (2)

24-44: LGTM!

컴포넌트 구조가 잘 설계되었습니다. Modifier 체인 순서(clip → background → padding)가 올바르고, PotiTheme을 일관되게 사용하고 있습니다.


71-85: LGTM!

Preview가 PotiTheme으로 올바르게 래핑되어 있고, 샘플 데이터가 컴포넌트 사용법을 잘 보여줍니다.

app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (2)

31-38: 미사용 enum 확인 필요

HistorySummaryType enum이 정의되어 있지만 현재 파일 내에서 사용되지 않습니다. 향후 기능 구현을 위한 것인지 확인이 필요합니다. 사용 계획이 없다면 제거하거나, 사용 예정이라면 TODO 주석을 추가하는 것이 좋겠습니다.


71-102: onClick 콜백이 빈 함수로 전달됨

HistoryItemonClick 콜백들이 모두 빈 람다 { }로 전달되고 있습니다. 각 항목 클릭 시 필터링된 목록으로 이동하는 등의 동작이 필요하다면, HistorySummaryCard에 콜백 파라미터를 추가하여 상위 컴포넌트에서 처리할 수 있도록 해야 합니다.

app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt (1)

43-46: LGTM!

profileNavGraphonBackClick 콜백을 추가하고 popBackStack()으로 연결한 것이 적절합니다. 다른 nav graph 패턴과 일관성을 유지하고 있습니다.

app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (1)

22-31: LGTM!

네비게이션 설정이 잘 구성되었습니다. onBackClick 콜백과 paddingValuesProfileScreenRoute에 적절히 전달하고 있습니다.

app/src/main/java/com/poti/android/core/designsystem/component/display/PotiRating.kt (1)

19-42: LGTM!

iconTinttextColor 파라미터를 추가하여 색상 커스터마이징을 지원하면서도, 기본값으로 PotiTheme.colors.gray800을 사용하여 기존 호환성을 유지한 좋은 설계입니다.

app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (2)

32-55: 하드코딩된 샘플 데이터 확인

UI 구현 PR이므로 현재 하드코딩된 데이터는 괜찮습니다. 다만, 실제 데이터 연동 시 ViewModel을 통해 상태를 관리하고 ProfileScreenRoute에서 주입받도록 수정이 필요합니다.


93-97: RatingBadge onClick 핸들러가 비어 있음

RatingBadgeonClick이 빈 람다로 처리되어 있습니다. 평점 상세 화면이나 리뷰 목록으로 이동하는 동작이 필요하다면 ProfileScreen에 콜백 파라미터로 추가하는 것이 좋겠습니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt (1)

26-29: 파라미터 이름 bias가 의미를 명확하게 전달하지 못합니다.

Preview 예시("나의 최애 선택하기", "아이브")를 보면 이 파라미터는 버튼의 레이블/텍스트 역할을 합니다. bias보다는 text, label, 또는 title과 같은 이름이 더 직관적입니다.

♻️ 제안하는 수정
 @Composable
 fun BadgeButton(
-    bias: String,
+    text: String,
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
 ) {

내부 Text 컴포넌트도 함께 수정:

         Text(
-            text = bias,
+            text = text,
             color = PotiTheme.colors.poti800,
             style = PotiTheme.typography.body14m,
         )
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d11f67 and 13fcace.

📒 Files selected for processing (1)
  • app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
🔇 Additional comments (3)
app/src/main/java/com/poti/android/presentation/user/component/BadgeButton.kt (3)

1-24: LGTM!

패키지 구조와 import가 적절하게 구성되어 있습니다.


31-54: LGTM!

Modifier 체인 순서가 올바르게 구성되어 있고, 레이아웃 구현이 깔끔합니다. noRippleClickable 사용이 RatingBadge 등 다른 컴포넌트와 일관성 있게 적용되었습니다.


57-78: LGTM!

Preview가 적절하게 구성되어 있습니다. PotiTheme으로 감싸서 정확한 렌더링을 보장하고, 다양한 사용 예시를 보여줍니다.

Copy link
Copy Markdown
Contributor

@doyeon0307 doyeon0307 left a comment

Choose a reason for hiding this comment

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

짱 빠르다 고생하셨서요!!!

UserProfile 이미지에 임시로 배경색 넣어뒀습니다.

프리뷰에 보이니까 좋네욤

HistorySummaryCard에서 PotiDivider를 사용하려 했는데 가로 구분선만 지원하더라구요! 세로 구분선도 지원 가능하게 코드 수정하고 공통 컴포넌트로 변경하면 좋을 것 같은데 어떻게 생각하시나욥

피그마에서 VerticalDivider도 디바이더 컴포넌트 사용되고 있어서 동의해요!! 굿뜨 아이디어

) {
Column(
modifier = modifier
.widthIn(min = 328.dp),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: fillMaxWidth 적용되어 있기 때문에 widthIn 사용하지 않아도 괜찮을 것 같아요!

.clip(RoundedCornerShape(12.dp))
.background(PotiTheme.colors.gray100)
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: HistoryItem들이 weight(1f)로 남은 공간을 모두 차지하고 있어서, Arrangement.SpaceBetween 속성이 무의미한 것 같습니다!

또한 모든 디바이더에 패딩을 넣어주고 있어서, 대신 horizontalArrangment = Arrangment.spacedby(8.dp) 넣어주면 더 간단히 구현 가능할 것 같네용

+추가로 develop 풀 받아서 horizontalArrangment = Arrangment.spacedby(screenWidthDp(8.dp)) 적용해주면 좋을듯!!

verticalAlignment = Alignment.CenterVertically,
) {
HistoryItem(
title = "전체",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: string resource 추출 어떠신가요~?
코드 컨벤션
전반적으로 적용하기로 팀 내 논의가 있기도 했고 추출 시 다국어 지원이 가능해지는 장점이 있어요

import com.poti.android.core.common.extension.noRippleClickable
import com.poti.android.core.designsystem.theme.PotiTheme

enum class HistorySummaryType {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 이 친구들은 어디 쓰여용?

Copy link
Copy Markdown
Contributor Author

@sonyerim sonyerim Jan 14, 2026

Choose a reason for hiding this comment

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

음 서버 연결할 때 쓰일까봐 만들어뒀는데 지금은 안 쓰이니까 일단 지울게용

@Composable
fun RatingBadge(
rating: String,
onClick: () -> Unit,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 피그마 기획>화면설계서 확인해보았는데, 본 컴포넌트는 버튼은 아니구 별점을 보여주기만 하는 용도로 파악됩니다! onClick 넣지 않아도 될 거 같아용가리

Comment on lines +33 to +35
fun ProfileScreenRoute(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 타 유저 프로필 클릭해서 진입하는 화면이어서, path 파라미터로 user id값을 항상 넘겨주며 열리게 될 것 같아요! 미리 파라미터 뚫어주심 좋을듯해요

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요거는 제가 했습니당!!

modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
.padding(horizontal = 16.dp, vertical = 20.dp),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 여기도 풀 받은 담에 기기대응 확장함수!

imageUrl = imageUrl,
nickname = nickname,
email = email,
modifier = Modifier,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p2: 활용 안 되고 있어 Modifier 지워줘도 될 것 같슴당~!

RatingBadge(
rating = rating,
onClick = {},
modifier = Modifier.width(71.dp),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 피그마에 너비가 hug로 되어있는 걸 보아, 내부 패딩+content에 따라 너비 유동적으로 조정될 수 있도록 의도된 컴포넌트 같습니다. modifier 고정 너비 지워주셔도 좋을 듯 해요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

image image

그냥 지우면 몬생겨져서 .widthIn(min = 71.dp) 줬어요!!

@@ -18,6 +21,12 @@ fun NavController.navigateToProfile() {

fun NavGraphBuilder.profileNavGraph(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: ProfileRoute에 user id 파라미터 추가하실 때, 여기도 아티클 참고하셔서 Type Safe한 방식으로 수정해주심 좋습니다!

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee left a comment

Choose a reason for hiding this comment

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

잘햇다!!!

Comment on lines +31 to +38
enum class HistorySummaryType {
PARTICIPATED_ALL,
PARTICIPATED_IN_PROGRESS,
PARTICIPATED_FINISHED,
RECRUITED_ALL,
RECRUITED_IN_PROGRESS,
RECRUITED_FINISHED,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 이거 participated, recruited 안 나누고 all, in progress, finished 로만 해서 HistorySummaryCard 에 entires.forEach로 하면 HistoryItem 각각 안 넣어줘도 될 것 같네욤

HistoryItem(
title = "전체",
count = totalCount,
onClick = { },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: onClick 이벤트도 컴포넌트 파라미터에 연결해주세용

.clip(CircleShape)
.background(PotiTheme.colors.black)
.noRippleClickable(onClick)
.padding(horizontal = 12.dp, vertical = 8.dp),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 근데 이런 조그만 버튼이나 컴포넌트들은 굳이 안 해도 될 듯!!

Copy link
Copy Markdown
Contributor

@cmj7271 cmj7271 left a comment

Choose a reason for hiding this comment

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

고생 많았어요~~


data class HistoryItemUiModel(
val type: HistorySummaryType,
val titleRes: Int,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p2: StringRes 붙여줘여

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

붙여주자용!! 저번에 pr 답을 못했었는데, StringRes 붙이면 문자열 리소스 id 라는 것을 명시해주는 것이기 때문에 다른 타입의 리소스 id 등을 넣었을 때 런타임 크래스가 발생하는 것을 막아줄 수 있습니다!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 패키지 구조에서 UiState 는 model/ 에 넣기로 한거 같아용
노션

Comment on lines +30 to +34
data class HistorySummaryUiModel(
val totalCount: Int,
val inProgressCount: Int,
val finishedCount: Int,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 data class 가 ProfileUiState 가 아니라 여기에 있는 이유가 뭔가요?
단순 궁금증...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

아마 첨에 Screen만 있었어서 임의로 여기에 만들고
UiState 추가한 뒤에 이사시키는 걸 깜빡한듯..? 이사가자!!

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee left a comment

Choose a reason for hiding this comment

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

pr 반영하고 알려주세요!!


data class HistoryItemUiModel(
val type: HistorySummaryType,
val titleRes: Int,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

붙여주자용!! 저번에 pr 답을 못했었는데, StringRes 붙이면 문자열 리소스 id 라는 것을 명시해주는 것이기 때문에 다른 타입의 리소스 id 등을 넣었을 때 런타임 크래스가 발생하는 것을 막아줄 수 있습니다!

Comment on lines +30 to +34
data class HistorySummaryUiModel(
val totalCount: Int,
val inProgressCount: Int,
val finishedCount: Int,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 이거 스웨거 보면 명세서 나와있어욤!! 그거 보고 도메인 모델 만들어서 사용해주면 될 것 같아요!!! 내 pr 참조!!

onBackClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val scrollState = rememberScrollState()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 요거는 Screen 함수 안에 있어도 될듯!!

Comment on lines +33 to +35
fun ProfileScreenRoute(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

요거는 제가 했습니당!!

Spacer(Modifier.height(24.dp))

HistorySummaryCard(
title = "모집 내역",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 스트링 추출 해주세요!!

Comment on lines +89 to +94
Column(
modifier = modifier.fillMaxSize(),
) {
PotiHeaderPage(
onNavigationClick = onBackClick,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: 이거 혹시 sticky 헤더라고 명시되어 있었나용?? 못 봐서..

Comment on lines +71 to +87
val historyItems = listOf(
HistoryItemUiModel(
type = HistorySummaryType.ALL,
titleRes = R.string.user_history_all,
count = uiState.recruitHistory.totalCount,
),
HistoryItemUiModel(
type = HistorySummaryType.IN_PROGRESS,
titleRes = R.string.user_history_ongoing,
count = uiState.recruitHistory.inProgressCount,
),
HistoryItemUiModel(
type = HistorySummaryType.FINISHED,
titleRes = R.string.user_history_ended,
count = uiState.recruitHistory.finishedCount,
),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: 요거 꼭 Screen에 있어야 하나용?? 제 생각엔 뷰모델이나 uiState에? 있어도 될 것 같은데!

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@app/src/main/java/com/poti/android/domain/model/HistorySummary.kt`:
- Around line 1-10: The domain model HistorySummaryItem currently depends on
presentation.HistorySummaryType; remove this architectural coupling by
relocating the enum or the data class: either move the HistorySummaryType enum
into the domain layer (e.g., create HistorySummaryType in package
com.poti.android.domain.model and update all imports, including in
HistorySummaryCard.kt to import
com.poti.android.domain.model.HistorySummaryType) or move HistorySummaryItem
into the presentation layer so it can keep using the presentation enum; update
all references to the moved symbol (HistorySummaryItem or HistorySummaryType)
across the codebase to the new package.

In `@app/src/main/res/values/strings.xml`:
- Around line 8-16: The two string resources are inconsistent:
header_section_ongoing uses "진행 중" while user_history_ongoing uses "진행중";
standardize them by updating the value of user_history_ongoing to "진행 중" (or
change both if another form is preferred) so both resources match, and ensure
any related translations or usages (header_section_ongoing,
user_history_ongoing) are updated accordingly.
🧹 Nitpick comments (5)
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (3)

36-61: 하드코딩된 샘플 데이터를 ViewModel로 이동 필요

ProfileScreenRoute에서 UI 상태가 하드코딩되어 있습니다. 실제 구현 시 ViewModel에서 데이터를 가져와야 합니다. 현재 플레이스홀더라면 TODO 주석을 추가해주세요.

// TODO: Replace with actual data from ViewModel
// val uiState by viewModel.uiState.collectAsStateWithLifecycle()

130-146: Preview와 Route에서 중복되는 샘플 데이터

historyItems 리스트가 ProfileScreenRouteProfileScreenPreview에서 동일하게 중복됩니다. Preview용 샘플 데이터를 companion object나 별도 파일로 추출하면 유지보수가 용이합니다.

♻️ 권장 수정안
private val previewHistoryItems = listOf(
    HistorySummaryItem(
        type = HistorySummaryType.ALL,
        titleRes = R.string.user_history_all,
        count = 7,
    ),
    // ... 
)

private val previewUiState = ProfileUiState(
    imageUrl = "",
    nickname = "포티포티포티",
    email = "poti@app.jam",
    rating = "4.8",
    infoList = listOf("최근 3일 이내 활동", "2025년 12월 28일 가입"),
    recruitHistoryItems = previewHistoryItems,
)

117-122: 빈 클릭 핸들러 구현 필요

onItemClick = { type -> }가 비어있습니다. 네비게이션이나 필터링 로직 연결이 필요하다면 TODO 주석을 남겨주세요.

 HistorySummaryCard(
     title = stringResource(R.string.user_history_recruit),
     items = uiState.recruitHistoryItems,
-    onItemClick = { type -> },
+    onItemClick = { type -> 
+        // TODO: Navigate to history list filtered by type
+    },
     modifier = Modifier.fillMaxWidth(),
 )
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (2)

33-34: 불필요한 명시적 import

kotlin.collections.forEachIndexedkotlin.collections.lastIndex는 Kotlin 표준 라이브러리에서 자동으로 import되므로 명시적 import가 필요하지 않습니다.

-import kotlin.collections.forEachIndexed
-import kotlin.collections.lastIndex

159-171: Preview에서 하드코딩된 문자열

Preview에서 "참여 내역" 문자열이 하드코딩되어 있습니다. stringResource(R.string.user_history_participate)를 사용하면 일관성을 유지할 수 있습니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b308b4e and 28186c7.

📒 Files selected for processing (5)
  • app/src/main/java/com/poti/android/domain/model/HistorySummary.kt
  • app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/model/ProfileUiState.kt
  • app/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/java/com/poti/android/presentation/user/profile/model/ProfileUiState.kt
🧰 Additional context used
🧬 Code graph analysis (2)
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (6)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt (1)
  • PotiHeaderPage (19-69)
app/src/main/java/com/poti/android/presentation/user/component/UserProfile.kt (1)
  • UserProfile (25-66)
app/src/main/java/com/poti/android/presentation/user/component/RatingBadge.kt (1)
  • RatingBadge (21-42)
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (1)
  • UserInfo (25-45)
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (1)
  • HistorySummaryCard (42-92)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
🔇 Additional comments (3)
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (1)

70-125: LGTM!

ProfileScreen composable 구조가 깔끔하게 구성되어 있습니다. 스크롤 가능한 컨텐츠, 적절한 spacing, 컴포넌트 조합이 잘 되어있습니다.

app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (2)

42-92: LGTM!

HistorySummaryCard 구현이 잘 되어있습니다. Arrangement.spacedBy(8.dp)weight(1f) 적용, 인덱스 기반 divider 렌더링 로직이 적절합니다.


94-131: LGTM!

HistoryItem의 press 상태 처리가 잘 구현되어 있습니다. interactionSource를 활용한 배경색 변경과 noRippleClickable 적용이 적절합니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 8 to +16
<string name="header_section_ongoing">진행 중</string>
<string name="header_section_ended">종료</string>

<!-- User Component -->
<string name="user_history_recruit">모집 내역</string>
<string name="user_history_participate">참여 내역</string>
<string name="user_history_all">전체</string>
<string name="user_history_ongoing">진행중</string>
<string name="user_history_ended">종료</string>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

"진행 중" 표기 불일치 확인 필요

Line 8의 header_section_ongoing은 "진행 중" (띄어쓰기 O)인데, Line 15의 user_history_ongoing은 "진행중" (띄어쓰기 X)입니다. 의도된 차이인지 확인해주세요.

🤖 Prompt for AI Agents
In `@app/src/main/res/values/strings.xml` around lines 8 - 16, The two string
resources are inconsistent: header_section_ongoing uses "진행 중" while
user_history_ongoing uses "진행중"; standardize them by updating the value of
user_history_ongoing to "진행 중" (or change both if another form is preferred) so
both resources match, and ensure any related translations or usages
(header_section_ongoing, user_history_ongoing) are updated accordingly.

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee left a comment

Choose a reason for hiding this comment

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

디벨롭에 새롭게 머지된 거 있으면 항상 풀 당겨와서 작업해주라!!

Comment on lines +7 to +8
val type: HistorySummaryType,
@StringRes val titleRes: Int,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 도메인 레이어는 프레젠테이션 레이어를 알면 안돼요. HistorySummaryType은 프레젠테이션에 있는 타입이고, StringRes도 알고 있으면 안됩니다. 그리고 이 HistorySummaryItem는 도메인 모델이라기보다는 프레젠테이션 계층의 HistorySummaryCard를 그리기 위한 UI 모델이라는 느낌이 드네요. 프레젠테이션 계층으로 빼는게 좋을 것 같습니다.

)
profileNavGraph(
paddingValues = paddingValues,
onBackClick = { navigator.navController.popBackStack() },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: navGraph에는 navController를 직접 넘기고 그래프가 각 Route에 내비게이션 이벤트를 전달하도록 하는 것 더 확장성 있을 것 같습니당

@Composable
fun HistorySummaryCard(
title: String,
items: List<HistorySummaryItem>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: 불필요한 리컴포지션 방지를 위해 ImmutableList를 쓰는게 어떤가요??

color = PotiTheme.colors.poti600,
style = PotiTheme.typography.title18sb,
)
Spacer(modifier = Modifier.height(8.dp))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: 왜 spaceBy 안 썼나용??


@Composable
fun UserInfo(
infoList: List<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: 요기도 ImmutableList 쓸 수 있을듯??


@Composable
fun ProfileScreenRoute(
onBackClick: () -> Unit,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 이거 제가 분철팟 상세 뷰에서 onPopBackStack 으로 써서 Route에서는 onPopBackStack로 이름 맞추면 좋을 것 같아용. Screen에서는 onBackClick 그대로 써도 좋을듯!

Comment on lines +36 to +51
val historyItems = listOf(
HistorySummaryItem(
type = HistorySummaryType.ALL,
titleRes = R.string.user_history_all,
count = 7,
),
HistorySummaryItem(
type = HistorySummaryType.IN_PROGRESS,
titleRes = R.string.user_history_ongoing,
count = 2,
),
HistorySummaryItem(
type = HistorySummaryType.FINISHED,
titleRes = R.string.user_history_ended,
count = 5,
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 이것도 서버에서 오는 데이터이기 때문에 UiState로 빼는게 나을 것 같은데 Route에 둔 이유가 있나요??

email = uiState.email,
)

Spacer(Modifier.height(24.dp))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: 여기서도 간격이 다 24.dp 반복되고 있는데 spaceBy 쓰는게 어때요

Comment on lines +5 to +12
data class ProfileUiState(
val imageUrl: String = "",
val nickname: String = "",
val email: String = "",
val rating: String = "",
val infoList: List<String> = emptyList(),
val recruitHistoryItems: List<HistorySummaryItem> = emptyList(),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: 이거 값들을 하나하나 작성했는데, 스웨거나 명세서 보고 서버에서 오는 데이터들을 도메인 모델로 묶어서 사용하는게 좋을 것 같아요. UserProfile 이라는 도메인 모델을 하나 만들고 UiState에서 UserProfile이라는 도메인모델을 ApiState로 감싸 주세용. ApiState는 데이터 로딩 상태를 나타내기 위해 제가 core에 만들어두웠습니다. 추가로 메인 브랜치 풀 받으면 도메인 모델에 user 패키지 있어서 거기에 UserProfile 만들어서 사용하면 될듯!!

Comment on lines +11 to +16
<!-- User Component -->
<string name="user_history_recruit">모집 내역</string>
<string name="user_history_participate">참여 내역</string>
<string name="user_history_all">전체</string>
<string name="user_history_ongoing">진행중</string>
<string name="user_history_ended">종료</string>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: 주석에 User Component라고 하는 것 보다 User Profile 이나 ProfileScreen 등 어디서 사용하는지 정확히 적어야 덜 헷갈릴 것 같아요! string 리소스 id에도 profile이라는 단어가 들어갔으면 좋겠습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

마이페이지랑, 프로필에서 공통으로 사용돼서 User 라고 했는데, 마이페이지랑 프로필 스트링을 따로 나누는게 좋을까요?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

아뇽 같은 string을 사용하면 같은거 사용해도 되는데 아무래도 프로필이라는게 공통으로 더 잘 묶일 것 같아서 추천해봤습니당 user는.. 뭔가 범위가 좀 넒은 것 같아서?

Copy link
Copy Markdown
Contributor

@jyvnee jyvnee left a comment

Choose a reason for hiding this comment

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

수고햇서~~

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt`:
- Around line 42-45: The call to profileNavGraph passes
navigator.navController::popBackStack (which returns Boolean) into
onPopBackStack (type () -> Unit); wrap the method reference in an explicit
lambda that invokes popBackStack and discards its Boolean result so the types
align and intent is clear—i.e., replace the method reference with a lambda that
calls navigator.navController.popBackStack() and does not return its value when
invoking profileNavGraph's onPopBackStack parameter.
♻️ Duplicate comments (1)
app/src/main/res/values/strings.xml (1)

72-77: ‘진행 중’ 표기 통일 필요
Line 10의 header_section_ongoing은 “진행 중”인데, Line 76의 user_history_ongoing은 “진행중”이라 화면별 띄어쓰기 차이가 납니다. 하나로 통일해 주세요.

🔧 제안 변경
-    <string name="user_history_ongoing">진행중</string>
+    <string name="user_history_ongoing">진행 중</string>
🧹 Nitpick comments (1)
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (1)

76-83: 날짜 포맷/문구 리소스화와 예외 범위 축소를 권장합니다.
현재 날짜 포맷과 “가입” 문구가 하드코딩되어 있어 로컬라이징에 취약하고, 모든 Exception을 삼켜 detekt 경고가 발생합니다. 파싱 실패만 처리하도록 예외 범위를 좁히고 문구/포맷은 strings.xml로 분리해 주세요.

♻️ 제안 변경
-import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeParseException
...
-    } catch (e: Exception) {
+    } catch (e: DateTimeParseException) {
         joinedAt
     }

joinedAt 포맷이 ISO-8601인지도 확인해 주세요. 코딩 가이드라인에 따라 제안드립니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dce68d8 and 01c815c.

📒 Files selected for processing (9)
  • app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt
  • app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
  • app/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/java/com/poti/android/presentation/user/profile/ProfileViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: 코드를 리뷰할 때 다음 Kotlin 및 Jetpack Compose 모범 사례를 중점적으로 확인:

  1. Jetpack Compose 성능 및 UI 최적화:

    • Recomposition 방지: 불필요한 리컴포지션을 유발하는 코드를 지적해줘. 특히 LazyColumn 등 리스트에서 key 파라미터가 누락되었는지 확인해줘.
    • 스마트한 계산: 계산 비용이 높은 로직은 rememberderivedStateOf로 감싸져 있는지 확인해줘.
    • 안정성(Stability): List나 일반 클래스를 State로 사용할 때, 불필요한 리컴포지션을 막기 위해 @Immutable이나 @Stable 어노테이션 사용을 제안해줘.
    • Modifier 규칙: Modifier는 항상 컴포저블 함수의 첫 번째 선택적 파라미터로 받아야 하며, 재사용성을 위해 외부에서 주입받아야 해.
    • 리소스 사용: 하드코딩된 값(색상, 크기, 문자열) 대신 Theme, Dimens, strings.xml 리소스를 사용하는지 체크해줘.
    • 접근성 예외: Icon이나 Image 컴포저블의 contentDescriptionnull인 경우는 장식용(Decorative) 이미지로 간주하고 지적하지 마.
  2. MVI (Model-View-Intent) 아키텍처 및 상태 관리:

    • 단방향 데이터 흐름(UDF): UI는 상태를 직접 변경할 수 없고, 반드시 Intent(또는 Event)를 통해 ViewModel로 요청을 보내야 해.
    • 불변 상태(Immutable State): UiState는 반드시 data class + val 프로퍼티로 구성되어야 하며, 상태 업데이트 시 .copy()를 사용해야 해.
    • Side Effect 분리: 네비게이션, Toast 같은 일회성 이벤트는 UiState가 아닌 Channel이나 SharedFlow로 처리하여 화면 회전 시 재발생하지 않도록 해야 해.
    • 안전한 수집: UI에서 Flow 수집 시 collectAsStateWithLifecycle()을 사용하여 생명주기를 준수하는지 확인해줘.
    • 상태 노출: ViewModel 내부에서 StateFlowSharedFlow를 올바르게 노출하고 있는지 확인해줘.
    • 호이스팅(Hoisting): 상태나 UI 이벤트 처리가 적절하게 호이스팅되었는지 확인해줘. UI 로직과 비즈니스 로직을 분리하고, UI가 로직을 직접 수행하지 않도록 확인해줘.
  3. Clean Architecture (Dependency Rule) 준수:

    • Domain 순수성: Domain 레이어(domain 패키지)는 절대로 안드로이드 의존성(android.*, R 클래스 등)을 가져서는 안 돼. 순수 Kotlin 코드인지 엄격히 확인해줘.
    • 데이터 노출 금지: Data 레이어의 DTO(@Entity, @SerializedName)가 UI나 Domain 레이어로 바로 노출되면 안 돼. 반드시 Mapper를 통해 변환했는지 확인해줘.
    • 계층 간 의존성: UI -> Domain <- Data 방향이 올바른지, UI가 Data 레이어를 직접 참조하고 있진 않은지 감시해줘.
  4. Kotlin & Coroutines 안전성:

    • Scope 사용: GlobalScope 사용을 금지하고, viewModelScopelifecycleScope 사용을 권장해줘.
    • Dispatcher 주입: Dispatchers.IO 등을 하드코딩하지 말고, 테스트 용이성을 위해 생성자를 통해 주입(Injection)받도록 제안해줘.
    • 예외 처리: Coroutine 실행 시 runCatching이나 CoroutineExceptionHandler 등을 통해 예외 처리가 누락되지 않았는지 확인해줘.
    • Null-Safety: 불필요한 !! 연산자 사용을 지적...

Files:

  • app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt
  • app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt
🧬 Code graph analysis (3)
app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (1)
app/src/main/java/com/poti/android/presentation/user/profile/ProfileScreen.kt (1)
  • ProfileScreenRoute (34-55)
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (2)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (1)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
🪛 detekt (1.23.8)
app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt

[warning] 81-81: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🔇 Additional comments (11)
app/src/main/java/com/poti/android/domain/model/user/UserProfile.kt (2)

3-13: UserProfile 모델 정의가 명확합니다.
불변 필드 기반이라 도메인 모델로 사용하기 좋습니다.


15-19: HistorySummary 구조도 깔끔합니다.

app/src/main/java/com/poti/android/presentation/user/component/HistorySummaryCard.kt (4)

33-37: HistorySummaryType 정의 좋습니다.


39-105: HistorySummaryCard 구성과 이벤트 전달이 깔끔합니다.


108-143: HistoryItem의 pressed 상태 처리 좋습니다.


145-173: 프리뷰는 런타임 영향이 없어 검토 생략합니다.

app/src/main/java/com/poti/android/presentation/user/component/UserInfo.kt (3)

27-50: UserInfo 레이아웃 구성 좋습니다.
screenWidthDp 기반 패딩으로 기기 대응도 고려되어 있습니다.


52-74: UserInfoItem 스타일링 OK입니다.


86-101: 프리뷰는 검토 생략합니다.

app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (1)

22-31: 뒤로가기 핸들러 전달 및 패딩 적용 흐름이 명확합니다.
ProfileScreenRouteonPopBackStackModifier.padding(paddingValues)를 전달해 네비게이션-화면 간 책임이 분리되어 깔끔합니다.

app/src/main/java/com/poti/android/presentation/user/profile/model/Contracts.kt (1)

9-19: MVI 계약 구성이 깔끔합니다.
UiState/UiIntent/UiEffect가 명확히 분리되어 있어 상태·이벤트·사이드이펙트 흐름이 이해하기 쉽습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 42 to 45
profileNavGraph(
paddingValues = paddingValues,
onPopBackStack = navigator.navController::popBackStack,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# profileNavGraph 함수 정의 검색
rg -nP --type=kt '\bfun\s+profileNavGraph\s*\(' -C3

Repository: team-poti/POTI-ANDROID

Length of output: 92


🏁 Script executed:

# PotiNavHost 파일의 전체 컨텍스트 확인
find . -name "PotiNavHost.kt" -type f

Repository: team-poti/POTI-ANDROID

Length of output: 135


🏁 Script executed:

# navigator와 navController 정의 확인
rg -nP --type=kt '\bnavigator\b|NavController' app/src/main/java/com/poti/android/presentation/main/ -C2

Repository: team-poti/POTI-ANDROID

Length of output: 92


🏁 Script executed:

# PotiNavHost.kt 파일 전체 확인
cat -n ./app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt

Repository: team-poti/POTI-ANDROID

Length of output: 2115


🏁 Script executed:

# profileNavGraph 함수 검색 (rg without type)
rg -nP 'fun\s+profileNavGraph\s*\(' -C3

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# NavGraph 확장함수들 검색
rg -nP 'fun.*NavGraphBuilder.*profileNavGraph|fun.*profileNavGraph' -C3

Repository: team-poti/POTI-ANDROID

Length of output: 890


🏁 Script executed:

# NavController의 popBackStack 시그니처 확인
rg -n 'fun popBackStack'

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# 혹시 확장함수로 오버라이드되는 경우 확인
rg -n 'fun.*popBackStack\s*\(\s*\)\s*:\s*Unit'

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# PotiNavigator 클래스 정의 확인
rg -n 'class PotiNavigator|interface PotiNavigator' -C5

Repository: team-poti/POTI-ANDROID

Length of output: 1325


🏁 Script executed:

# ProfileNavigation.kt 전체 파일 확인
cat -n app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt

Repository: team-poti/POTI-ANDROID

Length of output: 1290


🏁 Script executed:

# 혹시 popBackStack 오버로드나 확장함수 확인
rg -n 'fun.*NavController.*popBackStack|popBackStack\s*\(.*\)\s*:\s*Unit'

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# 현재 코드 부분 다시 확인 (라인 22-25 전후)
sed -n '20,30p' app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt

Repository: team-poti/POTI-ANDROID

Length of output: 353


🏁 Script executed:

# ProfileScreenRoute 확인
rg -n 'fun ProfileScreenRoute|composable.*ProfileScreenRoute' -C5

Repository: team-poti/POTI-ANDROID

Length of output: 1379


🏁 Script executed:

# 혹시 컴파일러가 SAM 변환을 하는지 확인하기 위해
# 프로젝트의 build.gradle 또는 gradle.properties 확인
cat build.gradle.kts 2>/dev/null || cat build.gradle 2>/dev/null | head -50

Repository: team-poti/POTI-ANDROID

Length of output: 473


🏁 Script executed:

# 다른 navGraph 호출들과 비교하여 onPopBackStack 패턴 확인
rg -n 'onPopBackStack\s*=' -C2

Repository: team-poti/POTI-ANDROID

Length of output: 8484


🏁 Script executed:

# 다른 navGraph 호출들의 onPopBackStack 처리 확인
rg -n 'onPopBackStack\s*=\s*.*::popBackStack' -C1

Repository: team-poti/POTI-ANDROID

Length of output: 2264


🏁 Script executed:

# 혹시 타입 어댑터 또는 확장함수가 있는지 확인
rg -n 'fun.*Boolean.*Unit|popBackStack.*Unit'

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# NavController 확장함수 확인
rg -n 'fun NavController' app/src/main/java/com/poti/android/presentation/main/

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# NavController 타입 확인 및 popBackStack 확인
rg -n 'class NavController|interface NavController|typealias.*NavController'

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# OnboardingNavigation과 PartyDetailNavigation의 함수 시그니처 확인
rg -n 'fun.*OnboardingGuideRoute|fun.*OnboardingNicknameRoute|fun.*OnboardingArtistRoute|fun.*PartyDetailRoute' -A2 | head -40

Repository: team-poti/POTI-ANDROID

Length of output: 1518


🏁 Script executed:

# 혹시 build 오류가 있는지 확인
find . -name "*.log" -o -name "build_errors*" 2>/dev/null | head -5

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# OnboardingNavigation.kt의 OnboardingGuideRoute 호출 부분 전체 확인
sed -n '50,60p' app/src/main/java/com/poti/android/presentation/onboarding/navigation/OnboardingNavigation.kt

Repository: team-poti/POTI-ANDROID

Length of output: 575


🏁 Script executed:

# Kotlin 라이브러리 버전 확인
cat app/build.gradle.kts | grep -A5 -B5 'kotlin\|compose'

Repository: team-poti/POTI-ANDROID

Length of output: 2409


🏁 Script executed:

# NavHostController와 NavController의 관계 확인
rg -n 'NavHostController|typealias.*NavHostController'

Repository: team-poti/POTI-ANDROID

Length of output: 434


🏁 Script executed:

# 프로젝트에서 정의된 popBackStack 확장함수 확인
rg -n 'fun.*popBackStack\s*\(' -C2

Repository: team-poti/POTI-ANDROID

Length of output: 48


🏁 Script executed:

# libs.versions.toml 또는 libs.gradle 확인하여 navigation 버전 확인
find . -name "libs.versions.toml" -o -name "versions.gradle" 2>/dev/null | xargs cat 2>/dev/null | grep -i navigation

Repository: team-poti/POTI-ANDROID

Length of output: 389


명시적인 람다로 래핑하여 타입 안전성을 명확히 하세요.
navController::popBackStackBoolean을 반환하지만, onPopBackStack() -> Unit 타입입니다. 반환값을 명시적으로 무시하는 람다로 래핑하면 의도가 더 분명하고 타입 일관성도 개선됩니다.

🔧 제안 변경
-            onPopBackStack = navigator.navController::popBackStack,
+            onPopBackStack = { navigator.navController.popBackStack() },
🤖 Prompt for AI Agents
In `@app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt` around
lines 42 - 45, The call to profileNavGraph passes
navigator.navController::popBackStack (which returns Boolean) into
onPopBackStack (type () -> Unit); wrap the method reference in an explicit
lambda that invokes popBackStack and discards its Boolean result so the types
align and intent is clear—i.e., replace the method reference with a lambda that
calls navigator.navController.popBackStack() and does not return its value when
invoking profileNavGraph's onPopBackStack parameter.

Copy link
Copy Markdown
Contributor

@doyeon0307 doyeon0307 left a comment

Choose a reason for hiding this comment

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

어푸푸

@sonyerim sonyerim merged commit 1ccb04b into develop Jan 16, 2026
2 checks passed
@github-project-automation github-project-automation bot moved this from To-do to Done in POTI-ANDROID Jan 16, 2026
@sonyerim sonyerim deleted the feat/#23-profile-ui branch January 16, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🌟 Feat 새로운 기능 구현 💕 예림 🔎 UI UI 작업

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Feat] 프로필 화면 UI 구현

4 participants