Skip to content

[FEAT/#103] 챌린지 루틴 목록 조회 API 연결 완료 #119

Closed
hyeminililo wants to merge 83 commits intodevelopfrom
feat/#103-challenge-routine-api
Closed

[FEAT/#103] 챌린지 루틴 목록 조회 API 연결 완료 #119
hyeminililo wants to merge 83 commits intodevelopfrom
feat/#103-challenge-routine-api

Conversation

@hyeminililo
Copy link
Copy Markdown
Contributor

@hyeminililo hyeminililo commented Jan 20, 2026

Related issue 🛠

Work Description ✏️

  • 루틴 목록 조회 API 연결 완료

Screenshot 📸

Uncompleted Tasks 😅

N/A

To Reviewers 📢

항상 고맙고 감사합니다

Summary by CodeRabbit

  • 새로운 기능

    • 홈케어 루틴 데이터 조회 및 선택 기능 추가
    • 루틴 선택 후 미션 화면으로 이동하는 내비게이션(단계별 흐름) 추가
  • 개선 사항

    • 챌린지 시작 → 루틴 선택 → 미션으로의 전환 흐름 개선(더 부드러운 이동)
    • 로딩 표시 및 화면 여백(padding) 처리 개선
  • 제거

    • 챌린지 미션 진행 화면(진행도 화면) 삭제 — 관련 UI가 제거되었습니다.

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

hyeminililo and others added 30 commits January 20, 2026 02:32
- Int가 아닌 Long으로 변경
- 가존 Model에 isSelected를 추가하고 이 부분을 State에서 관리할 수 있도록 변경
- UiState(Loading/Failure/Success) 수집 후 상태에 따라 화면 분기 처리
- Success 시 ChallengeMissionSelectedScreen에 missions/hasSelected 등 UI 상태 전달
- 뒤로가기/닫기/미션 클릭/추가하기 이벤트를 ViewModel 메서드로 연결(onBackClick, onCloseClick, onTodoMissionClick, onAddTodoClick)
- Preview에서 미션 클릭 시 isSelected 토글 및 버튼 enabled 동작까지 확인 가능하도록 구성
- ChallengeMissionSelected 화면에 ViewModel을 적용하고 Route에서 UiState를 수집하도록 구조 정리
- UI에서 직접 상태를 관리하던 로직을 제거하고, Screen은 전달받은 상태와 이벤트만 처리하도록 역할 분리
- 미션 클릭, 뒤로가기, 닫기, 추가하기 액션을 ViewModel 이벤트로 위임하여 단방향 데이터 흐름 적용
- Preview에서는 로컬 상태를 사용해 선택 토글 및 버튼 활성화 동작을 확인 가능하도록 유지
- 컨벤션에 맞춰 줄바꿈 정리
- ktLint 실행
- ChallengeRoutine 모델 기본값 부여
- UI에 Model과 겹치는 부분 삭제 및 변경
- 챌린지 뷰모델 선ㅇ너
- 챌린지 뷰모델을 스크린에 적용
# Conflicts:
#	app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt
#	app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt
#	app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt
@hyeminililo hyeminililo self-assigned this Jan 20, 2026
@hyeminililo hyeminililo requested a review from a team as a code owner January 20, 2026 18:02
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 20, 2026

Walkthrough

홈케어 루틴 조회 API 연동을 위해 서비스/데이터소스/레포지토리와 DI 바인딩을 추가하고, 프레젠테이션에서 루트 및 모델을 ChallengeStart/ChallengeRoutine/ChallengeMission 구조로 재배치하며 side-effect 기반 네비게이션을 도입했습니다.

Changes

코호트 / 파일(s) 변경 요약
DI 모듈
\app/src/main/java/com/cherrish/android/data/di/...\
DataSourceModule, RepositoryModule, ServiceModuleChallenge 관련 바인딩/제공자 추가 (ChallengeDataSource, ChallengeRepository, ChallengeService)
원격 API · 데이터소스
\app/src/main/java/com/cherrish/android/data/remote/service/ChallengeService.kt`, `.../datasource/ChallengeDataSource.kt`, `.../datasourceimpl/ChallengeDataSourceImpl.kt``
Retrofit API 엔드포인트 GET api/challenges/homecare-routines 정의, 데이터소스 인터페이스 및 구현 추가
DTO · 모델 (데이터층)
\app/src/main/java/com/cherrish/android/data/remote/dto/...ChallengesResponseDto.kt`, `.../request/ChallengeRequestDto.kt`, `.../model/ChallengeHomecareRoutinesResponseModel.kt``
응답/요청 DTO 다수 추가 및 ChallengeHomecareRoutinesResponseModeltoModel() 확장함수 추가
레포지토리
\app/src/main/java/com/cherrish/android/data/repository/ChallengeRepository.kt`, `.../repositoryimpl/ChallengeRepositoryImpl.kt``
ChallengeRepository 인터페이스 및 구현 추가, 데이터 호출을 suspendRunCatching으로 래핑하고 DTO→Model 매핑 수행
프레젠테이션 - 모델 변경
\app/src/main/java/com/cherrish/android/presentation/challenge/routine/model/ChallengeRoutineModel.kt`, `.../ChallengeRoutineUiModel.kt``
기존 ChallengeRoutineModel 제거, ChallengeRoutineUiModel(id:Int) 추가 및 toUiModel() 확장함수 추가
프레젠테이션 - 루틴 화면/뷰모델
\app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt`, `.../ChallengeRoutineScreen.kt`, `.../ChallengeRoutineUiState.kt`, `.../component/ChallengeRoutineOnboardingBody.kt``
ViewModel에 ChallengeRepository 주입으로 API 기반 초기화 도입, side-effect(flow) 추가, id 타입 Long→Int 변경 및 UI 모델 교체
프레젠테이션 - 네비게이션 및 시작 화면
\app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt`, `.../start/ChallengeStartRoute.kt`, `.../start/ChallengeStartViewModel.kt``
라우트 재구성: ChallengeStart, ChallengeRoutine, ChallengeMission 추가; 라우팅 함수 3개 추가; StartViewModel에 side-effect 도입
프레젠테이션 - 미션 관련
\app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeSideEffect.kt`, `.../mission/ChallengeMissionUiState.kt`, `.../mission/ChallengeMissionViewModel.kt`, `.../mission/ChallengeMissionSelectedScreen.kt``
ChallengeSideEffect.navigateToMission 추가, MissionUiState에 Fake companion 추가, 레이아웃 패딩 소폭 조정
프레젠테이션 - 제거
\app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressScreen.kt```
전체 파일 삭제 (미션 진행 화면 관련 208줄 제거)
기타 UI / 네비게이션 연동
\app/src/main/java/com/cherrish/android/presentation/main/...\, \app/src/main/java/com/cherrish/android/presentation/home/...\, \app/src/main/java/com/cherrish/android/core/designsystem/component/type/CherrishGaugeType.kt``
Main 네비게이션에 ChallengeStart/Routine/Mission 라우팅 호출 반영, HomeViewModel 의존성 조정, LoadingScreen 추가, CherrishGaugeType enum 추가

Sequence Diagram

sequenceDiagram
    actor User
    participant Screen as ChallengeRoutineScreen
    participant VM as ChallengeRoutineViewModel
    participant Repo as ChallengeRepository
    participant DS as ChallengeDataSource
    participant Service as ChallengeService
    participant API as RemoteAPI

    User->>Screen: 진입
    Screen->>VM: 초기화
    VM->>Repo: getChallengeRoutineData()
    Repo->>DS: getHomecareRoutineData()
    DS->>Service: getHomecareRoutineData()
    Service->>API: GET /api/challenges/homecare-routines
    API-->>Service: DTO 리스트
    Service-->>DS: BaseResponse<DTO[]>
    DS-->>Repo: BaseResponse<DTO[]>
    Repo->>Repo: DTO -> Model (toModel/ toUiModel)
    Repo-->>VM: Result<Model 리스트>
    VM-->>Screen: uiState 업데이트
    User->>Screen: 루틴 선택 / 다음 클릭
    Screen->>VM: onRoutineClick / onNextClick
    VM->>VM: _sideEffect.emit(navigateToMission)
    VM-->>Screen: ChallengeSideEffect.navigateToMission
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • nhyeonii
  • sohee6989
  • usuuhyn
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
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.
Out of Scope Changes check ❓ Inconclusive HomeViewModel의 의존성 타입 변경(HomeRepository → HomeRepositoryImpl)과 ChallengeMissionProgressScreen 전체 삭제는 API 연결 요구사항과 직접 관련이 없어 보입니다. HomeViewModel 변경과 ChallengeMissionProgressScreen 삭제가 #103과 관련이 있는지 명확히 하고, 필요시 별도 PR로 분리하는 것을 검토하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 메인 변경사항을 명확하게 요약하고 있습니다: 챌린지 루틴 목록 조회 API 연결 완료.
Linked Issues check ✅ Passed API 연결을 위한 데이터 계층, 저장소 계층, 뷰모델 계층이 모두 구현되었고, challenge_start에서 challenge_routine 경로로의 네비게이션 플로우가 완성되었습니다.
Description check ✅ Passed PR 설명이 리포지토리의 필수 템플릿 구조를 대부분 따르고 있으나, 스크린샷과 상세한 작업 내용이 부족합니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@hyeminililo hyeminililo added FEAT✨ 새로운 기능 구현 혜민🍒 혜민 담당 labels Jan 20, 2026
sohee6989 and others added 7 commits January 21, 2026 04:17
머지 병합

# Conflicts:
#	app/src/main/java/com/cherrish/android/core/designsystem/component/type/CherrishGaugeType.kt

# Conflicts:
#	app/src/main/java/com/cherrish/android/core/designsystem/component/type/CherrishGaugeType.kt
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: 11

Caution

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

⚠️ Outside diff range comments (3)
app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartScreen.kt (1)

45-49: paddingValues 적용 순서가 올바르지 않습니다.

Based on learnings, paddingValues는 시스템 바(status/navigation bar)를 위한 것이므로 root container의 modifier에 먼저 적용해야 합니다. 현재는 horizontal 패딩이 먼저 적용되고 있습니다.

♻️ 제안된 수정
     Column(
         modifier = modifier
             .fillMaxSize()
-            .padding(horizontal = 17.dp)
             .padding(paddingValues)
+            .padding(horizontal = 17.dp)
-
     ) {
app/src/main/java/com/cherrish/android/presentation/challenge/mission/ChallengeMissionViewModel.kt (1)

55-60: onAddTodoClick이 선택된 상태에서도 기능 구현이 없습니다.

if (!state.isSelected) return@updateSuccess state 조건은 통과하지만, 이후 state.copy()만 호출하여 상태 변화가 없는 no-op 상태입니다. 선택된 미션을 처리하는 로직(API 호출, 데이터 저장, 네비게이션 등)을 구현하거나, 프로젝트의 다른 ViewModel들처럼 SideEffect 패턴을 통해 네비게이션을 처리해야 합니다.

app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt (1)

52-54: 로딩 화면에서 Scaffold 인셋이 누락되고 있습니다

로딩 상태에서 paddingValues가 전달되지 않아 시스템 바/Scaffold 인셋이 무시되고 있습니다. 또한 로컬 LoadingScreen이 디자인시스템 LoadingScreen 컴포넌트를 섀도잉하고 있습니다. 아래처럼 paddingValues를 전달하고 루트 컨테이너에 적용해 주세요.

🛠️ 수정 제안
         is UiState.Loading -> {
-            LoadingScreen()
+            LoadingScreen(paddingValues = paddingValues)
         }
 `@Composable`
 private fun LoadingScreen(
+    paddingValues: PaddingValues,
 ) {
     Column(
         modifier = Modifier
             .fillMaxSize()
+            .padding(paddingValues)
             .background(color = CherrishTheme.colors.gray0),
         horizontalAlignment = Alignment.CenterHorizontally,
         verticalArrangement = Arrangement.Center
     ) {
         CircularProgressIndicator()
     }
 }
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/data/remote/dto/response/ChallengesResponseDto.kt`:
- Around line 28-59: The DTOs have a type mismatch:
ChallengeMissionProgressDataDto.progressPercentage is Int while
AdvanceDayDataDto.progressPercentage is Double; since the advance-day endpoint
and ChallengeMissionProgressResponseDto use Int, either remove the unused
AdvanceDayDataDto or change its progressPercentage to Int to match; locate
AdvanceDayDataDto and either delete that class (if unused) or update its
progressPercentage property to Int and run build/tests to ensure consistency
across usages (also update any mapping/parsing that references
AdvanceDayDataDto).

In
`@app/src/main/java/com/cherrish/android/data/repositoryimpl/ChallengeRepositoryImpl.kt`:
- Around line 13-19: Replace the unsafe use of data!! in getChallengeRoutineData
to handle null explicitly: call challengeDataSource.getHomecareRoutineData(),
check its .data for null, and either return a failed Result (e.g.,
Result.failure(IllegalStateException("homecare data missing"))) or return
Result.success(emptyList()) per project policy; update suspendRunCatching usage
so mapping (it.toModel()) only runs when .data is non-null and include a clear
error message referencing getChallengeRoutineData and getHomecareRoutineData
when returning failure.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/mission/ChallengeMissionViewModel.kt`:
- Around line 18-20: The ViewModel currently seeds MutableStateFlow with
UiState.Success(ChallengeMissionUiState.Fake) (and later overwrites with a fixed
list in init), which can mask real API results; change the initial state to a
neutral state (e.g., UiState.Loading or UiState.Empty) in
ChallengeMissionViewModel and remove the hardcoded overwrite in init so the
state is updated only from the Repository/UseCase result (the fetch/update
method used in init). If you need fake data for previews or debug, gate
ChallengeMissionUiState.Fake behind a debug/preview flag or a separate factory
method so production paths always consume the real useCase/repository responses.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt`:
- Around line 34-56: UiState.Failure currently renders nothing; update the
Failure branch inside ChallengeMissionProgressRoute to show a simple error UI
(e.g., an ErrorScreen composable or Column with Text and a Button) that displays
the failure message and offers a retry action. Wire the retry Button to a
ViewModel retry/refresh method (call viewModel::retry or add a retry/fetch
function to ChallengeMissionProgressViewModel if missing) and pass any error
detail from the UiState.Failure payload so the user can see what went wrong and
retry. Ensure the new Error UI replaces the empty block under the
UiState.Failure case in ChallengeMissionProgressRoute.
- Around line 101-132: Replace the hardcoded texts in
ChallengeMissionSelectedTitle with string resources: add two entries in
strings.xml (e.g., <string name="challenge_title">%s 챌린지</string> and <string
name="seven_day_plan">7일 플랜</string>), then in ChallengeMissionSelectedTitle use
stringResource(R.string.challenge_title, challengeName) for the dynamic title
and stringResource(R.string.seven_day_plan) for the badge; also ensure you
import androidx.compose.ui.res.stringResource and remove the literal strings
from Text composables.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt`:
- Around line 50-55: Rename the inconsistent parameter
navigationChallengeMission in the function challengeNavGraph to
navigateToChallengeMission to match the navigateToChallengeRoutine naming
pattern; update all references/usages of navigationChallengeMission (e.g., the
call site inside challengeNavGraph where it's invoked) to the new name so the
parameter and its invocations (within challengeNavGraph and any lambdas or
callers) remain consistent.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt`:
- Around line 54-63: Remove the dead commented-out hardcoded initialization
block for _uiState.updateSuccess and its
ChallengeRoutineUiState/ChallengeRoutineUiModel entries; locate the commented
lines referencing _uiState.updateSuccess, ChallengeRoutineUiState, and
ChallengeRoutineUiModel in ChallengeRoutineViewModel and delete the entire
commented section so only active code remains (no behavior change required).
- Around line 51-53: The onLogFailure block in ChallengeRoutineViewModel is
empty, leaving _uiState stuck at UiState.Loading on API failure; update
onLogFailure to set the ViewModel's state to an error (e.g., _uiState.value =
UiState.Error(...)) or a non-loading state and pass an appropriate error
message/exception so the UI can show feedback, and ensure you reference and use
the existing _uiState and UiState types inside the onLogFailure handler in
ChallengeRoutineViewModel.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartScreen.kt`:
- Around line 27-37: ChallengeStartRoute currently injects a
ChallengeStartViewModel that is never used; either remove the unused parameter
from ChallengeStartRoute (and its default hiltViewModel() injection) and update
callers to stop passing a viewModel, or if the ViewModel is needed for side
effects, subscribe to its sideEffect Flow inside ChallengeStartRoute (e.g., use
LaunchedEffect and collect/collectLatest on viewModel.sideEffect) and handle
emitted events (or forward state/handlers into ChallengeStartScreen). Update
imports and any callers accordingly and keep the function signature consistent
with ChallengeStartScreen’s needs.

In
`@app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartViewModel.kt`:
- Around line 16-25: The _sideEffect MutableSharedFlow, the exposed sideEffect
SharedFlow, and the onNextClick() method in ChallengeStartViewModel are unused
because ChallengeStartScreen receives an onNavigateRoutine callback directly;
remove the unused fields and method (symbols: _sideEffect, sideEffect,
onNextClick, ChallengeStartViewModel) to avoid dead code, or alternatively
refactor ChallengeStartScreen to collect viewModel.sideEffect and invoke
onNavigateRoutine from the collected ChallengeSideEffect if you prefer the
shared-effect pattern (symbols to touch: ChallengeStartScreen,
ChallengeStartViewModel, ChallengeSideEffect).

In `@app/src/main/java/com/cherrish/android/presentation/home/HomeViewModel.kt`:
- Line 8: HomeViewModel currently depends on the concrete HomeRepositoryImpl
instead of the HomeRepository interface; update the class to depend on
HomeRepository (not HomeRepositoryImpl) so DI/RepositoryModule bindings and
tests/mocks work correctly: change any imports/constructor parameters/properties
referencing HomeRepositoryImpl to HomeRepository and ensure the DI-injected type
used in HomeViewModel is the interface (HomeRepository) everywhere.
🧹 Nitpick comments (7)
app/src/main/java/com/cherrish/android/data/remote/dto/response/ChallengesResponseDto.kt (1)

7-14: TodayRoutineDtoChallengeRoutineDto가 동일한 필드를 가지고 있습니다.

두 DTO 클래스가 완전히 동일한 필드(routineId, name, scheduledDate, isComplete)를 가지고 있어 DRY 원칙을 위반합니다. 하나의 공통 DTO로 통합하는 것을 고려해 주세요.

또한 ChallengeHomecareRoutinesResponseDtoidInt 타입인 반면, 다른 DTO들의 routineIdchallengeIdLong 타입입니다. API 응답에 따라 일관된 타입 사용이 필요한지 확인해 주세요.

Also applies to: 61-74, 100-113

app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeSideEffect.kt (1)

3-5: Kotlin 네이밍 컨벤션: data object는 PascalCase 사용 권장

navigateToMission은 소문자로 시작하고 있으나, Kotlin의 data object는 타입이므로 PascalCase 네이밍 컨벤션을 따르는 것이 좋습니다.

♻️ 제안된 수정
 sealed interface ChallengeSideEffect {
-    data object navigateToMission : ChallengeSideEffect
+    data object NavigateToMission : ChallengeSideEffect
 }
app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineScreen.kt (1)

48-67: Loading/Failure 상태에 대한 UI 피드백 고려

현재 UiState.LoadingUiState.Failure 상태에서 빈 블록으로 처리되어 사용자에게 피드백이 없습니다. 로딩 인디케이터나 에러 메시지 표시를 고려해 보세요.

♻️ 제안된 개선 예시
when (val state = uiState) {
    is UiState.Loading -> {
        // 로딩 인디케이터 표시
        CherrishLoadingScreen()
    }
    is UiState.Failure -> {
        // 에러 메시지 또는 재시도 버튼 표시
        CherrishErrorScreen(onRetry = viewModel::fetchRoutines)
    }
    is UiState.Success -> { /* ... */ }
    else -> {}
}
app/src/main/java/com/cherrish/android/presentation/main/MainAppState.kt (1)

117-123: navigateToChallengeRoutineNavOptions 파라미터 추가를 고려해주세요.

navigateToChallengeMissionNavOptions 파라미터를 받지만, navigateToChallengeRoutine은 받지 않습니다. 다른 네비게이션 함수들(navigateToOnboarding, navigateToHome 등)과의 일관성을 위해 동일한 시그니처를 사용하는 것이 좋습니다.

또한, 두 함수 사이에 빈 줄을 추가하면 가독성이 향상됩니다.

♻️ 제안하는 수정사항
-    fun navigateToChallengeRoutine() {
-        navController.navigateToChallengeRoutine()
+    fun navigateToChallengeRoutine(navOptions: NavOptions? = null) {
+        navController.navigateToChallengeRoutine(navOptions)
     }
+
     fun navigateToChallengeMission(navOptions: NavOptions? = clearStackNavOptions) {
         navController.navigateToChallengeMission(navOptions)
     }
app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt (2)

21-23: ChallengeMission 선언과 함수 사이에 빈 줄을 추가해주세요.

data object 선언과 extension function 사이에 빈 줄이 없어 가독성이 떨어집니다.

♻️ 제안하는 수정사항
 `@Serializable`
 data object ChallengeMission : Route
+
 fun NavController.navigateToChallengeStart(

72-77: 주석 처리된 TODO 코드를 정리해주세요.

// navigateToProgress = { }가 주석으로 남아 있습니다. 향후 구현이 필요한 경우 TODO 주석으로 명시하거나, 불필요하면 제거해주세요.

♻️ 제안하는 수정사항
     composable<ChallengeMission> {
         ChallengeMissionSelectedRoute(
             paddingValues = paddingValues
-            // navigateToProgress = { }
         )
     }
app/src/main/java/com/cherrish/android/presentation/challenge/mission/ChallengeMissionUiState.kt (1)

6-6: Fake 데이터는 테스트/프리뷰 전용으로 한정하는 게 안전합니다.

companion object의 public Fake는 실사용 경로에서 의존될 위험이 있어, internal/@VisibleForTesting/프리뷰 전용 Provider로 분리하는 것을 권장합니다.

Also applies to: 19-48

Comment on lines +28 to +59
@Serializable
data class ChallengeMissionProgressDataDto(
@SerialName("challengeId")
val challengeId: Long,

@SerialName("title")
val title: String,

@SerialName("currentDay")
val currentDay: Int,

@SerialName("progressPercentage")
val progressPercentage: Int,

@SerialName("cherryLevel")
val cherryLevel: Int,

@SerialName("cherryLevelName")
val cherryLevelName: String,

@SerialName("progressToNextLevel")
val progressToNextLevel: Double,

@SerialName("remainingRoutinesToNextLevel")
val remainingRoutinesToNextLevel: Int,

@SerialName("todayRoutines")
val todayRoutines: List<TodayRoutineDto>,

@SerialName("cheeringMessage")
val cheeringMessage: 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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "progressPercentage" --type=kt -C2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

fd "ChallengesResponseDto.kt" && cat -n app/src/main/java/com/cherrish/android/data/remote/dto/response/ChallengesResponseDto.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 4228


🏁 Script executed:

rg "ChallengeMissionProgressDataDto|AdvanceDayDataDto" -C 3 | head -50

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 1548


🏁 Script executed:

rg "progressPercentage" -B 2 -A 2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 9103


🏁 Script executed:

rg "AdvanceDayDataDto" -B 3 -A 3

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 802


🏁 Script executed:

rg "AdvanceDayDataDto" --type-list | grep -i kotlin

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 463


🏁 Script executed:

rg "AdvanceDayDataDto" -C 5

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 1264


🏁 Script executed:

rg "AdvanceDayDataDto" --no-heading

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 192


🏁 Script executed:

rg "AdvanceDay" -C 2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 4750


🏁 Script executed:

fd "ChallengeMissionProgressResponseDto.kt" && cat -n app/src/main/java/com/cherrish/android/data/remote/dto/response/ChallengeMissionProgressResponseDto.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 1619


progressPercentage 필드 타입 불일치 확인

ChallengeMissionProgressDataDto에서는 progressPercentageInt 타입(Line 40)이고, AdvanceDayDataDto에서는 Double 타입(Line 127)입니다. 두 DTO는 거의 동일한 구조인데 이 필드만 타입이 다르며, AdvanceDayDataDto는 현재 코드베이스에서 사용되지 않고 있습니다.

실제 advance-day 엔드포인트는 ChallengeMissionProgressResponseDto를 반환하며 여기서도 progressPercentageInt 타입입니다. 불필요한 AdvanceDayDataDto를 제거하거나, 사용 계획이 있다면 두 DTO의 타입을 일치시켜야 합니다.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/data/remote/dto/response/ChallengesResponseDto.kt`
around lines 28 - 59, The DTOs have a type mismatch:
ChallengeMissionProgressDataDto.progressPercentage is Int while
AdvanceDayDataDto.progressPercentage is Double; since the advance-day endpoint
and ChallengeMissionProgressResponseDto use Int, either remove the unused
AdvanceDayDataDto or change its progressPercentage to Int to match; locate
AdvanceDayDataDto and either delete that class (if unused) or update its
progressPercentage property to Int and run build/tests to ensure consistency
across usages (also update any mapping/parsing that references
AdvanceDayDataDto).

Comment on lines +13 to +19
override suspend fun getChallengeRoutineData():
Result<List<ChallengeHomecareRoutinesResponseModel>> =
suspendRunCatching {
challengeDataSource
.getHomecareRoutineData()
.data!!
.map { it.toModel() }
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

data!! NPE 위험 — 명시적 널 처리 필요.

서버 응답에서 data가 null이면 즉시 NPE가 발생합니다. 실패로 돌릴지/빈 리스트로 처리할지 정책을 명시해 주세요.

🛠️ 수정 제안(명시적 실패 처리)
-        suspendRunCatching {
-            challengeDataSource
-                .getHomecareRoutineData()
-                .data!!
-                .map { it.toModel() }
-        }
+        suspendRunCatching {
+            val data = challengeDataSource
+                .getHomecareRoutineData()
+                .data
+                ?: throw IllegalStateException("Homecare routine data is null")
+            data.map { it.toModel() }
+        }
📝 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
override suspend fun getChallengeRoutineData():
Result<List<ChallengeHomecareRoutinesResponseModel>> =
suspendRunCatching {
challengeDataSource
.getHomecareRoutineData()
.data!!
.map { it.toModel() }
override suspend fun getChallengeRoutineData():
Result<List<ChallengeHomecareRoutinesResponseModel>> =
suspendRunCatching {
val data = challengeDataSource
.getHomecareRoutineData()
.data
?: throw IllegalStateException("Homecare routine data is null")
data.map { it.toModel() }
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/data/repositoryimpl/ChallengeRepositoryImpl.kt`
around lines 13 - 19, Replace the unsafe use of data!! in
getChallengeRoutineData to handle null explicitly: call
challengeDataSource.getHomecareRoutineData(), check its .data for null, and
either return a failed Result (e.g.,
Result.failure(IllegalStateException("homecare data missing"))) or return
Result.success(emptyList()) per project policy; update suspendRunCatching usage
so mapping (it.toModel()) only runs when .data is non-null and include a clear
error message referencing getChallengeRoutineData and getHomecareRoutineData
when returning failure.

Comment on lines +18 to +20
MutableStateFlow<UiState<ChallengeMissionUiState>>(
UiState.Success(ChallengeMissionUiState.Fake)
)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jan 20, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

하드코딩된 초기/로드 데이터가 실제 API 결과를 덮어쓸 위험이 큽니다.

초기 상태를 Success(Fake)로 두고 init에서 다시 고정 리스트로 덮어써서, API 연동이 되어 있더라도 UI가 스텁 데이터로 고정될 수 있습니다. 실제 Repository/UseCase 결과로 갱신하도록 전환하거나, Fake는 디버그/프리뷰 분기에서만 쓰는 구조로 분리해 주세요.

Also applies to: 27-38

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/mission/ChallengeMissionViewModel.kt`
around lines 18 - 20, The ViewModel currently seeds MutableStateFlow with
UiState.Success(ChallengeMissionUiState.Fake) (and later overwrites with a fixed
list in init), which can mask real API results; change the initial state to a
neutral state (e.g., UiState.Loading or UiState.Empty) in
ChallengeMissionViewModel and remove the hardcoded overwrite in init so the
state is updated only from the Repository/UseCase result (the fetch/update
method used in init). If you need fake data for previews or debug, gate
ChallengeMissionUiState.Fake behind a debug/preview flag or a separate factory
method so production paths always consume the real useCase/repository responses.

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.

저 이것도 반영할까요 ..?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@hyeminililo 좋은 질문입니다! 제 의견은 다음과 같습니다:

현재 PR 범위를 고려한 추천:

이 PR은 "챌린지 루틴 목록 조회 API 연결"에 집중하고 있고, ChallengeMissionViewModel의 미션 데이터는 아직 API가 연결되지 않은 상태로 보입니다.

두 가지 옵션이 있습니다:

  1. 지금 반영하지 않기 (추천):

    • 현재 PR의 범위를 루틴 API 연결로 한정
    • 미션 API 연결할 때 함께 리팩토링
    • 이슈를 생성하여 추후 작업으로 관리
  2. 지금 일부 반영하기:

    • 초기 상태만 UiState.Loading으로 변경
    • loadMissions()는 그대로 두되, 추후 API 연결 시 교체 예정임을 주석으로 표시
    private val _uiState = MutableStateFlow<UiState<ChallengeMissionUiState>>(UiState.Loading)
    
    init {
        loadMissions() // TODO: Replace with API call when mission endpoint is ready
    }

추천: 현재 PR의 범위를 지키고, 미션 데이터 API 연결은 별도 이슈로 관리하는 것이 좋을 것 같습니다. 원하신다면 follow-up 이슈를 생성해 드릴까요?

Comment on lines +34 to +56
@Composable
fun ChallengeMissionProgressRoute(
paddingValues: PaddingValues,
viewModel: ChallengeMissionProgressViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()

when (val state = uiState) {
is UiState.Loading -> {
LoadingScreen()
}
is UiState.Failure -> {}
is UiState.Success -> {
ChallengeMissionprogressScreen(
paddingValues = paddingValues,
uiState = state.data,
onTodoClick = viewModel::onTodoClick,
onCompleteTodayClick = viewModel::onCompletedTodayClick
)
}

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 | 🟡 Minor

Failure 상태가 빈 화면으로 남습니다

UiState.Failure에서 아무 UI도 그리지 않아 사용자에게 멈춘 화면처럼 보일 수 있습니다. 최소한 오류 메시지/재시도 버튼을 제공해 주세요.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt`
around lines 34 - 56, UiState.Failure currently renders nothing; update the
Failure branch inside ChallengeMissionProgressRoute to show a simple error UI
(e.g., an ErrorScreen composable or Column with Text and a Button) that displays
the failure message and offers a retry action. Wire the retry Button to a
ViewModel retry/refresh method (call viewModel::retry or add a retry/fetch
function to ChallengeMissionProgressViewModel if missing) and pass any error
detail from the UiState.Failure payload so the user can see what went wrong and
retry. Ensure the new Error UI replaces the empty block under the
UiState.Failure case in ChallengeMissionProgressRoute.

Comment on lines +101 to +132
@Composable
private fun ChallengeMissionSelectedTitle(
challengeName: String,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "$challengeName 챌린지",
style = CherrishTheme.typography.title1SB18,
color = CherrishTheme.colors.gray1000
)

Text(
text = "7일 플랜",
color = CherrishTheme.colors.gray700,
style = CherrishTheme.typography.body3M12,
modifier = Modifier
.background(
color = CherrishTheme.colors.gray100,
shape = RoundedCornerShape(4.dp)
)
.border(
width = 1.dp,
color = CherrishTheme.colors.gray700,
shape = RoundedCornerShape(4.dp)
)
.padding(horizontal = 8.dp, vertical = 3.dp)
)
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:

find . -type f -name "strings.xml" | head -20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 111


🏁 Script executed:

git ls-files | grep -E "(strings|ChallengeMissionprogress)" | head -20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 221


🏁 Script executed:

head -150 app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 5247


🏁 Script executed:

cat app/src/main/res/values/strings.xml | head -100

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 144


🏁 Script executed:

rg "stringResource" app/src/main/java/com/cherrish/android/presentation/challenge/ -A 1 | head -30

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 56


🏁 Script executed:

rg "import.*stringResource" app/src/main/java/com/cherrish/android/ | head -10

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 56


🏁 Script executed:

rg "stringResource" app/src/main/java/ --type kotlin | head -20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 56


🏁 Script executed:

fd "strings" app/src/main/res/ --type f

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 109


🏁 Script executed:

rg "chapellenges|challenge" app/src/main/res/values/strings.xml

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 56


하드코딩된 문자열을 stringResource로 추출 권장

"7일 플랜""${challengeName} 챌린지"는 다국어 지원을 위해 strings.xml에 정의하고 stringResource()를 사용하는 것이 좋습니다. 특히 "7일 플랜"은 직접 추출 가능하며, 동적 값이 포함된 문자열은 strings.xml에서 "%s 챌린지" 형태로 포매팅 인자를 활용할 수 있습니다.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt`
around lines 101 - 132, Replace the hardcoded texts in
ChallengeMissionSelectedTitle with string resources: add two entries in
strings.xml (e.g., <string name="challenge_title">%s 챌린지</string> and <string
name="seven_day_plan">7일 플랜</string>), then in ChallengeMissionSelectedTitle use
stringResource(R.string.challenge_title, challengeName) for the dynamic title
and stringResource(R.string.seven_day_plan) for the badge; also ensure you
import androidx.compose.ui.res.stringResource and remove the literal strings
from Text composables.

Comment on lines 51 to 53
.onLogFailure {
}
}
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

API 실패 시 에러 처리가 누락되었습니다.

onLogFailure 블록이 비어 있어서 API 호출 실패 시 UI 상태가 UiState.Loading에 머물러 사용자에게 아무런 피드백이 제공되지 않습니다. 실패 상태로 업데이트하거나 적절한 에러 처리를 추가해주세요.

🐛 제안하는 수정사항
                 .onLogFailure {
+                    _uiState.value = UiState.Failure(it)
                 }
📝 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
.onLogFailure {
}
}
.onLogFailure {
_uiState.value = UiState.Failure(it)
}
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt`
around lines 51 - 53, The onLogFailure block in ChallengeRoutineViewModel is
empty, leaving _uiState stuck at UiState.Loading on API failure; update
onLogFailure to set the ViewModel's state to an error (e.g., _uiState.value =
UiState.Error(...)) or a non-loading state and pass an appropriate error
message/exception so the UI can show feedback, and ensure you reference and use
the existing _uiState and UiState types inside the onLogFailure handler in
ChallengeRoutineViewModel.

Comment on lines 27 to 37
@Composable
fun ChallengeStartRoute(
paddingValues: PaddingValues,
onNavigateRoutine: () -> Unit,
viewModel: ChallengeStartViewModel = hiltViewModel()

) {
ChallengeStartScreen(
paddingValues = paddingValues,
onNextClick = viewModel::onNextClick
onNextClick = onNavigateRoutine
)
}
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

사용되지 않는 viewModel 파라미터

viewModel: ChallengeStartViewModel이 주입되지만 Route 내에서 전혀 사용되지 않습니다. 사용하지 않는 의존성은 제거하거나, ChallengeStartViewModelsideEffect를 활용하려면 해당 flow를 구독해야 합니다.

♻️ 제안: 사용하지 않는 viewModel 제거
 `@Composable`
 fun ChallengeStartRoute(
     paddingValues: PaddingValues,
-    onNavigateRoutine: () -> Unit,
-    viewModel: ChallengeStartViewModel = hiltViewModel()
+    onNavigateRoutine: () -> Unit
 ) {
     ChallengeStartScreen(
         paddingValues = paddingValues,
         onNextClick = onNavigateRoutine
     )
 }
📝 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
@Composable
fun ChallengeStartRoute(
paddingValues: PaddingValues,
onNavigateRoutine: () -> Unit,
viewModel: ChallengeStartViewModel = hiltViewModel()
) {
ChallengeStartScreen(
paddingValues = paddingValues,
onNextClick = viewModel::onNextClick
onNextClick = onNavigateRoutine
)
}
`@Composable`
fun ChallengeStartRoute(
paddingValues: PaddingValues,
onNavigateRoutine: () -> Unit
) {
ChallengeStartScreen(
paddingValues = paddingValues,
onNextClick = onNavigateRoutine
)
}
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartScreen.kt`
around lines 27 - 37, ChallengeStartRoute currently injects a
ChallengeStartViewModel that is never used; either remove the unused parameter
from ChallengeStartRoute (and its default hiltViewModel() injection) and update
callers to stop passing a viewModel, or if the ViewModel is needed for side
effects, subscribe to its sideEffect Flow inside ChallengeStartRoute (e.g., use
LaunchedEffect and collect/collectLatest on viewModel.sideEffect) and handle
emitted events (or forward state/handlers into ChallengeStartScreen). Update
imports and any callers accordingly and keep the function signature consistent
with ChallengeStartScreen’s needs.

Comment on lines +16 to 25
private val _sideEffect = MutableSharedFlow<ChallengeSideEffect>()
val sideEffect: SharedFlow<ChallengeSideEffect> = _sideEffect.asSharedFlow()

fun onNextClick() {
viewModelScope.launch {
_sideEffect.emit(
ChallengeSideEffect.navigateToMission
)
}
}
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:

fd "ChallengeStartScreen.kt" --type f

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 165


🏁 Script executed:

rg "ChallengeStartScreen" -t kt --max-count=20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

rg "sideEffect" -t kt -B 3 -A 3 --max-count=50

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

rg "onNextClick" -t kt -B 2 -A 2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

cat app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartScreen.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 3849


🏁 Script executed:

rg "sideEffect" --max-count=50

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 4847


🏁 Script executed:

rg "onNextClick" -B 2 -A 2

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 15857


🏁 Script executed:

rg "navigateToMission" -B 3 -A 3

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 5135


sideEffect flow와 onNextClick() 메서드는 사용되지 않는 코드입니다

ChallengeStartViewModel에서 정의한 sideEffect flow와 onNextClick() 메서드는 실제로 호출되지 않습니다. ChallengeStartScreen은 viewModel을 주입받지만 사용하지 않으며, 대신 onNavigateRoutine 콜백을 직접 전달받아 처리합니다.

다른 화면들(SplashScreen, HomeScreen, CalendarScreen, ChallengeRoutineScreen 등)은 viewModel.sideEffect.collectLatestSideEffect를 통해 side effect를 처리하는 패턴을 따르고 있으므로, 두 가지 방식 중 하나를 선택해야 합니다:

  1. ChallengeStartScreen에서도 viewModel.sideEffect를 수집하여 side effect 패턴으로 통일
  2. 또는 사용하지 않는 _sideEffect, sideEffect, onNextClick() 제거
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartViewModel.kt`
around lines 16 - 25, The _sideEffect MutableSharedFlow, the exposed sideEffect
SharedFlow, and the onNextClick() method in ChallengeStartViewModel are unused
because ChallengeStartScreen receives an onNavigateRoutine callback directly;
remove the unused fields and method (symbols: _sideEffect, sideEffect,
onNextClick, ChallengeStartViewModel) to avoid dead code, or alternatively
refactor ChallengeStartScreen to collect viewModel.sideEffect and invoke
onNavigateRoutine from the collected ChallengeSideEffect if you prefer the
shared-effect pattern (symbols to touch: ChallengeStartScreen,
ChallengeStartViewModel, ChallengeSideEffect).

import com.cherrish.android.core.common.state.UiState
import com.cherrish.android.data.model.toTodayDateString
import com.cherrish.android.data.repository.HomeRepository
import com.cherrish.android.data.repositoryimpl.HomeRepositoryImpl
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

🧩 Analysis chain

🏁 Script executed:

# First, find the HomeViewModel file and check its content
find . -name "HomeViewModel.kt" -type f | head -20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 204


🏁 Script executed:

# Also check for HomeRepository interface and HomeRepositoryImpl
fd -t f "HomeRepository" --extension kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 227


🏁 Script executed:

# Look for DI/Hilt module files
fd -t f -e kt | grep -i "module\|di" | head -20

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 1788


🏁 Script executed:

cat -n app/src/main/java/com/cherrish/android/presentation/home/HomeViewModel.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 3260


🏁 Script executed:

cat -n app/src/main/java/com/cherrish/android/data/repository/HomeRepository.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 317


🏁 Script executed:

cat -n app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 2573


🏁 Script executed:

head -40 app/src/main/java/com/cherrish/android/data/repositoryimpl/HomeRepositoryImpl.kt

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 728


🏁 Script executed:

# Search for other ViewModel `@Inject` constructors to see the pattern
rg -A2 "@HiltViewModel" --type=kt -A3 | grep -E "class |@Inject|Repository" | head -30

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 100


🏁 Script executed:

# Search for other ViewModel `@Inject` constructors to see the pattern
rg -A3 "@HiltViewModel" --type kotlin | grep -E "class |@Inject|Repository" | head -40

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 2612


🏁 Script executed:

# Alternative: search for all `@Inject` constructor patterns in ViewModels
rg "@Inject constructor" app/src/main/java/com/cherrish/android/presentation --type-list | head -5

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 463


🏁 Script executed:

# Search for other ViewModels to see if they use interface or impl
fd "ViewModel.kt" app/src/main/java/com/cherrish/android/presentation -x grep -l "Repository" {}

Repository: TEAM-Cherrish/Cherrish-Android

Length of output: 630


구현체 직접 주입은 확립된 DI 패턴을 벗어납니다.

이 파일은 다른 모든 ViewModel과 달리 HomeRepository 인터페이스 대신 HomeRepositoryImpl 구현체에 직접 의존합니다. 이는 테스트/모킹을 어렵게 하고 코드베이스의 일관된 DI 패턴을 해칩니다. HomeRepository 인터페이스에 의존하도록 수정하세요 (RepositoryModule에 이미 바인딩되어 있음).

♻️ 권장 수정안
-import com.cherrish.android.data.repositoryimpl.HomeRepositoryImpl
+import com.cherrish.android.data.repository.HomeRepository
...
-class HomeViewModel `@Inject` constructor(
-    private val homeRepositoryImpl: HomeRepositoryImpl
+class HomeViewModel `@Inject` constructor(
+    private val homeRepository: HomeRepository
...
-            homeRepositoryImpl.getMainDashboard().onSuccess { response ->
+            homeRepository.getMainDashboard().onSuccess { response ->
📝 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 com.cherrish.android.data.repositoryimpl.HomeRepositoryImpl
import com.cherrish.android.data.repository.HomeRepository
🤖 Prompt for AI Agents
In `@app/src/main/java/com/cherrish/android/presentation/home/HomeViewModel.kt` at
line 8, HomeViewModel currently depends on the concrete HomeRepositoryImpl
instead of the HomeRepository interface; update the class to depend on
HomeRepository (not HomeRepositoryImpl) so DI/RepositoryModule bindings and
tests/mocks work correctly: change any imports/constructor parameters/properties
referencing HomeRepositoryImpl to HomeRepository and ensure the DI-injected type
used in HomeViewModel is the interface (HomeRepository) everywhere.

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

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/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt (1)

56-63: onRoutineClick 함수 시그니처가 호출부 기대 타입과 불일치합니다.

ChallengeRoutineViewModel.onRoutineClick(id: Int)(Int) -> Unit이지만, ChallengeMissionTodoSection의 콜백 파라미터는 onRoutineClick: (Long) -> Unit을 기대합니다. 이로 인해 ViewModel 함수를 직접 전달할 수 없으며, 타입 변환 시 데이터 손실(Long → Int) 위험이 있습니다. 호출부의 콜백 타입(Long)과 ViewModel 함수 매개변수(Int) 타입을 일치시키거나, 적절한 변환 로직을 추가하세요.

♻️ Duplicate comments (2)
app/src/main/java/com/cherrish/android/presentation/challenge/navigation/ChallengeNavigator.kt (1)

50-55: 파라미터 네이밍 일관성 맞춰 주세요.

navigationChallengeMission만 동사형 규칙에서 벗어나 있어 가독성이 떨어집니다. navigateToChallengeMission으로 맞추는 게 좋습니다.

✏️ 제안 수정
 fun NavGraphBuilder.challengeNavGraph(
     paddingValues: PaddingValues,
     navigateToChallengeRoutine: () -> Unit,
-    navigationChallengeMission: () -> Unit,
+    navigateToChallengeMission: () -> Unit,
     navigateUp: () -> Unit
 ) {
 ...
-            navigateToMission = navigationChallengeMission
+            navigateToMission = navigateToChallengeMission

Also applies to: 68-68

app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt (1)

38-52: API 실패 시 상태 전환 누락(중복 지적).
실패 시 _uiStateLoading에 머무를 수 있어 사용자 피드백이 없습니다. 이전 리뷰에서 동일 이슈가 이미 지적되었습니다.

Copy link
Copy Markdown
Contributor

@nhyeonii nhyeonii left a comment

Choose a reason for hiding this comment

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

오호오호 ~~ 리뷰 한번만 확인해주세요 !!

val currentDay: Int,

@SerialName("progressPercentage")
val progressPercentage: Double, // 42.8
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.

Suggested change
val progressPercentage: Double, // 42.8
val progressPercentage: Double,

주석 다 지워주시오 ~

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.

앗 넵넵 !!!

Comment on lines +16 to +19
challengeDataSource
.getHomecareRoutineData()
.data!!
.map { it.toModel() }
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.

Suggested change
challengeDataSource
.getHomecareRoutineData()
.data!!
.map { it.toModel() }
challengeDataSource.getHomecareRoutineData().data!!.map { it.toModel() }

요정도는 한줄에 붙여서 작성해주면 좋을 거 가트 ~~

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.

고마워요 !!! 고쳤습니당 ~!!!


@Serializable
data object ChallengeMission : Route
fun NavController.navigateToChallengeStart(
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.

여기 줄 띄워주깅

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.

악 아직도 요런 실수를 하다니 !!!!!!!!!!!!

challengeRepository
.getChallengeRoutineData()
.onSuccess { responseModels ->
_uiState.value = UiState.Success(
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.

value 직접 접근 말고 update 사용해주세용

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.

넵 !!!! 요거 ,, 저 이거 완전 기억해요 ,,

Comment on lines +15 to +16
id = id,
routine = description
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.

Suggested change
id = id,
routine = description
id = this.id,
routine = this.description

this 붙여주깅

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.

넵 !! 항상 친절한 설명감사합니다아 ~!!

@HiltViewModel
class HomeViewModel @Inject constructor(
private val homeRepository: HomeRepository
private val homeRepositoryImpl: HomeRepositoryImpl
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.

이거 왜 Impl을 주입받지..?

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.

요것도 일단 수정해둘게용 !!!

_uiState.update { UiState.Loading }

homeRepository.getMainDashboard().onSuccess { response ->
homeRepositoryImpl.getMainDashboard().onSuccess { response ->
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.

여기도 왜 Impl을 주입받으까용..?

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.

이것두 수정하겠습니다 !!

Copy link
Copy Markdown
Contributor

@usuuhyn usuuhyn left a comment

Choose a reason for hiding this comment

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

수고하셧습니다! 혜미니 언니들 리뷰 반영.. 화이팅!!!!
소히 언니 어푸까지 기다려주시와요

7311950465033cb505469dc705829913

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

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/cherrish/android/presentation/challenge/start/ChallengeStartScreen.kt (1)

45-50: Scaffold paddingValues를 먼저 적용하세요

현재는 수평 패딩이 먼저 적용돼 시스템 인셋이 의도대로 반영되지 않을 수 있습니다. 루트에 paddingValues를 우선 적용한 뒤 추가 패딩을 주는 패턴으로 정리해주세요.

♻️ 제안 수정
     Column(
         modifier = modifier
             .fillMaxSize()
-            .padding(horizontal = 17.dp)
-            .padding(paddingValues)
+            .padding(paddingValues)
+            .padding(horizontal = 17.dp)

Based on learnings, apply paddingValues before any additional padding.

♻️ Duplicate comments (2)
app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt (1)

38-51: API 실패 시 에러 처리가 누락되었습니다.

getChallengeRoutineData() 호출에서 onSuccess만 처리하고 onFailure가 없습니다. API 호출 실패 시 _uiStateUiState.Loading 상태에 머물러 사용자에게 피드백이 제공되지 않습니다.

🐛 제안하는 수정사항
     private fun loadRoutines() {
         viewModelScope.launch {
             challengeRepository.getChallengeRoutineData()
                 .onSuccess { responseModels ->
                     _uiState.update {
                         UiState.Success(
                             ChallengeRoutineUiState(
                                 routines = responseModels.map { it.toUiModel() }.toPersistentList()
                             )
                         )
                     }
                 }
+                .onFailure { throwable ->
+                    _uiState.update { UiState.Failure(throwable) }
+                }
         }
     }
app/src/main/java/com/cherrish/android/presentation/challenge/start/ChallengeStartScreen.kt (1)

27-36: 사용하지 않는 ViewModel 주입 정리 여부 확인

ChallengeStartRoute에서 viewModel이 사용되지 않아 불필요한 인스턴스 생성이 발생할 수 있습니다. 필요 없다면 파라미터/주입 제거 또는 side-effect 수집 로직 추가를 검토해주세요.

🧹 Nitpick comments (3)
app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineScreen.kt (2)

84-90: paddingValues 파라미터가 사용되지 않고 있습니다.

ChallengeRoutineScreenpaddingValues를 파라미터로 받지만 실제로 root container에 적용하지 않습니다. 학습된 패턴에 따르면, Scaffold에서 전달받은 paddingValues는 root container의 modifier에 먼저 적용해야 시스템 바와의 레이아웃 겹침을 방지할 수 있습니다.

현재 systemBarsPadding()navigationBarsPadding()을 함께 사용하면 중복 패딩이 발생할 수 있습니다.

♻️ 제안하는 수정사항
     Column(
         modifier = modifier
             .fillMaxSize()
             .background(CherrishTheme.colors.gray0)
-            .navigationBarsPadding()
-            .systemBarsPadding()
+            .padding(paddingValues)
     ) {

또는 Scaffold 없이 사용하는 경우라면, 사용하지 않는 paddingValues 파라미터를 제거하는 것을 고려해주세요.

Based on learnings, paddingValues는 root container에 먼저 적용되어야 합니다.


50-56: Loading 및 Failure 상태에 대한 UI 처리가 누락되었습니다.

현재 UiState.LoadingUiState.Failure 상태에서 빈 블록으로 처리되어 사용자에게 아무런 피드백이 제공되지 않습니다. 로딩 인디케이터나 에러 메시지를 표시하는 것을 권장합니다.

♻️ 제안 예시
is UiState.Loading -> {
    // 로딩 인디케이터 표시
    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        CircularProgressIndicator()
    }
}

is UiState.Failure -> {
    // 에러 메시지 또는 재시도 버튼 표시
}
app/src/main/java/com/cherrish/android/presentation/challenge/routine/ChallengeRoutineViewModel.kt (1)

71-74: 사용되지 않는 빈 함수들을 제거해주세요.

onBackClick()onCloseClick()은 빈 구현이며, ChallengeRoutineRoute에서 직접 파라미터로 전달받은 콜백을 사용하고 있어 ViewModel의 이 함수들은 호출되지 않습니다. 불필요한 코드는 제거하는 것이 좋습니다.

♻️ 제안하는 수정사항
-    fun onBackClick() {
-    }
-
-    fun onCloseClick() {}

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

Labels

FEAT✨ 새로운 기능 구현 혜민🍒 혜민 담당

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 챌린지 홈케어 루틴 목록 조회 API 연결

4 participants