Skip to content

Conversation

@boiledeggg
Copy link
Member

@boiledeggg boiledeggg commented Nov 29, 2025

Related issue 🛠

Work Description ✏️

  1. 컴포지션 단계에서 상태 초기화하는 책임을 뷰모델로 이전.

    • 기존의 LifecycleResumeEffect를 활용해 UI에서 물품 정보를 초기화하던 로직을 제거
    • ViewModel의 init 블럭 내부에서 초기 상태를 불러오도록 수정 (테스트 코드와의 tradeoff)
  2. 좋아요를 상태 관리에서 이벤트 관리 방식으로 수정

    • StateFlow를 활용한 좋아요 상태 변경 감지 방식 제거
    • UiState 자체에서 좋아요 상태를 관리하고, 상태가 변경되면 SharedFlow로 이벤트를 전달하도록 수정
    • 중복 이벤트 실행을 방지
  3. 좋아요 로직 수정

    • 좋아요 값을 계속 반전시켜서 매개변수로 전달하는 이상했던 과거 로직을 청산
  4. 기타 코드 리팩터링

    • 상태 업데이트 로직 수정
    • 불필요한 예외처리 제거
    • 함수 책임 분리 강화

Uncompleted Tasks 😅

  • 테스트 코드 작성하려다가 라이브러리를 몽땅 채팅 DB 관련 브랜치에서 추가했다는걸 깨닫고 포기..

To Reviewers 📢

  • 이제 슬금슬금 뷰 하나씩 리팩해보려고 합니다! 많관부

- 초기 상태를 뷰모델의 init 블록으로 초기화
- 좋아요를 이벤트 형식으로 수집하고, 중복 서버 통신을 방지
- 좋아요 상태 및 좋아요 수의 갱신 로직을 합침
- 좋아요 유즈케이스를 더욱 직관적으로 수정
- 상태 업데이트 로직 안정화
- 불필요한 예외처리 제거
- 함수 책임 분리 강화
@coderabbitai
Copy link

coderabbitai bot commented Nov 29, 2025

Walkthrough

ProductDetail 기능 리팩토링으로 새로운 SetInterestUseCase 도입 및 관심 상품 처리 흐름 개선. ProductDetailScreen에서 라이프사이클 기반 상태 관리 제거 및 콜백 기반 위임으로 단순화. ProductDetailViewModel에서 관심 상품 처리를 흐름 기반으로 재구성하여 디바운싱 및 중복 제거 로직 적용. MainScreen 네비게이션 그래프 패딩 조정.

Changes

코호트 / 파일(s) 변경 요약
새로운 관심 상품 유스케이스
domain/interest/src/main/java/com/napzak/market/interest/usecase/SetInterestUseCase.kt
주입 가능한 새로운 유스케이스 클래스 추가. InterestProductRepository에 의존하며, 관심 상품 설정/해제를 라우팅하는 suspend operator 함수 제공
ProductDetail 화면 리팩토링
feature/detail/src/main/java/com/napzak/market/detail/ProductDetailScreen.kt
라이프사이클 기반 데이터 페치 제거. systemBarsPadding 추가. 로컬 isInterested 상태 제거 및 Boolean 콜백 기반 좋아요 액션 위임으로 변경. 삭제 다이얼로그 상태 관리 단순화
ProductDetail 뷰모델 리팩토링
feature/detail/src/main/java/com/napzak/market/detail/ProductDetailViewModel.kt
SetInterestProductUseCase → SetInterestUseCase로 변경. 관심 상품 처리를 MutableSharedFlow 기반으로 재구성하여 디바운싱 및 distinctUntilChanged 적용. 제품 상세 로딩을 init 경로로 이동. 부작용 처리 명시화 (NavigateUp, ShowToast)
네비게이션 패딩 조정
feature/main/src/main/java/com/napzak/market/main/MainScreen.kt
MainNavHost에서 제품 상세 네비게이션 그래프 modifier에서 innerPadding 제거

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • ProductDetailViewModel: Flow 기반 비동기 관심 상품 처리, 디바운싱 로직, 부작용 방출 메커니즘 검증 필요
  • SetInterestUseCase와 ViewModel 통합: UseCase 호출 경로 및 데이터 흐름 일관성 검증
  • ProductDetailScreen 상태 관리: 라이프사이클 제거에 따른 UI 동기화 및 isInterested 파생 로직 검증
  • 부작용 처리 일관성: 여러 작업(제품 상세 로딩, 관심 설정, 거래 상태 변경, 삭제)에 걸친 새로운 부작용 방출 패턴 검증

Possibly related PRs

Suggested reviewers

  • yeonjeen
  • jm991014
  • chrin05
  • jangsjw

Poem

🐰 ProductDetail 리팩토링으로,
흐름 기반 관심사 춤춘다네!
라이프사이클 거둬내고,
깔끔한 콜백으로 춤을 춘다. ✨
디바운싱된 좋아요, 너무 멋져!

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 리팩토링 작업의 주요 대상인 '물품 상세 화면 뷰모델 리팩토링'을 명확하게 나타내며, 변경사항의 핵심을 잘 요약하고 있습니다.
Linked Issues check ✅ Passed 코드 변경이 이슈 #374의 ProductDetail 리팩토링 목표와 일치합니다. ViewModel 리팩토링, 상태 관리 개선, 좋아요 로직 정리 등이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed SetInterestUseCase 추가는 리팩토링 목표 범위 내 필요한 변경이며, MainScreen 패딩 제거는 ProductDetail 화면 연계 조정으로 관련 범위 내입니다.
Description check ✅ Passed PR 설명이 template의 모든 필수 섹션을 포함하고 있으며, 작업 내용과 변경 사항이 명확하게 설명되어 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#374-product-detail

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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

🧹 Nitpick comments (1)
feature/detail/src/main/java/com/napzak/market/detail/ProductDetailScreen.kt (1)

143-151: 콜백 파라미터가 무시되고 있습니다.

ProductDetailBottomBarLikeButton은 이미 클릭 시 onLikeButtonClick(!isLiked)를 호출하여 토글된 값을 전달합니다. 하지만 여기서 전달하는 람다는 해당 파라미터를 무시하고 !isInterested를 다시 계산합니다.

isLiked == isInterested이므로 동작은 정확하지만, 코드 가독성을 위해 파라미터를 직접 전달하는 것이 좋습니다.

 ProductDetailBottomBar(
     isLiked = isInterested,
     onChatButtonClick = { onChatButtonClick(productId) },
-    onLikeButtonClick = { onLikeButtonClick(!isInterested) },
+    onLikeButtonClick = onLikeButtonClick,
 )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5882f35 and e37c6a2.

📒 Files selected for processing (4)
  • domain/interest/src/main/java/com/napzak/market/interest/usecase/SetInterestUseCase.kt (1 hunks)
  • feature/detail/src/main/java/com/napzak/market/detail/ProductDetailScreen.kt (7 hunks)
  • feature/detail/src/main/java/com/napzak/market/detail/ProductDetailViewModel.kt (3 hunks)
  • feature/main/src/main/java/com/napzak/market/main/MainScreen.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-02T08:42:53.237Z
Learnt from: boiledeggg
Repo: napzakmarket/Napzak-Android PR: 367
File: feature/chat/src/main/java/com/napzak/market/chat/chatroom/ChatRoomScreen.kt:87-88
Timestamp: 2025-11-02T08:42:53.237Z
Learning: In the Napzak-Android project, when ChatRoomViewModel exposes chatMessageFlow as StateFlow<Flow<PagingData<ReceiveMessage<*>>>>, the correct pattern in ChatRoomScreen is to first collect it with collectAsStateWithLifecycle() to get the inner Flow<PagingData<...>>, then call collectAsLazyPagingItems() on that result. This nested StateFlow structure is used because the paging flow depends on dynamic state (e.g., current room ID).

Applied to files:

  • feature/detail/src/main/java/com/napzak/market/detail/ProductDetailViewModel.kt
  • feature/detail/src/main/java/com/napzak/market/detail/ProductDetailScreen.kt
📚 Learning: 2025-08-12T07:18:00.700Z
Learnt from: jm991014
Repo: napzakmarket/Napzak-Android PR: 319
File: feature/detail/src/main/java/com/napzak/market/detail/component/bottomsheet/ProductBottomSheet.kt:32-34
Timestamp: 2025-08-12T07:18:00.700Z
Learning: `ProductBottomSheet`과 같은 `DragHandleBottomSheet` 기반 컴포넌트에서는 `navigationBarsPadding()`을 사용하지 않고 고정 bottom padding (28.dp)을 사용한다. ModalBottomSheet는 시스템 내비게이션 바 위에 플로팅되므로 navigationBarsPadding이 불필요하다.

Applied to files:

  • feature/detail/src/main/java/com/napzak/market/detail/ProductDetailScreen.kt
⏰ 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: build
🔇 Additional comments (7)
feature/main/src/main/java/com/napzak/market/main/MainScreen.kt (1)

307-307: LGTM!

Product detail 화면의 패딩 책임을 네비게이션 그래프 레벨에서 상세 화면 자체로 이동한 것은 적절합니다. ProductDetailScreen.kt에서 systemBarsPadding()을 직접 적용하도록 변경되어 일관성 있게 동작합니다.

feature/detail/src/main/java/com/napzak/market/detail/ProductDetailScreen.kt (1)

154-156: LGTM!

네비게이션 그래프에서 패딩을 제거하고 systemBarsPadding()을 Scaffold에 직접 적용한 것은 적절한 변경입니다. 상세 화면이 자체적으로 시스템 바 인셋을 처리합니다.

feature/detail/src/main/java/com/napzak/market/detail/ProductDetailViewModel.kt (4)

93-100: LGTM! debounce와 distinctUntilChanged를 활용한 좋은 패턴입니다.

_interestFlow에 debounce와 distinctUntilChanged를 적용하여 불필요한 API 호출을 방지하고, 낙관적 UI 업데이트와 실제 서버 동기화를 분리한 설계가 잘 되어 있습니다.


161-168: 삭제 후 사이드 이펙트 순서 확인 필요

NavigateUpShowToast보다 먼저 전송됩니다. 화면이 먼저 닫히면 토스트가 표시되지 않거나, 이전 화면에서 표시될 수 있습니다. 의도한 동작인지 확인해 주세요.

순서를 바꾸거나 토스트 표시 후 딜레이를 추가하는 것을 고려해 보세요.


102-105: LGTM!

updateIsInterested가 UI 상태를 즉시 업데이트하고 사이드 이펙트를 트리거하는 명확한 책임 분리가 잘 되어 있습니다.


65-71: LGTM!

init 블록에서 초기 데이터 로드를 처리하여 UI 측의 LifecycleResumeEffect 기반 초기화 로직을 제거한 것은 ViewModel의 책임에 맞는 적절한 변경입니다. productId가 null인 경우의 에러 처리도 잘 되어 있습니다.

domain/interest/src/main/java/com/napzak/market/interest/usecase/SetInterestUseCase.kt (1)

6-13: I'll verify the original review comment by checking whether the old SetInterestProductUseCase has been properly removed or is no longer used in the codebase.
<function_calls>

#!/bin/bash

Search for all references to SetInterestProductUseCase

echo "=== Searching for SetInterestProductUseCase references ==="
rg -n "SetInterestProductUseCase" --type kotlin -A 2 -B 2

echo -e "\n=== Checking if SetInterestProductUseCase file exists ==="
fd -t f "SetInterestProductUseCase" --type kotlin

echo -e "\n=== Searching in all file types ==="
rg -n "SetInterestProductUseCase" -A 2 -B 2 2>/dev/null | head -100


</function_calls>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] ProductDetail 리팩토링

2 participants