Skip to content

[Feat/#35] 분철팟 상세 뷰 UI 구현#39

Merged
jyvnee merged 12 commits intodevelopfrom
feat/#35-party-detail-ui
Jan 15, 2026
Merged

[Feat/#35] 분철팟 상세 뷰 UI 구현#39
jyvnee merged 12 commits intodevelopfrom
feat/#35-party-detail-ui

Conversation

@jyvnee
Copy link
Copy Markdown
Contributor

@jyvnee jyvnee commented Jan 14, 2026

Related issue 🛠️

Work Description ✏️

  • 분철팟 상세뷰 UI를 구현했습니다.

Screenshot 📸

스크린샷 동영상
Screenshot_20260115_052403
Screen_recording_20260115_051840.mp4

Uncompleted Tasks 😅

N/A

To Reviewers 📢

애뮬 돌려보면 잘 나오는데 녹화하면 개밤티 필터가 씌워져서 나옴!!!!! ㅠㅠ

Summary by CodeRabbit

릴리즈 노트

  • 새로운 기능

    • 파티 상세 화면 추가: 이미지, 상세 정보, 배송 옵션, 판매자 및 참가자 목록 표시
    • 참가자 및 배송 옵션 데이터 구조 추가
    • 더 명확한 라우팅(파티 상세·프로필 이동 시 ID 전달)
  • 개선사항

    • 프로필 이미지 처리 개선(널 가능, 고정 플레이스홀더 적용)
    • 금액 표시용 정수 포맷팅(toMoneyString) 추가
    • 반응형 UI 패딩 적용 및 문자열 리소스 정리

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

@jyvnee jyvnee requested a review from a team January 14, 2026 20:20
@jyvnee jyvnee self-assigned this Jan 14, 2026
@jyvnee jyvnee added 🌟 Feat 새로운 기능 구현 🔎 UI UI 작업 ✨ 지현 labels Jan 14, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 14, 2026

📝 Walkthrough

Walkthrough

분철팟 상세 UI와 관련 도메인 모델, 네비게이션(콜백→NavController), ViewModel 상태/이펙트, 여러 Compose 컴포넌트, 문자열 리소스 및 반응형 패딩/프로필 이미지 로더 변경을 추가합니다. (기능적 흐름·네비게이션과 UI/상태 연동이 도입됨)

Changes

Cohort / File(s) 요약
도메인 모델 추가
app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt, app/src/main/java/com/poti/android/domain/model/party/Participant.kt, app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt, app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt, app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt
PartyDetail과 관련된 데이터 클래스(배송옵션, 참가자, 이미지, 사용자 요약) 추가
상태 타입 및 계약
app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt, app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
파티 상태 enum과 UI 상태/인텐트/이펙트 계약(PartyDetailUiState, PartyDetailIntent, PartyDetailEffect) 추가
UI 컴포넌트 (Compose)
app/src/main/java/com/poti/android/presentation/party/detail/component/...
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt
상세화면 관련 컴포저블들(헤더, 콘텐츠, 참가자, 업로더 등) 추가 및 PotiProfileSummary가 profileImageUrl: String?로 변경되고 SubcomposeAsyncImage→AsyncImage로 교체
화면·라우트·네비게이션 변경
app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt, .../party/PartyNavigation.kt, .../party/detail/navigation/PartyDetailNavigation.kt, .../party/home/navigation/HomeNavigation.kt, .../user/profile/navigation/ProfileNavigation.kt
콜백 기반 네비게이션을 NavController 전달 방식으로 전환, ProfileRoute/PartyDetailRoute가 id 파라미터를 갖도록 변경 및 NavController 기반 호출로 전환
화면 구현 및 뷰모델
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt, app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt, app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt
PartyDetailRoute 시그니처 확장(뒤로/참여/프로필 내비게이션 콜백), PartyDetailScreen 구성 및 PartyDetailViewModel에 SavedStateHandle 기반 라우트 파싱, 초기 로드 인텐트와 더미 데이터 로딩 추가
문자열 리소스
app/src/main/res/values/strings.xml
파티 상세 및 버튼 관련 문자열 리소스 추가/중복 블록 제거, 일부 기존 문자열 삭제
유틸/스타일 조정
app/src/main/java/com/poti/android/core/common/extension/IntExt.kt, app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt, .../PotiHeaderPage.kt, .../PotiHeaderPrimary.kt
Int 확장 toMoneyString() 추가 및 screenWidthDp 사용으로 반응형 수평 패딩 적용

Sequence Diagram(s)

sequenceDiagram
  participant Nav as NavController
  participant Route as PartyDetailRoute
  participant VM as PartyDetailViewModel
  participant UI as PartyDetailScreen
  participant Repo as (Data source) 

  Nav->>Route: navigateToPartyDetail(partyId)
  Route->>VM: savedStateHandle -> toRoute -> partyId
  VM->>VM: init { processIntent(LoadPartyDetail) }
  VM->>Repo: fetchPartyDetail(partyId)  -- (currently returns dummy)
  Repo-->>VM: PartyDetail data
  VM-->>UI: uiState = Success(PartyDetail)
  UI->>UI: render(Header, Content, Participants, Uploader)
  UI->>VM: user interaction (OnJoinClick / OnUploaderClick / OnBackClick)
  VM-->>Nav: emit Effect (NavigateToJoin / NavigateToProfile / NavigateBack)
  Nav->>Nav: perform navigation action
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • cmj7271
  • sonyerim
  • 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 분철팟 상세 뷰 UI 구현이 완료되었으며, 코드 변경 사항들이 이슈 #35의 목표를 충족하고 있습니다.
Out of Scope Changes check ✅ Passed 네비게이션 리팩토링, 모델 클래스 추가, UI 컴포넌트 구현 등이 모두 상세 뷰 UI 구현 목표와 관련있는 변경입니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

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.

고생하셨습니당당당!!!

Comment on lines +74 to +77
PotiHeaderPage(
onNavigationClick = onBackClick,
title = stringResource(R.string.party_detail_title, partyDetail.userSummary.nickname),
)
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: 스티키 스티키 하다고 합니다!! by 디쌤

VerticalDivider(
thickness = 1.dp,
color = PotiTheme.colors.gray800,
modifier = Modifier.height(21.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.

p2: 피그마에 fill로 되어 있어 시스템 텍스트 사이즈에 따라 높이가 달라져야할 것 같은데 Text 높이 재서 넣어주는 건 어때요?? 넘 복잡한강

style = PotiTheme.typography.title18sb,
color = PotiTheme.colors.black,
minLines = 2,
maxLines = 2,
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 name="party_detail_uploader">모집자</string>
<string name="party_detail_participants">참여자</string>
<string name="party_detail_review_count">%d개의 평가</string>
<string name="party_detail_participants_count">%1d/%2d</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.

p1: 여기 d 앞에 숫자 2 빼줘야할 것 같아요! 정수 두 자리로 처리돼서 슬래시 뒤에 공백 추가 된 것처럼 보이네욤
image

Comment on lines +47 to +51
PotiIconButton(
iconRes = R.drawable.ic_arrow_right_lg,
onClick = { onClick(userSummary.userId) },
tint = PotiTheme.colors.gray700,
)
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: IconButton이 Row에 쏙 들어온게 아니고 이렇게 튀어나가 있어서 이 버튼은 다르게 구현해야할 것 같슴다!
image

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.

이거는 버튼이 Row 에 가운데 정렬 되어 있어서 Row 안에 넣고 Screen에서 PartyUploaderInfo 패딩을 좌우 다르게 줬습니당 피그마에 Row 밖에 있다고 꼭 코드 짤 때도 Row 밖에 넣을 필요는 없음!!

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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (1)

19-23: 프로필 화면 구현이 누락되어 있습니다.

composable<ProfileRoute.Profile> 블록이 비어있어 이 경로로 네비게이션 시 아무것도 표시되지 않습니다. 또한 paddingValues 파라미터가 전달되지 않고 있습니다.

코드베이스의 다른 네비게이션 패턴(authNavGraph, historyNavGraph, onboardingNavGraph)과 일관성 있게 다음과 같이 구현해주세요:

🛠️ 권장 수정안
 fun NavGraphBuilder.profileNavGraph(
     paddingValues: PaddingValues,
 ) {
-    composable<ProfileRoute.Profile> { }
+    composable<ProfileRoute.Profile> { backStackEntry ->
+        val profile = backStackEntry.toRoute<ProfileRoute.Profile>()
+        ProfileScreen(
+            userId = profile.userId,
+            modifier = Modifier.padding(paddingValues),
+        )
+    }
 }

참고로 현재 코드베이스에서 ProfileScreen 컴포저블을 찾을 수 없습니다. 아직 구현 중이거나 다른 파일에 있다면 경로를 확인해주세요. TODO 주석을 추가하는 것도 좋은 방안입니다.

🤖 Fix all issues with AI agents
In `@app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt`:
- Around line 3-7: Fix the minor comment typo in the DeliveryOption data class:
change the comment for the deliveryId property (currently "바송 방식 ID") to the
correct text "배송 방식 ID" in the DeliveryOption declaration so the comment reads
correctly alongside the deliveryId property.

In
`@app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt`:
- Line 20: The import for hiltViewModel is using the deprecated package; replace
the import statement "androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel"
with "androidx.hilt.navigation.compose.hiltViewModel" so all usages of
hiltViewModel() in PartyDetailScreen.kt reference the modern package; ensure
there are no other deprecated hilt lifecycle imports in the file and run a quick
build to confirm the symbol resolves.

In `@app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt`:
- Around line 8-18: HomeRoute declares an unused parameter
onNavigateToGoodsCategory; either remove that parameter from HomeRoute's
signature or forward it to HomeScreen so it's used. If removing, delete
onNavigateToGoodsCategory from HomeRoute's parameter list and any callers; if
keeping, add a corresponding argument to the HomeScreen call (e.g.,
HomeScreen(onClick = onNavigateToPartyDetail, onNavigateToGoodsCategory =
onNavigateToGoodsCategory, modifier = modifier)) and ensure HomeScreen
defines/accepts that parameter. Make the change in the HomeRoute function and
update any affected call sites or HomeScreen signature accordingly.

In `@app/src/main/res/values/strings.xml`:
- Line 36: The string resource party_detail_announcement currently contains
placeholder text ("어쩌구 저쩌구"); replace its value in strings.xml with the
finalized announcement text from product/PM, preserving any intended line breaks
or formatting (use \n for newlines or string resources with <xliff:g> if needed)
and ensure the string name party_detail_announcement remains unchanged so
callers referencing it are unaffected.
♻️ Duplicate comments (1)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (1)

50-54: IconButton 레이아웃 이슈가 아직 남아있는 것 같아요.

이전 리뷰에서 지적된 것처럼, PotiIconButton이 Row의 경계를 벗어나는 문제가 있을 수 있어요. PotiIconButton의 내부 구현을 보면 size(48.dp)로 고정되어 있고, padding(12.dp)가 추가로 적용되어 있어서 실제 터치 영역이 의도보다 클 수 있어요.

해결 방안으로는:

  1. IconButton 대신 Icon에 직접 clickable modifier를 적용하거나
  2. Row에 적절한 패딩/클리핑을 적용하는 방법을 고려해 보세요.
🧹 Nitpick comments (11)
app/src/main/java/com/poti/android/core/common/extension/IntExt.kt (1)

5-8: DecimalFormat 인스턴스 재사용을 고려해 주세요.

현재 구현에서는 toMoneyString()이 호출될 때마다 새로운 DecimalFormat 인스턴스가 생성됩니다. 자주 호출되는 경우 성능에 영향을 줄 수 있으므로, 인스턴스를 캐싱하는 방식을 고려해 보시면 좋겠습니다.

♻️ 제안된 개선 코드
 package com.poti.android.core.common.extension
 
 import java.text.DecimalFormat
 
+private val moneyFormat = DecimalFormat("#,###")
+
 fun Int.toMoneyString(): String {
-    val decimalFormat = DecimalFormat("#,###")
-    return decimalFormat.format(this)
+    return moneyFormat.format(this)
 }

참고: DecimalFormat은 스레드 안전하지 않으므로, 멀티스레드 환경에서 사용될 가능성이 있다면 ThreadLocal을 사용하거나 현재 방식을 유지하는 것이 안전합니다.

app/src/main/java/com/poti/android/domain/model/party/Participant.kt (1)

3-9: Domain 모델 구현이 깔끔합니다.

Domain 레이어의 순수성이 잘 유지되어 있고, data classval 프로퍼티로 불변성이 보장되어 있습니다.

다만, selectedMembers: List<String>은 Compose에서 State로 사용될 때 불필요한 리컴포지션을 유발할 수 있습니다. 이 모델이 UI State에서 직접 사용된다면, @Immutable 어노테이션 추가를 권장드립니다.

♻️ 선택적 개선안
 package com.poti.android.domain.model.party
 
+import androidx.compose.runtime.Immutable
+
+@Immutable
 data class Participant(
     val userId: Long, // 참여자 유저 ID
     val nickname: String, // 참여자 닉네임
     val profileImage: String?, // 참여자 프로필 이미지 (없으면 null)
     val rating: Double, // 참여자 평점
     val selectedMembers: List<String>, // 선점한 멤버 목록
 )
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (1)

41-47: rating 표시 형식 확인이 필요해요.

userSummary.rating.toString()으로 Double 값을 그대로 문자열로 변환하고 있는데, 예를 들어 4.54.5로 표시되지만 4.04.0으로 표시될 거예요. 만약 소수점 한 자리로 통일하거나 특정 포맷이 필요하다면 String.format("%.1f", rating) 등의 포맷팅을 고려해 주세요.

♻️ 포맷팅 적용 예시
             PotiProfileSummary(
                 profileImageUrl = userSummary.profileImage,
                 nickname = userSummary.nickname,
                 sizeType = PotiProfileSummarySize.LARGE,
-                rating = userSummary.rating.toString(),
+                rating = String.format("%.1f", userSummary.rating),
                 reviewText = stringResource(R.string.party_detail_review_count, userSummary.reviewCount),
                 modifier = Modifier.weight(1f),
             )
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (1)

24-26: 컴포저블 파라미터 최적화를 고려해 보세요.

현재 PartyDetail 전체를 파라미터로 받고 있지만, 실제로 사용하는 필드는 participants, currentCount, totalCount뿐입니다. Compose의 리컴포지션 최적화를 위해 필요한 필드만 개별 파라미터로 받는 것이 권장됩니다.

♻️ 리팩토링 제안
 `@Composable`
 fun PartyParticipantsInfo(
-    partyDetail: PartyDetail,
+    participants: List<Participant>,
+    currentCount: Int,
+    totalCount: Int,
     modifier: Modifier = Modifier,
 ) {

이렇게 하면 PartyDetail의 다른 필드가 변경되어도 이 컴포저블이 불필요하게 리컴포지션되지 않습니다. 또한, Participant가 일반 data class이므로 @Immutable 또는 @Stable 어노테이션 적용도 검토해 주세요.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt (2)

31-40: remember를 사용하여 계산 결과를 캐싱하는 것을 권장드립니다.

textHeightDp 계산이 리컴포지션마다 반복될 수 있습니다. densitytextStyle이 변경될 때만 재계산되도록 remember로 감싸주시면 좋겠습니다.

♻️ 리팩토링 제안
     val density = LocalDensity.current
     val textStyle = PotiTheme.typography.body14m

-    val textHeightDp = with(density) {
+    val textHeightDp = remember(density, textStyle) {
+        with(density) {
-        if (textStyle.lineHeight.isSp) {
-            textStyle.lineHeight.toDp()
-        } else {
-            textStyle.fontSize.toDp()
+            if (textStyle.lineHeight.isSp) {
+                textStyle.lineHeight.toDp()
+            } else {
+                textStyle.fontSize.toDp()
+            }
         }
     }

코딩 가이드라인에 따라 계산 비용이 있는 로직은 remember로 감싸는 것이 권장됩니다.


27-29: 컴포저블 파라미터 최적화를 고려해 보세요.

PartyParticipantsInfo와 마찬가지로, PartyDetail 전체 대신 필요한 필드(content, deadline, deliveryOptions)만 받으면 리컴포지션 범위를 줄일 수 있습니다. 일관성 있는 설계를 위해 검토해 주세요.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (2)

33-48: Column에 Modifier.weight(1f) 추가를 권장드려요.

Row에서 Arrangement.SpaceBetween을 사용하고 있지만, 왼쪽 Columnweight(1f)가 없으면 긴 텍스트가 오른쪽 하트 아이콘을 밀어낼 수 있어요. 레이아웃 안정성을 위해 수정을 고려해 주세요.

♻️ 제안하는 수정 사항
     Row(
         modifier = modifier.fillMaxWidth(),
         horizontalArrangement = Arrangement.SpaceBetween,
     ) {
-        Column {
+        Column(modifier = Modifier.weight(1f)) {
             Text(
                 text = partyDetail.artist,

24-28: PartyDetail 타입의 안정성(Stability) 검토가 필요해요.

PartyDetailList 타입의 프로퍼티들(images, deliveryOptions, participants)을 포함하고 있어서, Compose 컴파일러가 이를 불안정(unstable)한 타입으로 인식할 수 있어요. 이로 인해 불필요한 리컴포지션이 발생할 수 있으니, PartyDetail data class에 @Immutable 어노테이션 추가를 고려해 주세요.

app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (1)

37-37: popBackStack 반환값 처리에 대해 알려드려요.

navController::popBackStackBoolean을 반환하지만, onPopBackStack() -> Unit 타입이에요. Kotlin에서는 반환값이 무시되어 동작에는 문제가 없지만, 백스택이 비어있을 때의 처리가 필요하다면 별도 래퍼 함수 사용을 고려해 주세요.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (2)

94-98: onLikeClick 콜백이 비어있어요.

좋아요 기능의 콜백이 빈 람다로 되어 있어요. 추후 ViewModel과 연결하여 좋아요 Intent를 처리해야 할 것 같아요. 현재 UI 구현 단계라면 TODO 주석을 남겨주시면 추적이 용이해요.

TODO 주석 추가나 ViewModel 연결 코드 생성을 도와드릴까요?


97-137: 반복되는 padding 값을 상수로 추출하면 좋을 것 같아요.

screenWidthDp(16.dp)가 여러 곳에서 반복 사용되고 있어요. 유지보수성을 위해 상수로 추출하는 것을 고려해 주세요.

♻️ 제안하는 수정 사항
`@Composable`
private fun PartyDetailScreen(
    // ...
) {
    val horizontalPadding = screenWidthDp(16.dp)
    
    // 사용 예시
    PartyDetailHeaderInfo(
        partyDetail = partyDetail,
        onLikeClick = {},
        modifier = Modifier.padding(horizontal = horizontalPadding, vertical = 20.dp),
    )
    // ...
}
📜 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 bdc6c07 and f42cb95.

📒 Files selected for processing (26)
  • app/src/main/java/com/poti/android/core/common/extension/IntExt.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPrimary.kt
  • app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt
  • app/src/main/java/com/poti/android/domain/model/party/Participant.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt
  • app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt
  • app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt
  • app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
  • app/src/main/res/values/strings.xml
🧰 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/core/common/extension/IntExt.kt
  • app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt
  • app/src/main/java/com/poti/android/domain/model/party/Participant.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt
  • app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt
  • app/src/main/java/com/poti/android/presentation/main/PotiNavHost.kt
  • app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt
  • app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPrimary.kt
  • app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiBottomButton.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt
  • app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt
  • app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt
  • app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt
  • app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt
🧬 Code graph analysis (10)
app/src/main/java/com/poti/android/presentation/party/PartyNavigation.kt (3)
app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt (1)
  • homeNavGraph (24-35)
app/src/main/java/com/poti/android/presentation/party/goodsfilter/navigation/GoodsNavigation.kt (1)
  • goodsFilterNavGraph (34-47)
app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (1)
  • partyDetailNavGraph (31-48)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (3)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt (1)
  • PotiProfileSummary (33-93)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiPrimaryTag.kt (1)
  • PotiPrimaryTag (42-61)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (2)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiIconButton.kt (1)
  • PotiIconButton (19-35)
app/src/main/java/com/poti/android/core/designsystem/theme/Theme.kt (1)
  • PotiTheme (48-71)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.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/core/designsystem/component/navigation/PotiHeaderPrimary.kt (1)
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/component/navigation/PotiBottomButton.kt (1)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (2)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (1)
  • PartyDetailRoute (35-50)
app/src/main/java/com/poti/android/presentation/party/detail/PartyJoinScreen.kt (1)
  • PartyJoinRoute (6-8)
app/src/main/java/com/poti/android/core/designsystem/component/navigation/PotiHeaderPage.kt (1)
app/src/main/java/com/poti/android/core/common/util/DimensionUtil.kt (1)
  • screenWidthDp (20-26)
app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt (1)
app/src/main/java/com/poti/android/presentation/party/home/HomeScreen.kt (1)
  • HomeRoute (8-18)
app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (3)
app/src/main/java/com/poti/android/core/designsystem/component/display/PotiProfileSummary.kt (1)
  • PotiProfileSummary (33-93)
app/src/main/java/com/poti/android/core/designsystem/component/button/PotiIconButton.kt (1)
  • PotiIconButton (19-35)
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 (25)
app/src/main/java/com/poti/android/presentation/user/profile/navigation/ProfileNavigation.kt (2)

10-13: LGTM! Type-safe Navigation 패턴이 잘 적용되었습니다.

@Serializabledata class를 활용한 Type-safe Navigation은 Modern Android Development에서 권장하는 방식입니다. userId를 route 파라미터로 전달하는 구조가 적절합니다.


15-17: LGTM! NavController 확장 함수가 깔끔하게 구현되었습니다.

Navigation 로직을 확장 함수로 캡슐화하여 호출부에서 route 구조를 알 필요 없이 사용할 수 있도록 한 점이 좋습니다.

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

16-31: LGTM! 반응형 패딩 적용이 잘 되었습니다.

screenWidthDp를 사용하여 화면 너비에 따른 반응형 수평 패딩을 적용한 부분이 PotiBottomButton, PotiHeaderPrimary 등 다른 디자인 시스템 컴포넌트들과 일관성 있게 구현되었습니다. Modern Android Development 권장 사항에 따라 다양한 화면 크기를 지원하는 좋은 접근 방식입니다.

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

12-28: LGTM! 반응형 수평 패딩 적용이 일관되게 구현되었습니다.

screenWidthDp(16.dp)를 사용하여 다양한 화면 크기에서 적절한 여백을 제공하는 반응형 레이아웃이 적용되었습니다. 다른 네비게이션 컴포넌트들과 동일한 패턴을 따르고 있어 일관성이 좋습니다.

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

21-37: LGTM! 반응형 패딩이 잘 적용되었습니다.

헤더의 시작(20.dp)과 끝(4.dp) 패딩에 screenWidthDp를 적용하여 다양한 화면 크기에서 적절한 레이아웃을 유지하도록 구현되었습니다. PotiHeaderPage, PotiBottomButton과 일관된 반응형 패딩 전략을 따르고 있어 디자인 시스템의 통일성이 유지됩니다.

app/src/main/java/com/poti/android/presentation/party/home/navigation/HomeNavigation.kt (1)

24-34: NavController 기반 네비게이션 전환이 잘 되어 있습니다.

콜백 기반에서 NavController 기반으로 전환하면서 메서드 레퍼런스(navController::navigateToPartyDetail)를 활용한 점이 깔끔합니다. 이 패턴은 Modern Android Development에서 권장하는 방식이며, 네비게이션 로직을 일관되게 관리할 수 있게 해줍니다.

app/src/main/java/com/poti/android/domain/type/PartyStatusType.kt (1)

1-10: Domain 타입 정의가 적절합니다.

PartyStatusType enum이 순수 Kotlin으로 작성되어 Domain 레이어의 순수성이 잘 유지되어 있습니다. 각 상태에 대한 한글 주석도 비즈니스 로직 이해에 도움이 됩니다.

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

47-56: AsyncImage로의 전환이 적절합니다.

SubcomposeAsyncImage에서 AsyncImage로 단순화하면서 placeholdererror 처리가 깔끔하게 구현되어 있습니다. profileImageUrl을 nullable로 변경한 것도 프로필 이미지가 없는 경우를 적절히 처리할 수 있게 해줍니다.

app/src/main/java/com/poti/android/domain/model/user/UserSummary.kt (1)

3-9: LGTM! 깔끔한 도메인 모델이에요.

순수 Kotlin으로 작성된 불변(immutable) data class로, Clean Architecture의 Domain 레이어 원칙을 잘 준수하고 있어요. profileImage의 nullable 처리도 적절하게 되어 있네요.

app/src/main/java/com/poti/android/domain/model/party/PartyImage.kt (1)

3-6: LGTM! 단순하고 명확한 도메인 모델이에요.

이미지 메타데이터를 담는 불변 data class로, 안드로이드 의존성 없이 순수 Kotlin으로 잘 구현되어 있어요.

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

18-39: NavController 기반 네비게이션으로의 전환, 좋아요!

콜백 기반에서 NavController 직접 전달 방식으로 변경하신 것은 Modern Android Development의 권장 패턴과 일치해요. 코드 가독성과 일관성이 향상되었네요.

참고로, goodsFilterNavGraphpartyCreateNavGraph는 현재 navController를 받지 않고 있는데, 향후 해당 화면들에서 네비게이션이 필요해지면 동일한 패턴으로 확장하시면 될 것 같아요.

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

32-35: LGTM! 네비게이션 호출 방식이 잘 업데이트되었어요.

partyNavGraphNavController를 직접 전달하는 방식으로 깔끔하게 변경되었네요. navigator.navController를 통해 접근하는 것도 적절해요.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyUploaderInfo.kt (1)

22-27: Composable 시그니처가 잘 구성되었어요.

필수 파라미터(userSummary, onClick) 다음에 Modifier를 선택적 파라미터로 배치한 것은 Compose 컨벤션에 맞아요. Modifier가 첫 번째 선택적 파라미터로 올바르게 위치해 있네요.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (1)

12-13: LGTM! MVI 패턴에 맞는 상태 노출 방식입니다.

MutableStateFlow를 private으로 선언하고 asStateFlow()로 불변 StateFlow를 노출하는 패턴이 올바르게 적용되어 있습니다. Modern Android Development 권장 사항에 부합합니다.

다만, 가독성을 위해 uiState에 명시적 타입을 추가하는 것도 고려해 보실 수 있습니다:

val uiState: StateFlow<PartyDetailUiState> = _uiState.asStateFlow()
app/src/main/java/com/poti/android/presentation/party/detail/DummyPartyDetailData.kt (2)

54-75: 중복된 userId 확인이 필요합니다.

participants 리스트에서 userId = 1을 가진 Participant가 두 개 존재합니다 (Lines 55-61, 62-68). 한 사용자가 여러 멤버를 선택한 경우를 표현하려는 의도라면, selectedMembers 리스트에 여러 멤버를 담는 것이 더 적절할 수 있습니다.

Participant(
    userId = 1,
    nickname = "참여자1",
    profileImage = null,
    rating = 4.5,
    selectedMembers = listOf("원영", "이서"),  // 한 Participant에 여러 멤버
),

더미 데이터라 큰 문제는 아니지만, 실제 데이터 구조와 일치하는지 확인해 주세요.


10-79: Preview/테스트용 더미 데이터 구성이 잘 되어 있습니다.

PartyDetail 모델의 모든 필드를 포함하여 다양한 시나리오를 테스트할 수 있도록 구성되어 있습니다. 프리뷰 작성 시 유용하게 활용될 것입니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyParticipantsInfo.kt (1)

48-68: 참여자 목록 렌더링 로직이 잘 구현되어 있습니다.

중첩된 forEach를 사용하여 각 참여자의 selectedMembers를 개별 행으로 표시하는 로직이 명확합니다. Column 내에서 사용되므로 LazyColumnkey 파라미터 이슈는 해당되지 않습니다.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailContent.kt (1)

81-100: 배송 옵션 FlowRow 구현이 잘 되어 있습니다.

FlowRowIntrinsicSize.Min을 활용하여 동적인 레이아웃을 구현하셨고, forEachIndexed를 통해 마지막 항목 이후에는 구분선을 표시하지 않는 로직도 적절합니다. toMoneyString() 확장 함수를 활용한 금액 포맷팅도 좋습니다.

app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt (2)

8-10: MVI 패턴의 UiState가 올바르게 구현되어 있습니다.

data classval 프로퍼티를 사용하여 불변 상태를 보장하고, ApiState<PartyDetail>을 통해 Loading/Success/Error 상태를 표현할 수 있는 구조입니다. Modern Android Development의 상태 관리 권장 사항을 잘 따르고 있습니다.

향후 기능 확장 시 추가적인 UI 상태(예: isRefreshing, errorMessage 등)가 필요하다면 이 data class에 필드를 추가하시면 됩니다.


1-2: 파일명 규칙 예외 처리가 적용되어 있습니다.

@file:Suppress("ktlint:standard:filename")으로 파일명과 클래스명 불일치 경고를 억제하고 있습니다. Contracts.kt에 여러 계약(UiState, Intent, SideEffect 등)을 함께 정의하려는 의도로 보입니다.

향후 PartyDetailIntentPartyDetailSideEffect 등이 추가될 예정이라면 현재 구조가 적절합니다. 단일 클래스만 유지된다면 파일명을 PartyDetailUiState.kt로 변경하는 것도 고려해 주세요.

app/src/main/java/com/poti/android/presentation/party/detail/component/PartyDetailHeaderInfo.kt (1)

42-48: 제목 텍스트에 maxLines 설정이 필요해 보여요.

minLines = 2만 설정되어 있고 maxLines가 없어서, 긴 제목의 경우 텍스트가 무한정 확장될 수 있어요. 기존 리뷰 코멘트에서도 언급된 것처럼, 상세 페이지에서는 타이틀을 다 보여줘야 할 것 같지만, 레이아웃 안정성을 위해 적절한 maxLines 값을 고려해 주세요.

app/src/main/java/com/poti/android/domain/model/party/PartyDetail.kt (1)

7-24: 도메인 모델이 잘 정의되어 있어요!

순수 Kotlin으로 작성되어 Android 의존성이 없고, 모든 프로퍼티가 val로 선언되어 불변성을 보장하고 있어요. Clean Architecture의 Domain 순수성 원칙을 잘 준수하고 있네요. 한글 주석도 명확해서 가독성이 좋습니다.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (1)

80-92: 이미지 페이저 구현이 적절해요.

HorizontalPagerAsyncImage를 활용한 이미지 갤러리 구현이 잘 되어 있어요. aspectRatio로 일관된 비율을 유지하고 있네요.

app/src/main/res/values/strings.xml (1)

25-37: PartyDetail 관련 문자열 리소스가 잘 정리되어 있어요!

섹션별 주석으로 구분되어 있고, 포맷 문자열도 positional argument(%1$s, %2$d)를 적절히 사용하고 있어요. 하드코딩 대신 리소스를 활용하는 좋은 패턴이에요.

app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (1)

36-41: 문제없습니다. onNavigateToProfile 콜백 시그니처가 일치해요.

navController::navigateToProfilefun NavController.navigateToProfile(userId: Long)의 메서드 참조이며, 이것이 콜백으로 전달될 때 (Long) -> Unit 타입으로 정확히 변환됩니다. 이는 PartyDetailRouteonNavigateToProfile: (Long) -> Unit 파라미터 타입과 완벽하게 일치합니다.

Likely an incorrect or invalid review comment.

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

import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
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

hiltViewModel import 경로가 deprecated되었어요.

androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel은 deprecated된 경로예요. Modern Android Development 권장사항에 따라 androidx.hilt.navigation.compose.hiltViewModel을 사용해 주세요.

🔧 제안하는 수정 사항
-import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import androidx.hilt.navigation.compose.hiltViewModel
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.hilt.navigation.compose.hiltViewModel
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt`
at line 20, The import for hiltViewModel is using the deprecated package;
replace the import statement
"androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel" with
"androidx.hilt.navigation.compose.hiltViewModel" so all usages of
hiltViewModel() in PartyDetailScreen.kt reference the modern package; ensure
there are no other deprecated hilt lifecycle imports in the file and run a quick
build to confirm the symbol resolves.

<string name="party_detail_participants">참여자</string>
<string name="party_detail_review_count">%d개의 평가</string>
<string name="party_detail_participants_count">%1$d/%2$d</string>
<string name="party_detail_announcement">안내 사항\n어쩌구 저쩌구\n어쩌구 저쩌구\n어쩌구 저쩌구\n</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

안내 사항 문자열이 플레이스홀더 텍스트로 되어 있어요.

party_detail_announcement가 "어쩌구 저쩌구"로 되어 있는데, 실제 안내 문구로 교체가 필요해 보여요. 기획 확정 후 업데이트해 주세요.

🤖 Prompt for AI Agents
In `@app/src/main/res/values/strings.xml` at line 36, The string resource
party_detail_announcement currently contains placeholder text ("어쩌구 저쩌구");
replace its value in strings.xml with the finalized announcement text from
product/PM, preserving any intended line breaks or formatting (use \n for
newlines or string resources with <xliff:g> if needed) and ensure the string
name party_detail_announcement remains unchanged so callers referencing it are
unaffected.

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.

고생하셨슴니당!!!!!!!!!


data class DeliveryOption(
val deliveryId: Long,
val deliveryId: Long, // 바송 방식 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: 바송숑

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

♻️ Duplicate comments (1)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (1)

22-22: hiltViewModel import 경로가 deprecated되었어요.

androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel은 deprecated된 경로예요. androidx.hilt.navigation.compose.hiltViewModel을 사용해 주세요.

🔧 제안하는 수정 사항
-import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import androidx.hilt.navigation.compose.hiltViewModel
🧹 Nitpick comments (3)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (2)

51-59: LaunchedEffect 키 사용을 개선해 주세요.

viewModel.sideEffect를 키로 사용하면 Flow 참조가 변경되지 않아 한 번만 실행되긴 하지만, 의도가 명확하지 않아요. Unit을 키로 사용하는 것이 더 직관적이에요. 이렇게 하면 컴포저블이 처음 컴포지션될 때 한 번 실행되고, 내부에서 Flow를 수집하는 의도가 명확해져요.

♻️ 제안하는 수정 사항
-    LaunchedEffect(viewModel.sideEffect) {
-        viewModel.sideEffect.collect { effect ->
+    LaunchedEffect(Unit) {
+        viewModel.sideEffect.collect { effect ->
             when (effect) {
                 PartyDetailEffect.NavigateBack -> onPopBackStack()
                 PartyDetailEffect.NavigateToJoin -> onNavigateToJoin()
                 is PartyDetailEffect.NavigateToProfile -> onNavigateToProfile(effect.userId)
             }
         }
     }

117-121: onLikeClick 핸들러가 비어 있어요.

좋아요 기능이 아직 구현되지 않은 것 같아요. 추후 구현 예정이시라면 TODO 주석을 남겨주시면 좋을 것 같아요. 필요하시면 PartyDetailIntentOnLikeClick을 추가하는 것을 도와드릴 수 있어요.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (1)

36-40: 서버 연결 시 에러 처리 추가가 필요해요.

launchScopeonError 콜백이 제공되지 않았어요. 현재는 더미 데이터를 사용하지만, 서버 연결 시 API 호출 실패에 대한 에러 처리가 필요해요. BaseViewModellaunchScopeonError 파라미터를 지원하므로 활용해 주세요.

♻️ 제안하는 수정 사항 (서버 연결 시)
     private fun loadPartyDetail() = launchScope(
+        onError = { error ->
+            updateState { copy(partyDetail = ApiState.Error(error)) }
+        }
+    ) {
         updateState { copy(partyDetail = ApiState.Loading) }
-        // TODO: [지현] 나중에 서버 연결
-        updateState { copy(partyDetail = ApiState.Success(dummyPartyDetail)) }
+        // TODO: [지현] 서버 API 호출
+        val result = repository.getPartyDetail(partyId)
+        updateState { copy(partyDetail = ApiState.Success(result)) }
     }
📜 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 ab907ca and c1247ac.

📒 Files selected for processing (5)
  • app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/poti/android/domain/model/delivery/DeliveryOption.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/presentation/party/detail/PartyDetailViewModel.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt
  • app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt
🧬 Code graph analysis (1)
app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (1)
app/src/main/java/com/poti/android/core/base/BaseViewModel.kt (3)
  • sendEffect (34-38)
  • launchScope (42-54)
  • updateState (30-32)
🔇 Additional comments (8)
app/src/main/java/com/poti/android/presentation/party/detail/navigation/PartyDetailNavigation.kt (2)

15-21: Route 정의가 깔끔하게 구현되었어요!

PartyDetailRoute.DetailpartyId 파라미터를 추가하여 타입 안전한 네비게이션을 구현하셨네요. @Serializable 어노테이션을 사용한 것도 Modern Android Development의 Navigation Compose 권장 사항을 잘 따르고 있어요.


31-47: 네비게이션 그래프 구성이 잘 되어 있어요!

navController를 파라미터로 받아서 콜백을 메서드 레퍼런스로 전달하는 패턴이 깔끔해요. MVI 아키텍처의 단방향 데이터 흐름을 잘 준수하고 있어요.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailScreen.kt (2)

103-115: HorizontalPager와 이미지 로딩 구현이 좋아요!

rememberPagerState를 사용하고 Coil의 AsyncImage로 이미지를 로드하는 패턴이 Modern Android Development 권장 사항을 잘 따르고 있어요. ContentScale.Crop으로 이미지 비율도 잘 처리했네요.


75-96: PartyDetailScreen 구조가 잘 설계되었어요!

Modifier를 첫 번째 선택적 파라미터로 배치하고, 콜백들을 통해 상태 호이스팅을 적절히 구현했어요. Scaffold를 활용한 topBarbottomBar 구성도 깔끔하네요.

app/src/main/java/com/poti/android/presentation/party/detail/PartyDetailViewModel.kt (2)

14-25: ViewModel 구조가 MVI 패턴을 잘 따르고 있어요!

@HiltViewModel@Inject가 올바르게 적용되었고, SavedStateHandle을 통한 route 인자 추출도 깔끔해요. BaseViewModel을 상속받아 상태 관리와 사이드 이펙트 처리가 일관되게 구현되었네요.


27-34: Intent 처리 로직이 깔끔해요!

모든 Intent가 when 표현식으로 명확하게 처리되고, 네비게이션 같은 일회성 이벤트는 sendEffect를 통해 분리되어 있어요. MVI의 단방향 데이터 흐름을 잘 준수하고 있네요.

app/src/main/java/com/poti/android/presentation/party/detail/model/Contracts.kt (2)

10-12: UiState 정의가 MVI 패턴을 잘 따르고 있어요!

data classval 프로퍼티로 불변성을 보장하고 있고, ApiState를 래핑하여 로딩/성공/에러 상태를 표현할 수 있어요. 코딩 가이드라인의 "불변 상태(Immutable State)" 원칙을 잘 준수했네요.


14-30: Intent와 Effect 분리가 잘 되어 있어요!

sealed interface를 사용해 Intent와 Effect를 명확히 정의했어요. 특히 네비게이션 같은 일회성 이벤트를 PartyDetailEffect로 분리하여 화면 회전 시 재발생하지 않도록 처리한 점이 좋아요. MVI 아키텍처의 Side Effect 분리 원칙을 잘 따르고 있네요.

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

Comment on lines +61 to +72
when (val state = uiState.partyDetail) {
is ApiState.Success -> {
PartyDetailScreen(
partyDetail = state.data,
onBackClick = { viewModel.processIntent(PartyDetailIntent.OnBackClick) },
onJoinClick = { viewModel.processIntent(PartyDetailIntent.OnJoinClick) },
onUploaderClick = { viewModel.processIntent(PartyDetailIntent.OnUploaderClick(it)) },
modifier = modifier,
)
}
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.

⚠️ Potential issue | 🟠 Major

Loading과 Error 상태 처리가 누락되었어요.

현재 ApiState.Success만 처리하고 있어서 로딩 중이거나 에러 발생 시 빈 화면이 표시돼요. 사용자 경험을 위해 로딩 인디케이터와 에러 UI를 추가해 주세요.

🔧 제안하는 수정 사항
     when (val state = uiState.partyDetail) {
+        is ApiState.Loading -> {
+            // TODO: 로딩 UI 추가 (예: Box with CircularProgressIndicator)
+        }
+        is ApiState.Error -> {
+            // TODO: 에러 UI 추가 (예: 재시도 버튼 포함)
+        }
         is ApiState.Success -> {
             PartyDetailScreen(
                 partyDetail = state.data,
                 onBackClick = { viewModel.processIntent(PartyDetailIntent.OnBackClick) },
                 onJoinClick = { viewModel.processIntent(PartyDetailIntent.OnJoinClick) },
                 onUploaderClick = { viewModel.processIntent(PartyDetailIntent.OnUploaderClick(it)) },
                 modifier = modifier,
             )
         }
-        else -> {}
     }

Copy link
Copy Markdown
Contributor

@sonyerim sonyerim left a comment

Choose a reason for hiding this comment

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

상세 뷰 이쁘게 나왔네용 밤티 X

@jyvnee jyvnee merged commit ad1f83f into develop Jan 15, 2026
2 checks passed
@github-project-automation github-project-automation bot moved this from To-do to Done in POTI-ANDROID Jan 15, 2026
@jyvnee jyvnee deleted the feat/#35-party-detail-ui branch January 15, 2026 15:41
cmj7271 added a commit that referenced this pull request Jan 16, 2026
* [feat/36] participant-overview implement

* [feat/#36] participant-detail implement

* [feat/#36] participant dropdown implement

* [fix/#36] apply ktlint

* [Refactor/#41] 필드 수정 (#42)

* [Feat/#41] ShortTextField에 trailingIcon 추가

* [Chore/#41] kdoc 주석 추가

* [Refactor/#41] FieldErrorMessage 삭제 및 필드에 PotiErrorMessage 적용

* [Refactor/#41] trailingIcon 기본값 null로 변경

* [Refactor/#41] 에러메시지 노출 조건 isNotEmpty -> isNotBlank 변경

* [fix/#36] 코드리뷰 반영

* [fix/#36] apply ktlint

* [Feat/#35] 분철팟 상세 뷰 UI 구현 (#39)

* [feat/#35] 헤더 컴포넌트 구현

* [feat/#35] 분철팟 상세 뷰 UI 구현

* [feat/#35] 분철팟 상세 뷰 UI 구현

* [feat/#35] 이미지 비율 수정

* [feat/#35] 내비게이션 연결

* [feat/#35] 패키지 분리

* [feat/#35] 내비게이션 파라미터 수정

* [feat/#35] 코리 반영

* [feat/#35] 내비 수정

* [feat/#35] 주석 수정

* [feat/#35] 뷰모델 연결

* [feat/#32] card-history implement

* [feat/#32] card-history implement

* [fix/#32] 코리 반영

* [Feat/#27] 탐색 컴포넌트 구현 (#37)

* [Feat/#27] GoodsSmallCard

* [Feat/#27] GoodsLargeCard

* [Feat/#27] PostsCard

* [Refactor/#27] 디자인 수정사항 반영

* [Refactor/#27] string 추출

* [Refactor/#27] 코드 리뷰 반영

* [Feat/#31] 등록 뷰 컴포넌트 (#40)

* [Feat/#31] Edit Option Price 컴포넌트 구현

* [Feat/#31] 이미지 관련 버튼 컴포넌트 구현

* [Feat/#31] 힌트 팝업 컴포넌트 구현

* [Chore/#31] klint 적용

* [Rename/#31] Hint.kt -> HintTooltip.kt

* [Refactor/#31] Edit Option Price 레이아웃 리팩토링 및 스트링 리소스 추출

* [Fix/#31] 툴팁 위치 계산식 수정
- 중앙 배치 위해 xOffset에 툴팁 폭 포함

* [Chore/#31] 미사용 코드 삭제

* [Chore/#31] klint 적용

* [Feat/#31] 갤러리 이미지 선택 기능 추가

* [Chore/#31] 오탈자 수정

* [Refactor/#31] 이미지 피커 개선
- Row -> LazyRow 변경해 contentPadding 적용
- distinct 추가해 중복 이미지 방지
- 이미지만 선택 가능하도록 조건 추가

* [Refactor/#31] Edit Option Price 개선
- 최대 길이 제한
- 특수문자 입력 제한
- take 대신 반복문 사용해 불필요한 string 객체 생성 방지
- TransformedText 콤마 추가 로직 단순화

* [Chore/#31] klint 적용

* [Refactor/#31] core 확장함수 적용

* [Fix/#31] strings 충돌 이슈 해결

* [fix/#36] 코드 리뷰 반영

* [fix/#36] apply ktlint

* [feat/36] participant-overview implement

* [fix/#36] fix conflict

* [feat/#36] participant dropdown implement

* [fix/#36] apply ktlint

* [fix/#36] resolve conflict

* [fix/#36] apply ktlint

* [fix/#36] 코드 리뷰 반영

* [fix/#36] apply ktlint

* [fix/#36] 코드 리뷰 반영

* [fix/#36] 코드리뷰 반영

* [fix/#36] 코드 리뷰 반영

* [fix/#36] apply ktlint

* [fix/#36] 코드리뷰 반영

* [fix/#36] apply ktlint

---------

Co-authored-by: dodo <108921606+doyeon0307@users.noreply.github.com>
Co-authored-by: 이지현 <95419701+jyvnee@users.noreply.github.com>
Co-authored-by: Yerim Son <119676236+sonyerim@users.noreply.github.com>
jyvnee added a commit that referenced this pull request Jan 16, 2026
* [feat/36] participant-overview implement

* [feat/#36] participant-detail implement

* [feat/#36] participant dropdown implement

* [fix/#36] apply ktlint

* [Refactor/#41] 필드 수정 (#42)

* [Feat/#41] ShortTextField에 trailingIcon 추가

* [Chore/#41] kdoc 주석 추가

* [Refactor/#41] FieldErrorMessage 삭제 및 필드에 PotiErrorMessage 적용

* [Refactor/#41] trailingIcon 기본값 null로 변경

* [Refactor/#41] 에러메시지 노출 조건 isNotEmpty -> isNotBlank 변경

* [fix/#36] 코드리뷰 반영

* [fix/#36] apply ktlint

* [Feat/#35] 분철팟 상세 뷰 UI 구현 (#39)

* [feat/#35] 헤더 컴포넌트 구현

* [feat/#35] 분철팟 상세 뷰 UI 구현

* [feat/#35] 분철팟 상세 뷰 UI 구현

* [feat/#35] 이미지 비율 수정

* [feat/#35] 내비게이션 연결

* [feat/#35] 패키지 분리

* [feat/#35] 내비게이션 파라미터 수정

* [feat/#35] 코리 반영

* [feat/#35] 내비 수정

* [feat/#35] 주석 수정

* [feat/#35] 뷰모델 연결

* [feat/#32] card-history implement

* [feat/#32] card-history implement

* [fix/#32] 코리 반영

* [Feat/#27] 탐색 컴포넌트 구현 (#37)

* [Feat/#27] GoodsSmallCard

* [Feat/#27] GoodsLargeCard

* [Feat/#27] PostsCard

* [Refactor/#27] 디자인 수정사항 반영

* [Refactor/#27] string 추출

* [Refactor/#27] 코드 리뷰 반영

* [Feat/#31] 등록 뷰 컴포넌트 (#40)

* [Feat/#31] Edit Option Price 컴포넌트 구현

* [Feat/#31] 이미지 관련 버튼 컴포넌트 구현

* [Feat/#31] 힌트 팝업 컴포넌트 구현

* [Chore/#31] klint 적용

* [Rename/#31] Hint.kt -> HintTooltip.kt

* [Refactor/#31] Edit Option Price 레이아웃 리팩토링 및 스트링 리소스 추출

* [Fix/#31] 툴팁 위치 계산식 수정
- 중앙 배치 위해 xOffset에 툴팁 폭 포함

* [Chore/#31] 미사용 코드 삭제

* [Chore/#31] klint 적용

* [Feat/#31] 갤러리 이미지 선택 기능 추가

* [Chore/#31] 오탈자 수정

* [Refactor/#31] 이미지 피커 개선
- Row -> LazyRow 변경해 contentPadding 적용
- distinct 추가해 중복 이미지 방지
- 이미지만 선택 가능하도록 조건 추가

* [Refactor/#31] Edit Option Price 개선
- 최대 길이 제한
- 특수문자 입력 제한
- take 대신 반복문 사용해 불필요한 string 객체 생성 방지
- TransformedText 콤마 추가 로직 단순화

* [Chore/#31] klint 적용

* [Refactor/#31] core 확장함수 적용

* [Fix/#31] strings 충돌 이슈 해결

* [fix/#36] 코드 리뷰 반영

* [fix/#36] apply ktlint

* [feat/36] participant-overview implement

* [fix/#36] fix conflict

* [feat/#36] participant dropdown implement

* [fix/#36] apply ktlint

* [fix/#36] resolve conflict

* [fix/#36] apply ktlint

* [fix/#36] 코드 리뷰 반영

* [fix/#36] apply ktlint

* [fix/#36] 코드 리뷰 반영

* [fix/#36] 코드리뷰 반영

* [fix/#36] 코드 리뷰 반영

* [fix/#36] apply ktlint

* [fix/#36] 코드리뷰 반영

* [fix/#36] apply ktlint

---------

Co-authored-by: dodo <108921606+doyeon0307@users.noreply.github.com>
Co-authored-by: 이지현 <95419701+jyvnee@users.noreply.github.com>
Co-authored-by: Yerim Son <119676236+sonyerim@users.noreply.github.com>
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 구현

3 participants