Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
Walkthrough원격 시술/고민 API 연동을 위해 DTO·서비스·데이터소스·레포지토리 계층을 추가하고, 캘린더-시술 네비게이션에 startDate 전달 및 캘린더 새로고침 이벤트 버스를 도입해 UI·뷰모델 흐름을 수정했습니다. (직렬화 유틸리티 및 여러 UI/상태/DI 변경 포함) Changes
Sequence Diagram(s)sequenceDiagram
participant UI as 시술 화면
participant VM as ProcedureViewModel
participant REPO as ProcedureRepository
participant DS as ProcedureDataSource
participant SVC as ProcedureService
participant API as 원격 API
UI->>VM: onSearchAction(query)
VM->>REPO: fetchProcedures(keyword, worryId)
REPO->>DS: getProcedures(keyword, worryId)
DS->>SVC: getProcedures(keyword, worryId)
SVC->>API: GET /api/procedures?keyword=...&worryId=...
API-->>SVC: BaseResponse<ProceduresResponseDto>
SVC-->>DS: BaseResponse<ProceduresResponseDto>
DS-->>REPO: BaseResponse<ProceduresResponseDto>
REPO->>REPO: toModel()
REPO-->>VM: Result<ProceduresResponseModel>
VM-->>UI: UI 상태 업데이트 (시술 목록)
sequenceDiagram
participant UI as 시술 화면
participant VM as ProcedureViewModel
participant REPO as UserProcedureRepository
participant DS as UserProcedureDataSource
participant SVC as UserProcedureService
participant API as 원격 API
participant BUS as CalendarRefreshEventBus
UI->>VM: onComplete()
VM->>VM: UserProceduresRequestModel 생성
VM->>REPO: addUserProcedures(request)
REPO->>DS: addUserProcedures(request.toDto())
DS->>SVC: addUserProcedures(requestDto)
SVC->>API: POST /api/user-procedures
API-->>SVC: BaseResponse<UserProceduresResponseDto>
SVC-->>DS: BaseResponse<UserProceduresResponseDto>
DS-->>REPO: BaseResponse<UserProceduresResponseDto>
REPO->>REPO: toModel()
REPO-->>VM: Result<UserProceduresResponseModel>
VM->>BUS: emit(RefreshRequired)
VM-->>UI: completeEvent (네비게이션)
sequenceDiagram
participant UI as 시술 화면
participant VM as ProcedureViewModel
participant REPO as WorryRepository
participant DS as WorryDataSource
participant SVC as WorryService
participant API as 원격 API
UI->>VM: 초기화
VM->>VM: fetchWorries()
VM->>REPO: getWorries()
REPO->>DS: getWorries()
DS->>SVC: getWorries()
SVC->>API: GET /api/worries
API-->>SVC: BaseResponse<List<WorryResponseDto>>
SVC-->>DS: BaseResponse<List<WorryResponseDto>>
DS-->>REPO: BaseResponse<List<WorryResponseDto>>
REPO->>REPO: toModel()
REPO-->>VM: Result<List<WorryModel>>
VM-->>UI: 고민 목록 표시
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 |
nhyeonii
left a comment
There was a problem hiding this comment.
우ㅇ옹 ~~ 넘 잘햇어요 리뷰 하번만 확인해죠요 !!!
| id = id, | ||
| name = name, | ||
| category = category?.takeIf { it.isNotBlank() } | ||
| ?: worries.map { it.content }.filter { it.isNotBlank() }.joinToString(" | "), |
There was a problem hiding this comment.
| id = id, | |
| name = name, | |
| category = category?.takeIf { it.isNotBlank() } | |
| ?: worries.map { it.content }.filter { it.isNotBlank() }.joinToString(" | "), | |
| id = this.id, | |
| name = this.name, | |
| category = this.category?.takeIf { it.isNotBlank() } | |
| ?: this.worries.map { it.content }.filter { it.isNotBlank() }.joinToString(" | "), |
P2: 요런식으로 작성해주는 거 어때요 ?! mapper / 변환 코드에서는 this를 붙이면 프로퍼티 출처가 명확해져서 가독성과 안전성이 좋아지는 것 같아용! 특히 id, name처럼 이름이 겹치기 쉬운 경우에 실수 방지도 댄답니닿ㅎㅎ
| minDowntimeDays = minDowntimeDays, | ||
| maxDowntimeDays = maxDowntimeDays |
There was a problem hiding this comment.
| minDowntimeDays = minDowntimeDays, | |
| maxDowntimeDays = maxDowntimeDays | |
| minDowntimeDays = this.minDowntimeDays, | |
| maxDowntimeDays = this.maxDowntimeDays |
| val downtimeDays: Int | ||
| ) | ||
|
|
||
| fun UserProceduresRequestModel.toDto(): UserProceduresRequestDto = |
| val recoveryTargetDate: LocalDate | ||
| ) | ||
|
|
||
| fun UserProceduresResponseDto.toModel(): UserProceduresResponseModel = |
| procedures = procedures.map { it.toModel() } | ||
| ) | ||
|
|
||
| fun CreatedUserProcedureDto.toModel(): CreatedUserProcedureModel = |
| fun refreshCalendar() { | ||
| val currentState = (_uiState.value as? UiState.Success)?.data | ||
| val selectedYearMonth = currentState?.selectedYearMonth ?: YearMonth.now() | ||
| val selectedDate = currentState?.selectedDate ?: LocalDate.now() | ||
|
|
||
| monthlyCache.clear() | ||
| dailyCache.clear() | ||
| loadMonthlyCalendar( | ||
| yearMonth = selectedYearMonth, | ||
| selectedDate = selectedDate | ||
| ) | ||
| } |
There was a problem hiding this comment.
| fun refreshCalendar() { | |
| val currentState = (_uiState.value as? UiState.Success)?.data | |
| val selectedYearMonth = currentState?.selectedYearMonth ?: YearMonth.now() | |
| val selectedDate = currentState?.selectedDate ?: LocalDate.now() | |
| monthlyCache.clear() | |
| dailyCache.clear() | |
| loadMonthlyCalendar( | |
| yearMonth = selectedYearMonth, | |
| selectedDate = selectedDate | |
| ) | |
| } | |
| fun refreshCalendar() { | |
| _uiState.updateSuccess { currentState -> | |
| monthlyCache.clear() | |
| dailyCache.clear() | |
| currentState.selectedDate?.let { | |
| loadMonthlyCalendar( | |
| yearMonth = currentState.selectedYearMonth, | |
| selectedDate = it | |
| ) | |
| } | |
| currentState | |
| } | |
| } |
P1 : 확장함수 사용해서 이렇게 작성하는건 어때요 ?!
| } | ||
|
|
||
| fun onAddButtonClick() { | ||
| val selectedDate = (_uiState.value as? UiState.Success)?.data?.selectedDate |
There was a problem hiding this comment.
P2 : onAddButtonClick이 함수
| val selectedDate = (_uiState.value as? UiState.Success)?.data?.selectedDate | |
| fun onAddButtonClick() { | |
| viewModelScope.launch { | |
| val selectedDate = when (val state = _uiState.value) { | |
| is UiState.Success -> state.data.selectedDate | |
| else -> LocalDate.now() | |
| } | |
| selectedDate?.let { _sideEffect.emit(CalendarSideEffect.NavigateToProcedure(it)) } | |
| } | |
| } |
이렇게 when으로 처리하면 가독성이 좋구 타입 캐스팅 명확해질 것 같은데 어때요잉 !?
| ) | ||
| } | ||
| } | ||
| .onFailure { e -> |
There was a problem hiding this comment.
| .onFailure { e -> | |
| .onLongFailure { e -> |
사용해쥬시용ㅎㅎㅎ
| _uiState.updateSuccess { ProcedureUiState.FakeNormal } | ||
| _completeEvent.tryEmit(Unit) | ||
| } | ||
| .onFailure { e -> |
There was a problem hiding this comment.
| .onFailure { e -> | |
| .onLogFailure { |
sohee6989
left a comment
There was a problem hiding this comment.
수고하셨습니다~!!
너무 복잡한 이걸 어떻게 구현하셨는지........
| procedures = procedures.map { it.toModel() } | ||
| ) | ||
|
|
||
| fun CreatedUserProcedureDto.toModel(): CreatedUserProcedureModel = |
There was a problem hiding this comment.
p3: (단순 궁금) 위에서는
procedures = procedures.map { UserProcedureItemDto(it.procedureId, it.downtimeDays) } 처럼 한 번에 매핑하셨는데
여기서는 Model을 2개로 나눠 각각 매퍼로 변환하신 이유가 있을까요?
There was a problem hiding this comment.
응답이 계층 구조라 상위/하위 모델로 나눠 매핑햇습니다!
| procedures = procedures.map { UserProcedureItemDto(it.procedureId, it.downtimeDays) } | ||
| ) | ||
|
|
||
| data class UserProceduresResponseModel( |
There was a problem hiding this comment.
p2: request/response 모델은 역할(입력/출력)과 변경 이유가 달라서
한 파일에 같이 두기보다 분리해두면 더 깔끔할 것 같아요ㅎㅎ
| color = CherrishTheme.colors.gray600 | ||
| ) | ||
|
|
||
| Spacer(modifier = Modifier.height(126.dp)) |
There was a problem hiding this comment.
p2: 이거 의미 없지 않나여?!
지금 화면 정렬을 상위에서 고정값으로 해주고있으니까요~!!
| item { | ||
| EmptySearchResult( | ||
| modifier = Modifier | ||
| .fillParentMaxSize() |
There was a problem hiding this comment.
p2: 이거 한줄로 써줘도 될듯?!합니다ㅎㅎ
|
|
||
| private fun ProcedureModel.toUiModel(): ProcedureCardItemUiModel = | ||
| ProcedureCardItemUiModel( | ||
| id = id, |
There was a problem hiding this comment.
p2: 여기도 위에 나현이 코리처러 this 써주면 좋을듯합니다!
| fun CalendarRoute( | ||
| paddingValues: PaddingValues, | ||
| onNavigateToProcedure: () -> Unit, | ||
| onNavigateToProcedure: (LocalDate) -> Unit, |
There was a problem hiding this comment.
p2: 수현씨 이거 네비이동 변수명 navigateTo~~로 통일해요오~!!
|
|
||
| @Serializable | ||
| private data object Procedure : Route | ||
| data class Procedure(val startDate: String) |
There was a problem hiding this comment.
p1: 이거 LocalDate로 해야되는거 아닌가여?!
다른 곳에서도 타입을 LocalDate로 쓰고 있어서요!
There was a problem hiding this comment.
이거는 나중에 이슈 파서 해결하겟습니다
| private val savedStateHandle: SavedStateHandle | ||
| ) : ViewModel() { | ||
| private val startDateArg = runCatching { | ||
| LocalDate.parse(savedStateHandle.toRoute<Procedure>().startDate) |
There was a problem hiding this comment.
p1: CalendarNavigator에서 코리 반영해주시면 이 부분도 따로 파싱이 필요 없지 않나여?!
There was a problem hiding this comment.
이것도 !! 나중에 이슈 파서 따로 해결할게요
| UiState.Success( | ||
| ProcedureUiState.FakeNormal.copy( | ||
| procedureItems = ProcedureUiState.FakeProcedureCardItems.procedureItems | ||
| procedureItems = persistentListOf(), |
There was a problem hiding this comment.
p1: 여기 fake 데이터 넣지말고 로딩으로 처리하는거 어떠세요??
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`:
- Around line 287-298: refreshCalendar currently clears monthlyCache/dailyCache
but only calls loadMonthlyCalendar when currentState.selectedDate is non-null,
leaving the UI stale if selectedDate is null; change the logic in
refreshCalendar (inside _uiState.updateSuccess) to compute a fallback date
(e.g., first day of currentState.selectedYearMonth or LocalDate.now() adjusted
to that month) when currentState.selectedDate is null and then call
loadMonthlyCalendar with yearMonth = currentState.selectedYearMonth and
selectedDate = fallbackDate so the UI always reloads; keep the cache clears
as-is and ensure you update only within the same updateSuccess block referencing
refreshCalendar, selectedDate, selectedYearMonth, and loadMonthlyCalendar.
- Around line 277-284: onAddButtonClick에서 _uiState 확인 시 UiState.Success일 때
state.data.selectedDate가 null이면 네비게이션이 스킵되므로, UiState.Success 케이스에서도
selectedDate가 null이면 LocalDate.now() 같은 기본값을 사용하도록 처리하세요; 구체적으로
onAddButtonClick의 when 분기(UiState.Success)에서 selectedDate를 안전하게 널 병합하거나 기본값으로
대체한 뒤 _sideEffect.emit(CalendarSideEffect.NavigateToProcedure(...))를 항상 호출하도록
변경하세요.
| fun onAddButtonClick() { | ||
| viewModelScope.launch { | ||
| _sideEffect.emit(CalendarSideEffect.NavigateToProcedure) | ||
| val selectedDate = when (val state = _uiState.value) { | ||
| is UiState.Success -> state.data.selectedDate | ||
| else -> LocalDate.now() | ||
| } | ||
| selectedDate?.let { _sideEffect.emit(CalendarSideEffect.NavigateToProcedure(it)) } | ||
| } |
There was a problem hiding this comment.
Success 상태에서 selectedDate가 null이면 네비게이션이 누락됩니다.
Success지만 selectedDate가 null인 경우 emit이 스킵되어 버튼이 무반응이 될 수 있습니다. null인 경우에도 기본 날짜로 이동하도록 보완하는 게 안전합니다.
✅ 제안 수정
- viewModelScope.launch {
- val selectedDate = when (val state = _uiState.value) {
- is UiState.Success -> state.data.selectedDate
- else -> LocalDate.now()
- }
- selectedDate?.let { _sideEffect.emit(CalendarSideEffect.NavigateToProcedure(it)) }
- }
+ viewModelScope.launch {
+ val selectedDate = when (val state = _uiState.value) {
+ is UiState.Success -> state.data.selectedDate ?: LocalDate.now()
+ else -> LocalDate.now()
+ }
+ _sideEffect.emit(CalendarSideEffect.NavigateToProcedure(selectedDate))
+ }🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`
around lines 277 - 284, onAddButtonClick에서 _uiState 확인 시 UiState.Success일 때
state.data.selectedDate가 null이면 네비게이션이 스킵되므로, UiState.Success 케이스에서도
selectedDate가 null이면 LocalDate.now() 같은 기본값을 사용하도록 처리하세요; 구체적으로
onAddButtonClick의 when 분기(UiState.Success)에서 selectedDate를 안전하게 널 병합하거나 기본값으로
대체한 뒤 _sideEffect.emit(CalendarSideEffect.NavigateToProcedure(...))를 항상 호출하도록
변경하세요.
| fun refreshCalendar() { | ||
| _uiState.updateSuccess { currentState -> | ||
| monthlyCache.clear() | ||
| dailyCache.clear() | ||
| currentState.selectedDate?.let { | ||
| loadMonthlyCalendar( | ||
| yearMonth = currentState.selectedYearMonth, | ||
| selectedDate = it | ||
| ) | ||
| } | ||
| currentState | ||
| } |
There was a problem hiding this comment.
selectedDate가 null이면 캐시만 비우고 재로딩이 되지 않습니다.
리프레시 이벤트 시 선택 날짜가 null이면 loadMonthlyCalendar가 호출되지 않아 UI가 최신화되지 않을 수 있습니다. 선택 날짜가 없을 때는 현재 월의 첫째 날 등으로 보완해 주세요.
✅ 제안 수정
fun refreshCalendar() {
_uiState.updateSuccess { currentState ->
monthlyCache.clear()
dailyCache.clear()
- currentState.selectedDate?.let {
- loadMonthlyCalendar(
- yearMonth = currentState.selectedYearMonth,
- selectedDate = it
- )
- }
+ val yearMonth = currentState.selectedYearMonth
+ val selectedDate = currentState.selectedDate ?: yearMonth.atDay(1)
+ loadMonthlyCalendar(
+ yearMonth = yearMonth,
+ selectedDate = selectedDate
+ )
currentState
}
}🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`
around lines 287 - 298, refreshCalendar currently clears monthlyCache/dailyCache
but only calls loadMonthlyCalendar when currentState.selectedDate is non-null,
leaving the UI stale if selectedDate is null; change the logic in
refreshCalendar (inside _uiState.updateSuccess) to compute a fallback date
(e.g., first day of currentState.selectedYearMonth or LocalDate.now() adjusted
to that month) when currentState.selectedDate is null and then call
loadMonthlyCalendar with yearMonth = currentState.selectedYearMonth and
selectedDate = fallbackDate so the UI always reloads; keep the cache clears
as-is and ensure you update only within the same updateSuccess block referencing
refreshCalendar, selectedDate, selectedYearMonth, and loadMonthlyCalendar.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/navigation/CalendarNavigator.kt`:
- Around line 19-25: Procedure uses `@Contextual` on startDate but no
serializersModule registering a LocalDate serializer exists, causing
MissingSerializerException when navigateToProcedure is called; fix by either
adding a serializersModule to your Json in NetworkModule that registers
LocalDateSerializer (registerLocalDate/Contextual(LocalDate::class,
LocalDateSerializer)) so `@Contextual` works, or change Procedure.startDate to use
explicit serialization via `@Serializable`(with = LocalDateSerializer::class) to
match other DTOs (update Procedure declaration and imports accordingly).
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt`:
- Around line 37-45: ProcedureViewModel의 startDateArg 초기화에서
savedStateHandle.get<String>("startDate")가 null이거나 포맷이 맞지 않으면 LocalDate.parse에서
NPE/예외가 발생하므로, savedStateHandle에서 값을 안전하게 읽어와(예:
savedStateHandle.get<String>("startDate")?.let { ... }) 명시적
포맷(DateTimeFormatter.ISO_LOCAL_DATE)으로 파싱하고 실패 시 기본값을 사용하거나(예: LocalDate.now())
명확한 IllegalArgumentException을 던지도록 변경해 startDateArg 초기화를 안전하게 만드세요; 대상 식별자:
ProcedureViewModel, startDateArg, savedStateHandle, LocalDate.parse.
♻️ Duplicate comments (2)
app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt (2)
346-379: 등록 실패 시 사용자 피드백이 없습니다.
onLogFailure블록이 비어 있어 실패 시 UI가 조용히 실패합니다. 에러 상태 전파 또는 실패 이벤트 발행이 필요합니다.
502-508: 광범위 예외 삼킴으로 디버깅이 어려워집니다.의도된 예외만 처리하도록 범위를 좁히는 편이 안전합니다.
🛠️ 개선 예시
private fun ProcedureUiState.recoveryTargetDateOrNull(): LocalDate? { return try { if (year.isBlank() || month.isBlank() || day.isBlank()) return null LocalDate.of(year.toInt(), month.toInt(), day.toInt()) - } catch (e: Exception) { + } catch (e: java.time.DateTimeException) { + null + } catch (e: NumberFormatException) { null } }
…rish/Cherrish-Android into feat/#90-procedure-api
There was a problem hiding this comment.
Actionable comments posted: 2
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/calendar/procedure/ProcedureViewModel.kt (1)
225-233: 새로 선택한 카드가 항상 맨 앞으로 이동합니다 — UX 의도 확인 필요.
persistentListOf(cardId) + currentList로 인해 선택 순서가 변경되며, 이는 DowntimeContent의 카드 표시 순서와 최종 제출 순서에도 영향을 미칩니다. 의도된 UX가 아니라면 선택 순서를 유지하거나 기존 리스트의 맨 앞이 아닌 다른 위치에 추가하도록 조정해 주세요.
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt`:
- Around line 76-107: The onLogFailure block in fetchWorries() is empty which
leaves _uiState stuck in UiState.Loading on failure; update the onLogFailure
handler to set a fallback UI state (e.g., _uiState.value =
UiState.Success(ProcedureUiState(worries = ProcedureUiState.FakeNormal.worries,
startDay = startDateArg)) or set an error state like UiState.Error) and log the
exception; modify the onLogFailure in fetchWorries() to reference _uiState,
UiState.Success/UiState.Error, ProcedureUiState.FakeNormal.worries, and the
caught exception so the UI always progresses on repository failure.
- Around line 47-49: The current startDateArg initializer swallows parse errors
and silently falls back to LocalDate.now(); update it to surface failures by
invoking the existing logging helper and preserve a safe fallback: wrap the
parse in runCatching, call onLogFailure on the caught exception with a clear
message referencing the route value (use
savedStateHandle.toRoute<Procedure>().startDate to include the raw input), and
then return the fallback LocalDate.now() only after logging; change the
expression around startDateArg so the failure is logged (e.g., runCatching {
LocalDate.parse(...) }.onFailure { it.onLogFailure("Failed to parse
Procedure.startDate: ...") }.getOrElse { LocalDate.now() }).
♻️ Duplicate comments (2)
app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt (2)
354-385: 시술 등록 실패 처리 부재(중복 이슈).이 구간의 실패 처리 부재는 이전 리뷰에서 이미 지적된 동일 이슈입니다.
507-513: 예외 삼키기 범위가 너무 넓습니다(중복 이슈).
Exception전체를 잡아 무시하고 있어 디버깅이 어렵습니다. 이전 리뷰에서 제안한 대로DateTimeException/NumberFormatException등 구체 예외로 좁혀 주세요.
| private val startDateArg = runCatching { | ||
| LocalDate.parse(savedStateHandle.toRoute<Procedure>().startDate) | ||
| }.getOrElse { LocalDate.now() } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt | head -80Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 4010
🏁 Script executed:
wc -l app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 170
🏁 Script executed:
# Search for error handling and logging patterns in the ViewModel
rg "getOrElse|getOrNull|runCatching|Timber|Log" app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt -A 2Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 689
🏁 Script executed:
# Check if there's an error state or failure handling in UiState
rg "UiState|Error|Failure" app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt -B 2 -A 2Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 2539
startDate 파싱 실패 시 무음 처리는 잘못된 일정으로 이어질 수 있습니다.
현재 예외를 삼키고 LocalDate.now()로 대체되어, 유효하지 않은 route 값이 조용히 "오늘"로 반영됩니다. 코드베이스의 다른 부분에서는 onLogFailure 확장 함수를 사용하여 실패를 기록하고 있는데, 이 부분에서만 무음 처리되고 있습니다. 최소한 로그를 통해 파싱 실패를 표면화하거나, 명시적으로 에러를 처리하는 것이 좋습니다.
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt`
around lines 47 - 49, The current startDateArg initializer swallows parse errors
and silently falls back to LocalDate.now(); update it to surface failures by
invoking the existing logging helper and preserve a safe fallback: wrap the
parse in runCatching, call onLogFailure on the caught exception with a clear
message referencing the route value (use
savedStateHandle.toRoute<Procedure>().startDate to include the raw input), and
then return the fallback LocalDate.now() only after logging; change the
expression around startDateArg so the failure is logged (e.g., runCatching {
LocalDate.parse(...) }.onFailure { it.onLogFailure("Failed to parse
Procedure.startDate: ...") }.getOrElse { LocalDate.now() }).
| fun fetchWorries() { | ||
| viewModelScope.launch { | ||
| worryRepository.getWorries() | ||
| .onSuccess { worries -> | ||
|
|
||
| val mapped = worries | ||
| .map { ProcedureWorryUiModel(id = it.id, content = it.content) } | ||
| .toPersistentList() | ||
|
|
||
| val worriesToUse = if (mapped.isEmpty()) { | ||
| ProcedureUiState.FakeNormal.worries | ||
| } else { | ||
| mapped | ||
| } | ||
|
|
||
| val currentState = _uiState.value | ||
| if (currentState is UiState.Success) { | ||
| _uiState.value = currentState.copy( | ||
| data = currentState.data.copy(worries = worriesToUse) | ||
| ) | ||
| } else { | ||
| _uiState.value = UiState.Success( | ||
| ProcedureUiState( | ||
| worries = worriesToUse, | ||
| startDay = startDateArg | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
| .onLogFailure { e -> | ||
| } | ||
| } |
There was a problem hiding this comment.
worries 로딩 실패 시 UI가 Loading에 고정될 수 있습니다.
onLogFailure가 비어 있어 초기 UiState.Loading에서 벗어나지 못할 수 있습니다. 실패 시에도 UI가 진행되도록 최소한 fallback/에러 상태로 전환해 주세요.
🛠️ 제안 변경
worryRepository.getWorries()
.onSuccess { worries ->
// ...
}
- .onLogFailure { e ->
- }
+ .onLogFailure { e ->
+ // 실패 시에도 로딩 해제 (프로젝트의 Error 상태가 있다면 그쪽 사용 권장)
+ _uiState.value = UiState.Success(
+ ProcedureUiState(
+ worries = ProcedureUiState.FakeNormal.worries,
+ startDay = startDateArg
+ )
+ )
+ // TODO: 사용자 피드백(스낵바/이벤트 등)
+ }🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt`
around lines 76 - 107, The onLogFailure block in fetchWorries() is empty which
leaves _uiState stuck in UiState.Loading on failure; update the onLogFailure
handler to set a fallback UI state (e.g., _uiState.value =
UiState.Success(ProcedureUiState(worries = ProcedureUiState.FakeNormal.worries,
startDay = startDateArg)) or set an error state like UiState.Error) and log the
exception; modify the onLogFailure in fetchWorries() to reference _uiState,
UiState.Success/UiState.Error, ProcedureUiState.FakeNormal.worries, and the
caught exception so the UI always progresses on repository failure.
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/calendar/procedure/ProcedureViewModel.kt`:
- Around line 390-410: fetchProcedures currently leaves the onLogFailure block
empty so API failures give no user feedback; implement error handling in
onLogFailure { error -> ... } to update the view model state (use
_uiState.update { current -> current.copy(...) }) to set an error message/flag
(e.g., errorMessage or showError/snackbar state) and reset any loading
indicators and selection fields as appropriate, and ensure the error message
uses error.message (or a localized fallback) so the UI can display whether it
was a network error vs. no results; reference the fetchProcedures function and
the _uiState property to locate and apply the fix.
♻️ Duplicate comments (4)
app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt (4)
47-49: startDate 파싱 실패 시 로깅이 누락되었습니다.
runCatching으로 안전하게 처리되지만, 실패 시 무음으로LocalDate.now()로 대체됩니다. 잘못된 route 값이 전달될 경우 디버깅이 어려워질 수 있습니다.♻️ 로깅 추가 제안
private val startDateArg = runCatching { LocalDate.parse(savedStateHandle.toRoute<Procedure>().startDate) - }.getOrElse { LocalDate.now() } + }.onFailure { e -> + Timber.w(e, "Failed to parse startDate, falling back to today") + }.getOrElse { LocalDate.now() }
104-106: API 실패 시 UI가 Loading 상태에 고정됩니다.
onLogFailure블록이 비어 있어worryRepository.getWorries()실패 시 UI가UiState.Loading에서 벗어나지 못합니다.🛠️ 실패 시 fallback 상태 설정
.onLogFailure { e -> + _uiState.value = UiState.Success( + ProcedureUiState( + worries = ProcedureUiState.FakeNormal.worries, + startDay = startDateArg + ) + ) }
377-384: 시술 등록 실패 시 사용자 피드백이 없습니다.
onLogFailure블록이 비어 있어 API 호출 실패 시 사용자에게 어떤 피드백도 제공되지 않습니다.🛠️ 에러 처리 추가 제안
.onLogFailure { e -> + // 에러 이벤트 발행 또는 스낵바 표시 + Timber.e(e, "시술 등록 실패") }
505-512: SwallowedException: 예외가 무시되고 있습니다.
Exception을 catch하고 로깅 없이null을 반환합니다. 예상치 못한 예외가 발생할 경우 디버깅이 어려워집니다. Based on static analysis hint.🛠️ 특정 예외만 처리하도록 개선
private fun ProcedureUiState.recoveryTargetDateOrNull(): LocalDate? { return try { if (year.isBlank() || month.isBlank() || day.isBlank()) return null LocalDate.of(year.toInt(), month.toInt(), day.toInt()) - } catch (e: Exception) { + } catch (e: java.time.DateTimeException) { + null + } catch (e: NumberFormatException) { null } }
| private fun fetchProcedures(keyword: String?, worryId: Long?) { | ||
| viewModelScope.launch { | ||
| procedureRepository.getProcedures(keyword = keyword, worryId = worryId) | ||
| .onSuccess { response -> | ||
| val items = response.procedures | ||
| .map { it.toUiModel() } | ||
| .toPersistentList() | ||
|
|
||
| ProcedureUiState.FakeNormal | ||
| _uiState.updateSuccess { current -> | ||
| current.copy( | ||
| procedureItems = items, | ||
| selectedProcedureCardIds = persistentListOf(), | ||
| procedureDowntimeMap = emptyMap(), | ||
| selectedProcedureForDowntime = null, | ||
| showDowntimeBottomSheet = false | ||
| ) | ||
| } | ||
| } | ||
| .onLogFailure { } | ||
| } | ||
| } |
There was a problem hiding this comment.
시술 목록 조회 실패 시 에러 처리가 누락되었습니다.
fetchProcedures의 onLogFailure 블록이 비어 있어 API 실패 시 사용자에게 피드백이 제공되지 않습니다. 검색 결과가 없는 것인지 네트워크 오류인지 구분할 수 없습니다.
🛠️ 에러 처리 추가 제안
.onLogFailure { e ->
+ Timber.e(e, "시술 목록 조회 실패")
+ // TODO: 사용자에게 에러 상태 표시 (스낵바 또는 에러 UI)
}🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/procedure/ProcedureViewModel.kt`
around lines 390 - 410, fetchProcedures currently leaves the onLogFailure block
empty so API failures give no user feedback; implement error handling in
onLogFailure { error -> ... } to update the view model state (use
_uiState.update { current -> current.copy(...) }) to set an error message/flag
(e.g., errorMessage or showError/snackbar state) and reset any loading
indicators and selection fields as appropriate, and ensure the error message
uses error.message (or a localized fallback) so the UI can display whether it
was a network error vs. no results; reference the fetchProcedures function and
the _uiState property to locate and apply the fix.
Related issue 🛠
Work Description ✏️
Screenshot 📸
Uncompleted Tasks 😅
To Reviewers 📢
Summary by CodeRabbit
새로운 기능
리팩터
✏️ Tip: You can customize this high-level summary in your review settings.