Conversation
Walkthrough홈 화면 API 연동을 위해 원격 서비스, DTO, 데이터소스, 리포지토리, 모델을 추가하고 ViewModel에 로드 및 사이드 이펙트 로직을 통합했으며, UI 컴포넌트와 일부 유틸/디자인 타입을 이에 맞춰 변경했습니다. (새 로딩 UI와 네비게이션 콜백 포함) Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant HomeScreen
participant HomeViewModel
participant HomeRepository
participant HomeDataSource
participant HomeService
participant API
User->>HomeScreen: 화면 로드
HomeScreen->>HomeViewModel: uiState/sideEffect 구독
Note over HomeViewModel: init -> loadHomeData()
HomeViewModel->>HomeViewModel: emit UiState.Loading
HomeViewModel->>HomeRepository: getMainDashboard()
HomeRepository->>HomeDataSource: getMainDashboard()
HomeDataSource->>HomeService: getMainDashboard()
HomeService->>API: GET api/main-dashboard
API-->>HomeService: BaseResponse<HomeResponseDto>
HomeService-->>HomeDataSource: BaseResponse<HomeResponseDto>
HomeDataSource-->>HomeRepository: BaseResponse<HomeResponseDto>
HomeRepository->>HomeRepository: DTO -> Model 매핑 (toModel)
HomeRepository-->>HomeViewModel: Result<HomeResponseModel>
HomeViewModel->>HomeViewModel: emit UiState.Success(HomeUiState)
HomeScreen->>HomeScreen: UI 렌더링(로딩/데이터)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
| ) { | ||
| val uiState by viewModel.uiState.collectAsStateWithLifecycle() | ||
|
|
||
| viewModel.sideEffect.collectLatestSideEffect { sideEffect -> |
|
|
||
| private fun loadHomeData() { | ||
| viewModelScope.launch { | ||
| _uiState.value = UiState.Loading |
| ) | ||
| ) | ||
| } | ||
| }.onFailure {} |
There was a problem hiding this comment.
Actionable comments posted: 3
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/home/component/PlanBoxSection.kt (1)
115-118: 확장 리스트의 key 중복 가능성확장 영역은 동일한 값의 항목이 있으면 key가 중복되어 상태 재사용/애니메이션 꼬임이 생길 수 있습니다. 안정적인 ID(가능하다면 procedureId)나 index를 포함하세요.
🛠️ 수정 제안
- plans.drop(previewCount).forEachIndexed { _, plan -> + plans.drop(previewCount).forEachIndexed { index, plan -> key( - "${plan.procedureName}-${plan.daysSince}-${plan.downtimePhase}" + "${plan.procedureName}-${plan.daysSince}-${plan.downtimePhase}-$index" ) {
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt`:
- Around line 16-23: The displayed weekday lacks the "요일" suffix because
DayOfWeek.toKoreanName() returns the short form; update
HomeResponseModel.toTodayDateString() so the returned string appends "요일" to the
short name. Specifically, locate the toTodayDateString function in
HomeResponseModel and change the formatted return from using (${dayKor}) to
(${dayKor}요일) so the output becomes "YYYY년 M월 D일 (월요일)". Ensure you only modify
the string assembly and keep the DayOfWeek.toKoreanName() extension unchanged.
In `@app/src/main/java/com/cherrish/android/data/model/HomeResponseModel.kt`:
- Around line 61-64: The current HomeResponseModel.toTodayDateString uses
LocalDate.parse(date) and DayOfWeek.valueOf(dayOfWeek) which will throw on
unexpected server formats; update the function to perform safe parsing: try
parsing date with a primary DateTimeFormatter and if that fails try one or two
fallback formatters (or wrap in try/catch) and default to LocalDate.now() if all
fail, and for dayOfWeek attempt DayOfWeek.valueOf(dayOfWeek) guarded by a
try/catch or by mapping common localized strings to DayOfWeek with a fallback of
the parsed date's day-of-week or an empty/placeholder string; ensure the
function always returns a formatted string (e.g., "<year>년 <month>월 <day>일
(<dayKor>)") without throwing and reference the
HomeResponseModel.toTodayDateString method, LocalDate.parse / DateTimeFormatter
and DayOfWeek.valueOf when making the changes.
In `@app/src/main/java/com/cherrish/android/presentation/home/HomeViewModel.kt`:
- Around line 42-57: The onLogFailure handler for
homeRepository.getMainDashboard does not update _uiState on error, causing the
UI to remain stuck in loading; modify the failure path inside the callback
attached to homeRepository.getMainDashboard (the chain using .onSuccess { ...
}.onLogFailure { ... }) to call _uiState.update and set UiState.Failure (include
the throwable or error message) instead of leaving it empty, so that UiState
transitions out of loading when getMainDashboard fails.
🧹 Nitpick comments (5)
app/src/main/java/com/cherrish/android/presentation/home/type/DowntimePhase.kt (1)
11-13: Silent fallback 동작에 대한 고려
runCatching을 사용한 안전한 enum 변환은 좋습니다. 다만, 서버에서 예상치 못한 값이 전달될 경우 조용히SENSITIVE로 처리되어 디버깅이 어려울 수 있습니다.개발/디버깅 용도로 로깅 추가를 고려해보세요:
💡 디버깅을 위한 로깅 추가 제안
fun String.toDowntimePhase(): DowntimePhase = runCatching { DowntimePhase.valueOf(this) } - .getOrElse { DowntimePhase.SENSITIVE } + .getOrElse { + timber.log.Timber.w("Unknown DowntimePhase value: $this, falling back to SENSITIVE") + DowntimePhase.SENSITIVE + }app/src/main/java/com/cherrish/android/data/repositoryimpl/HomeRepositoryImpl.kt (1)
14-15:data!!는 NPE로 실패 원인 파악이 어려움Line 14-15에서
data!!가 null이면 NPE로 실패합니다.Result.failure로 포장되긴 하지만, 원인 메시지가 부정확해 디버깅이 어려워집니다. 명시적인 예외로 바꾸는 편이 안전합니다.🛠️ 제안 수정
override suspend fun getMainDashboard(): Result<HomeResponseModel> = suspendRunCatching { - homeDataSource.getMainDashboard().data!!.toModel() + val dto = homeDataSource.getMainDashboard().data + ?: throw IllegalStateException("HomeResponseDto is null") + dto.toModel() }app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt (2)
81-104: paddingValues를 루트 컨테이너에 먼저 적용현재는 LazyColumn에만 paddingValues가 적용되어 배경/레이아웃이 시스템 바와 겹칠 수 있습니다. 루트 Box에 먼저 적용하고 LazyColumn의 paddingValues는 제거/조정하는 패턴이 안전합니다. Based on learnings, 일관된 인셋 처리를 위해 루트에 먼저 적용하는 패턴을 권장합니다.
♻️ 수정 예시
- Box( - modifier = modifier - .fillMaxSize() - .background(color = CherrishTheme.colors.graEnd) - ) { + Box( + modifier = modifier + .fillMaxSize() + .padding(paddingValues) + .background(color = CherrishTheme.colors.graEnd) + ) { ... - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues), + LazyColumn( + modifier = Modifier.fillMaxSize(),
141-149: 로딩 화면도 인셋을 반영하도록 개선로딩 상태에서도 시스템 바 겹침을 피하려면 LoadingScreen이 paddingValues를 받아 루트에 먼저 적용하는 쪽이 좋습니다. Based on learnings, 스크린 루트에 인셋을 우선 적용하는 규칙을 동일하게 유지하세요.
♻️ 수정 예시
-private fun LoadingScreen() { +private fun LoadingScreen( + paddingValues: PaddingValues +) { Column( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .background(color = CherrishTheme.colors.gray0),app/src/main/java/com/cherrish/android/presentation/home/HomeViewModel.kt (1)
46-54: selectedIndex 범위 안전성 확인 필요
selectedIndex = response.cherryLevel이 게이지 리스트의 0‑based 인덱스 범위를 벗어나면 UI에서 바로 크래시가 납니다(예: uiState.gauges[uiState.selectedIndex]). 서버 스펙이 0‑based인지 확인하고, 안전하게 보정하는 방안을 검토해 주세요.♻️ 보정 예시
- selectedIndex = response.cherryLevel + selectedIndex = response.cherryLevel + .coerceIn(0, CherrishGaugeType.entries.lastIndex)
| fun DayOfWeek.toKoreanName(): String = when (this) { | ||
| DayOfWeek.MONDAY -> "월요일" | ||
| DayOfWeek.TUESDAY -> "화요일" | ||
| DayOfWeek.WEDNESDAY -> "수요일" | ||
| DayOfWeek.THURSDAY -> "목요일" | ||
| DayOfWeek.FRIDAY -> "금요일" | ||
| DayOfWeek.SATURDAY -> "토요일" | ||
| DayOfWeek.SUNDAY -> "일요일" | ||
| DayOfWeek.MONDAY -> "월" | ||
| DayOfWeek.TUESDAY -> "화" | ||
| DayOfWeek.WEDNESDAY -> "수" | ||
| DayOfWeek.THURSDAY -> "목" | ||
| DayOfWeek.FRIDAY -> "금" | ||
| DayOfWeek.SATURDAY -> "토" | ||
| DayOfWeek.SUNDAY -> "일" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Step 1: Find the CalendarCalculator.kt file and examine the function
echo "=== Finding CalendarCalculator.kt ==="
find . -name "CalendarCalculator.kt" -type f
# Step 2: View the complete implementation
echo -e "\n=== Viewing CalendarCalculator.kt ==="
cat app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt
# Step 3: Find all usages of toKoreanName
echo -e "\n=== Finding all toKoreanName() usages ==="
rg -n --type=kt 'toKoreanName\s*\(' -A 3 -B 1Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 1023
🏁 Script executed:
#!/bin/bash
# Step 1: Search for all toKoreanName usages without the problematic -t flag
echo "=== Finding all toKoreanName() usages ==="
rg -n 'toKoreanName\s*\(' -A 3 -B 1
# Step 2: Also search for potential direct usage of DayOfWeek formatting
echo -e "\n=== Checking for other DayOfWeek references ==="
rg -n 'dayOfWeek' -A 2 -B 1 | head -80Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 5670
🏁 Script executed:
#!/bin/bash
# Verify the exact HomeResponseModel.kt implementation
echo "=== Viewing HomeResponseModel.kt context ==="
sed -n '60,65p' app/src/main/java/com/cherrish/android/data/model/HomeResponseModel.kt
# Check if there are any other toKoreanName usages we might have missed
echo -e "\n=== Complete toKoreanName usage audit ==="
rg -n 'toKoreanName' --no-headingRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 925
HomeResponseModel.kt의 표시 형식 수정 필요
toKoreanName()이 약칭을 반환하도록 변경되었으나, HomeResponseModel.kt 63번 줄의 toTodayDateString() 함수에서는 "요일" 없이 약칭만 표시됩니다. 현재 형식 "(월)" 대신 "(월요일)"로 표시되도록 수정이 필요합니다.
// 현재
return "${localDate.year}년 ${localDate.monthValue}월 ${localDate.dayOfMonth}일 ($dayKor)"
// 수정 필요
return "${localDate.year}년 ${localDate.monthValue}월 ${localDate.dayOfMonth}일 (${dayKor}요일)"🤖 Prompt for AI Agents
In `@app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt`
around lines 16 - 23, The displayed weekday lacks the "요일" suffix because
DayOfWeek.toKoreanName() returns the short form; update
HomeResponseModel.toTodayDateString() so the returned string appends "요일" to the
short name. Specifically, locate the toTodayDateString function in
HomeResponseModel and change the formatted return from using (${dayKor}) to
(${dayKor}요일) so the output becomes "YYYY년 M월 D일 (월요일)". Ensure you only modify
the string assembly and keep the DayOfWeek.toKoreanName() extension unchanged.
| fun HomeResponseModel.toTodayDateString(): String { | ||
| val localDate = LocalDate.parse(date) | ||
| val dayKor = DayOfWeek.valueOf(dayOfWeek).toKoreanName() | ||
| return "${localDate.year}년 ${localDate.monthValue}월 ${localDate.dayOfMonth}일 ($dayKor)" |
There was a problem hiding this comment.
날짜/요일 파싱 실패 시 런타임 예외 가능
Line 61-64에서 LocalDate.parse / DayOfWeek.valueOf가 서버 포맷 변경(예: 2026.01.19, Monday, 월)에 즉시 예외를 던질 수 있습니다. UI 표시 함수라서 크래시로 이어질 수 있으니 안전한 파싱과 폴백이 필요합니다.
🛠️ 제안 수정
fun HomeResponseModel.toTodayDateString(): String {
- val localDate = LocalDate.parse(date)
- val dayKor = DayOfWeek.valueOf(dayOfWeek).toKoreanName()
- return "${localDate.year}년 ${localDate.monthValue}월 ${localDate.dayOfMonth}일 ($dayKor)"
+ val localDate = runCatching { LocalDate.parse(date.trim()) }.getOrNull()
+ val dayKor = runCatching { DayOfWeek.valueOf(dayOfWeek.trim().uppercase()).toKoreanName() }.getOrNull()
+ return if (localDate != null && dayKor != null) {
+ "${localDate.year}년 ${localDate.monthValue}월 ${localDate.dayOfMonth}일 ($dayKor)"
+ } else {
+ "${date} (${dayOfWeek})"
+ }
}🤖 Prompt for AI Agents
In `@app/src/main/java/com/cherrish/android/data/model/HomeResponseModel.kt`
around lines 61 - 64, The current HomeResponseModel.toTodayDateString uses
LocalDate.parse(date) and DayOfWeek.valueOf(dayOfWeek) which will throw on
unexpected server formats; update the function to perform safe parsing: try
parsing date with a primary DateTimeFormatter and if that fails try one or two
fallback formatters (or wrap in try/catch) and default to LocalDate.now() if all
fail, and for dayOfWeek attempt DayOfWeek.valueOf(dayOfWeek) guarded by a
try/catch or by mapping common localized strings to DayOfWeek with a fallback of
the parsed date's day-of-week or an empty/placeholder string; ensure the
function always returns a formatted string (e.g., "<year>년 <month>월 <day>일
(<dayKor>)") without throwing and reference the
HomeResponseModel.toTodayDateString method, LocalDate.parse / DateTimeFormatter
and DayOfWeek.valueOf when making the changes.
| homeRepository.getMainDashboard().onSuccess { response -> | ||
| _uiState.update { | ||
| UiState.Success( | ||
| HomeUiState( | ||
| currentStep = response.cherryLevel, | ||
| gauges = CherrishGaugeType.entries.toImmutableList(), | ||
| challengeRate = response.challengeRate, | ||
| challengeName = response.challengeName, | ||
| todayDate = response.toTodayDateString(), | ||
| plans = response.recentProcedures.toImmutableList(), | ||
| upcomingPlans = response.upcomingProcedures.toImmutableList(), | ||
| selectedIndex = response.cherryLevel | ||
| ) | ||
| ) | ||
| } | ||
| }.onLogFailure {} |
There was a problem hiding this comment.
실패 시 Loading에서 빠져나오지 못함
실패 경로에서 UiState.Failure로 전환하지 않아 로딩이 무한 지속될 수 있습니다. 실패 상태를 명시적으로 업데이트하세요.
🛠️ 수정 제안
- homeRepository.getMainDashboard().onSuccess { response ->
+ homeRepository.getMainDashboard().onSuccess { response ->
_uiState.update {
UiState.Success(
HomeUiState(
@@
)
)
}
- }.onLogFailure {}
+ }.onLogFailure { throwable ->
+ _uiState.update { UiState.Failure(throwable) }
+ }🤖 Prompt for AI Agents
In `@app/src/main/java/com/cherrish/android/presentation/home/HomeViewModel.kt`
around lines 42 - 57, The onLogFailure handler for
homeRepository.getMainDashboard does not update _uiState on error, causing the
UI to remain stuck in loading; modify the failure path inside the callback
attached to homeRepository.getMainDashboard (the chain using .onSuccess { ...
}.onLogFailure { ... }) to call _uiState.update and set UiState.Failure (include
the throwable or error message) instead of leaving it empty, so that UiState
transitions out of loading when getMainDashboard fails.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt`:
- Around line 28-31: homeNavGraph currently passes an empty lambda into
HomeRoute so HomeSideEffect.NavigateToChallenge never triggers navigation;
update the homeNavGraph signature to accept a navigateToChallenge callback (same
type used in other graphs), thread that parameter into the HomeRoute call
(replace the empty {} with navigateToChallenge), and ensure the caller (e.g.,
MainScreen or the NavHost setup) provides the real navigation implementation so
HomeSideEffect.NavigateToChallenge will invoke it.
| HomeRoute( | ||
| paddingValues = paddingValues | ||
| paddingValues = paddingValues, | ||
| navigateToChallenge = {} | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# homeNavGraph 호출부 확인
rg -nP '\bhomeNavGraph\s*\(' --type kt
# navigateToChallenge 사용처 확인
rg -nP '\bnavigateToChallenge\b' --type ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 127
🏁 Script executed:
#!/bin/bash
# homeNavGraph 호출부 확인
rg -nP 'homeNavGraph\s*\('
# navigateToChallenge 사용처 확인
rg -nP 'navigateToChallenge' app/src/main/java/com/cherrish/android/presentation
# HomeNavigator.kt 파일 확인
cat -n app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 2220
🏁 Script executed:
#!/bin/bash
# MainScreen.kt에서 homeNavGraph 호출 부분 확인
sed -n '50,70p' app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt
# MainScreen.kt 전체 구조 확인
head -80 app/src/main/java/com/cherrish/android/presentation/main/MainScreen.kt
# HomeScreen 구현 확인
cat -n app/src/main/java/com/cherrish/android/presentation/home/HomeScreen.kt | head -60Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 6389
homeNavGraph에 navigateToChallenge 콜백 매개변수 추가 필요
HomeRoute에서 HomeSideEffect.NavigateToChallenge를 발생시켜도 빈 람다로 인해 네비게이션이 실행되지 않습니다. 다른 네비게이션 그래프들(onboardingNavGraph, calendarNavGraph 등)처럼 homeNavGraph도 콜백을 받아 전달해야 합니다.
수정안
fun NavGraphBuilder.homeNavGraph(
- paddingValues: PaddingValues
+ paddingValues: PaddingValues,
+ navigateToChallenge: () -> Unit
) {
composable<Home> {
HomeRoute(
paddingValues = paddingValues,
- navigateToChallenge = {}
+ navigateToChallenge = navigateToChallenge
)
}
}MainScreen.kt 호출부:
- homeNavGraph(paddingValues = innerPadding)
+ homeNavGraph(
+ paddingValues = innerPadding,
+ navigateToChallenge = appState::navigateToChallenge
+ )🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/home/navigation/HomeNavigator.kt`
around lines 28 - 31, homeNavGraph currently passes an empty lambda into
HomeRoute so HomeSideEffect.NavigateToChallenge never triggers navigation;
update the homeNavGraph signature to accept a navigateToChallenge callback (same
type used in other graphs), thread that parameter into the HomeRoute call
(replace the empty {} with navigateToChallenge), and ensure the caller (e.g.,
MainScreen or the NavHost setup) provides the real navigation implementation so
HomeSideEffect.NavigateToChallenge will invoke it.

Related issue 🛠
Work Description ✏️
Screenshot 📸
Uncompleted Tasks 😅
To Reviewers 📢
네비 연결은 새 이슈에서 하겠습니다~!!
Summary by CodeRabbit
새로운 기능
개선 사항
✏️ Tip: You can customize this high-level summary in your review settings.