Skip to content

[FEAT/#45] 캘린더 API 연결#63

Merged
nhyeonii merged 43 commits intodevelopfrom
feat/#45-calendar-view-api
Jan 16, 2026
Merged

[FEAT/#45] 캘린더 API 연결#63
nhyeonii merged 43 commits intodevelopfrom
feat/#45-calendar-view-api

Conversation

@nhyeonii
Copy link
Copy Markdown
Contributor

@nhyeonii nhyeonii commented Jan 15, 2026

Related issue 🛠

Work Description ✏️

  • 캘린더 화면의 서버 API를 전부 연결했어요.

Screenshot 📸

Screen_Recording_20260115_212520_Cherrish.mp4

Uncompleted Tasks 😅

  • 시술 카드 그라데이션 수정 및 엠티뷰 이미지 추가

To Reviewers 📢

캘린더 끝이다 우하하

Summary by CodeRabbit

  • 새로운 기능

    • 월간 일정 조회, 일별 절차 목록 및 이벤트별 다운타임 상세 조회 추가
    • 캘린더 표시 모드(일반 ↔ 다운타임) 전환, 날짜 선택·월 변경·이벤트 클릭 지원
  • 개선사항

    • 절차 예정일을 한국어 형식으로 표시
    • 절차 항목의 클릭/표시 동작 및 UI 응답성 개선(일부 결과 캐시 도입)
  • 주의

    • API 응답의 "code" 필드 타입이 변경되어 파싱/검증에 영향이 있을 수 있음
  • 호환성

    • 다운타임 기간 필드가 null 불가형으로 변경되어 입력/처리 방식이 달라질 수 있음

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

@nhyeonii nhyeonii self-assigned this Jan 15, 2026
@nhyeonii nhyeonii requested a review from a team as a code owner January 15, 2026 12:29
@nhyeonii nhyeonii added FEAT✨ 새로운 기능 구현 나현🍒 나현 담당 labels Jan 15, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 15, 2026

Walkthrough

캘린더 기능 연동을 위해 공용 응답 타입(code)을 문자열로 변경하고, 캘린더용 DTO/모델/매퍼, Retrofit 서비스, 데이터소스·레포지토리, DI 바인딩을 추가·교체하며 ViewModel·UI에 캐시·비동기 로직과 모드 전환 흐름을 도입합니다.

Changes

Cohort / File(s) Summary
네트워크 공용 타입
app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt
BaseResponse<T>code 타입을 IntString로 변경
DI 모듈 바인딩
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
Dummy 관련 바인딩을 Calendar 관련 바인딩으로 교체(메서드명·파라미터·반환 타입·임포트 변경)
원격 서비스·데이터소스
app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt, app/src/main/java/com/cherrish/android/data/remote/datasource/*, app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/*
Retrofit CalendarService 추가 및 CalendarDataSource 인터페이스와 CalendarDataSourceImpl 구현 추가
원격 DTOs (request/response)
app/src/main/java/com/cherrish/android/data/remote/dto/request/*, app/src/main/java/com/cherrish/android/data/remote/dto/response/*
캘린더 API용 직렬화 가능한 요청/응답 DTO 추가(월/일/다운타임 등)
데이터 모델 및 매핑
app/src/main/java/com/cherrish/android/data/model/*
DTO → 내부 모델 변환 확장 함수 및 CalendarMonthly/Daily/DownTime 모델 추가
저장소 계층
app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt, app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt
CalendarRepository 인터페이스 및 구현 추가(데이터소스 호출 → toModel() 변환 → Result 래핑)
프레젠테이션 상태·뷰모델
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt, app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
UiState에 캐시 필드 추가, ViewModel에 CalendarRepository 주입 및 초기 로드/월·일·다운타임 흐름과 병렬 비동기 로직·모드 전환 추가
프레젠테이션 모델·유틸리티
app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt, .../util/CalendarCalculator.kt, .../util/ProcedureUtil.kt, app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt
downTimeDuration nullable→non-null 변경, 날짜 포맷·요일 한국어 매핑 함수 추가, Procedure 판별 로직에서 null 체크→0 체크로 변경
UI 컴포넌트 변경
app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt, .../ProcedureScheduleCard.kt, .../CalendarScreen.kt
다운타임 모드 플래그 전달 및 클릭 가능성 조건부 적용, 표시 텍스트·프리뷰 값 조정; CalendarScreen은 실질 변경 없음(미세 변경)

Sequence Diagram(s)

sequenceDiagram
    participant VM as CalendarViewModel
    participant Repo as CalendarRepository
    participant DS as CalendarDataSource
    participant Service as CalendarService
    participant API as Backend API

    VM->>Repo: getCalendarMonthly(year, month)
    Repo->>DS: getCalendarMonthly(year, month)
    DS->>Service: getCalendarMonthly(year, month)
    Service->>API: GET /api/calendar/monthly?year=Y&month=M
    API-->>Service: BaseResponse<CalendarMonthlyResponseDto>
    Service-->>DS: BaseResponse<CalendarMonthlyResponseDto>
    DS-->>Repo: BaseResponse<CalendarMonthlyResponseDto>
    Repo->>Repo: data!!.toModel()
    Repo-->>VM: Result<CalendarMonthlyResponseModel>
    VM->>VM: updateUiState (selectedYearMonth, cached counts, procedure list)
Loading
sequenceDiagram
    participant VM as CalendarViewModel
    participant Repo as CalendarRepository
    participant DS as CalendarDataSource
    participant Service as CalendarService
    participant API as Backend API

    VM->>Repo: getCalendarDaily(date)
    Repo->>DS: getCalendarDaily(date)
    DS->>Service: getCalendarDaily(date)
    Service->>API: GET /api/calendar/daily?date=YYYY-MM-DD
    API-->>Service: BaseResponse<CalendarDailyResponseDto>
    Service-->>DS: BaseResponse<CalendarDailyResponseDto>
    DS-->>Repo: BaseResponse<CalendarDailyResponseDto>
    Repo->>Repo: data!!.toModel()
    Repo-->>VM: Result<CalendarDailyResponseModel>
    VM->>VM: map events -> ProcedureInfoModel, updateUiState
Loading
sequenceDiagram
    participant VM as CalendarViewModel
    participant Repo as CalendarRepository
    participant DS as CalendarDataSource
    participant Service as CalendarService
    participant API as Backend API

    VM->>Repo: getCalendarEventDowntime(id)
    Repo->>DS: getCalendarEventDowntime(id)
    DS->>Service: getCalendarEventDowntime(id)
    Service->>API: GET /api/calendar/events/{id}/downtime
    API-->>Service: BaseResponse<CalendarDownTimeResponseDto>
    Service-->>DS: BaseResponse<CalendarDownTimeResponseDto>
    DS-->>Repo: BaseResponse<CalendarDownTimeResponseDto>
    Repo->>Repo: data!!.toModel()
    Repo-->>VM: Result<CalendarDownTimeResponseModel>
    VM->>VM: switch to Downtime mode, updateUiState (downtime map)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • sohee6989
  • usuuhyn
  • hyeminililo
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 명확하게 요약하고 있습니다. '캘린더 API 연결'은 이번 PR의 핵심 작업을 잘 나타냅니다.
Description check ✅ Passed PR 설명이 템플릿의 필수 섹션을 포함하고 있으며, 관련 이슈, 작업 설명, 스크린샷, 완료 상태 등이 모두 기재되어 있습니다.
Linked Issues check ✅ Passed PR의 변경 사항들이 #45 이슈의 '캘린더 API 연결' 목표를 충족합니다. 서비스, 데이터소스, 저장소, DTO, 모델 등 전체 계층에서 Calendar API 연결을 구현했습니다.
Out of Scope Changes check ✅ Passed PR의 모든 변경 사항이 캘린더 API 연결과 관련된 범위 내에 있습니다. 네트워크 계층, 저장소 패턴, 뷰모델 로직 등이 모두 Calendar API 구현에 필요한 변경입니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt (1)

8-14: LocalDateTime.parse() 예외 처리 고려 필요

scheduledAt 문자열이 예상된 ISO 형식이 아닐 경우 DateTimeParseException이 발생하여 앱 크래시로 이어질 수 있습니다. 서버 응답이 항상 올바른 형식임을 보장할 수 없다면 방어적 처리를 권장합니다.

♻️ 에러 핸들링 추가 제안
-fun formatProcedureDay(scheduledAt: String): String {
-    val dateTime = LocalDateTime.parse(scheduledAt)
+fun formatProcedureDay(scheduledAt: String): String? {
+    val dateTime = runCatching { LocalDateTime.parse(scheduledAt) }.getOrNull()
+        ?: return null
     val month = dateTime.month.value
     val day = dateTime.dayOfMonth
     val dayOfWeek = dateTime.dayOfWeek.toKoreanName()
     return "${month}월 ${day}일 $dayOfWeek"
 }

또는 호출부에서 이미 유효성이 보장된다면 현재 구현을 유지해도 됩니다.

app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (2)

188-210: 빈 에러 핸들러가 사용자 피드백을 제공하지 않습니다.

API 호출 실패 시 사용자에게 에러 상태를 알리지 않아 UI가 이전 데이터 또는 빈 상태로 유지됩니다. 에러 상태 업데이트나 토스트 메시지 등의 피드백을 고려해 주세요.

♻️ 권장 수정
-            }.onLogFailure { }
+            }.onLogFailure { error ->
+                // 에러 이벤트 발행 또는 상태 업데이트
+                // 예: _errorEvent.emit(error.message)
+            }

213-237: 다운타임 상세 로딩 로직은 적절하나 에러 처리가 필요합니다.

buildMap을 사용한 날짜별 다운타임 상태 매핑 로직이 잘 구현되어 있습니다. 다만, Line 236의 빈 .onLogFailure { } 블록에서 에러 피드백을 추가하는 것을 권장합니다.

참고: sensitiveDays, cautionDays, recoveryDays에 동일 날짜가 있을 경우 나중에 추가된 상태로 덮어쓰여집니다. 이것이 의도된 우선순위라면 괜찮지만, 그렇지 않다면 검토가 필요합니다.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0983606 and 33409be.

📒 Files selected for processing (5)
  • app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarScreen.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧠 Learnings (1)
📚 Learning: 2026-01-12T19:49:27.085Z
Learnt from: nhyeonii
Repo: TEAM-Cherrish/Cherrish-Android PR: 41
File: app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt:30-39
Timestamp: 2026-01-12T19:49:27.085Z
Learning: When a Jetpack Compose screen composable receives a Scaffold paddingValues: PaddingValues parameter (commonly from Scaffold), apply it to the root container's modifier first (e.g., .padding(paddingValues)) before applying any additional padding. This ensures content respects system bars (status/navigation) and avoids layout overlap. This pattern should be followed across presentation screens under app/src/main/java/com/cherrish/android/presentation/ to maintain consistent insets handling.

Applied to files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧬 Code graph analysis (1)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)
app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt (1)
  • formatProcedureDay (8-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PR Lint Check
  • GitHub Check: PR Build Check
🔇 Additional comments (5)
app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt (1)

3-3: LGTM!

daysUntil extension function을 core/util에서 import하여 사용하는 것은 적절합니다. 공통 유틸리티를 재사용하는 좋은 패턴입니다.

app/src/main/java/com/cherrish/android/core/util/CalendarCalculator.kt (2)

6-6: LGTM!

요일 간 일수 계산 로직이 정확합니다. (other.value - value + 7) % 7 공식은 주 내에서 forward distance를 올바르게 계산합니다.


16-24: LGTM!

when expression이 모든 DayOfWeek 값을 exhaustive하게 처리하며, 한국어 요일 매핑이 정확합니다.

app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (2)

27-39: LGTM! ViewModel 초기화 구조가 적절합니다.

Hilt 주입, 초기 Loading 상태, 그리고 init 블록에서의 자동 로딩 패턴이 잘 구현되어 있습니다. 인메모리 캐시(monthlyCache, dailyCache)를 통한 데이터 재사용 전략도 적절합니다.


78-86: LGTM!

월 변경 로직이 깔끔하게 구현되어 있습니다. 현재 월인 경우 오늘 날짜, 아닌 경우 해당 월의 첫째 날로 선택하는 로직이 적절합니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review 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.

❤️ Share

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

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: 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/calendar/component/ProcedureInfoItem.kt (1)

104-108: ProcedureScheduleInfodownTimeDuration 타입 불일치

공개 함수 ProcedureInfoItem에서는 downTimeDuration: Int로 non-nullable 타입을 사용하지만, private 함수 ProcedureScheduleInfo에서는 여전히 Int?로 nullable 타입을 사용하고 있습니다. 일관성을 위해 타입을 통일해 주세요.

🔧 수정 제안
 `@Composable`
 private fun ProcedureScheduleInfo(
     colors: ProcedureColors,
     procedureDay: String,
-    downTimeDuration: Int?
+    downTimeDuration: Int
 ) {
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt`:
- Line 9: BaseResponse.code is never used to validate API success, so update the
response handling in CalendarRepositoryImpl and DummyRepositoryImpl to check
BaseResponse.code (using the API's success value, e.g., "0" or "SUCCESS") before
accessing data; replace direct .data!! usage with safe null handling: if code
indicates success return data (or map to domain model), otherwise throw or
return a meaningful error/exception including BaseResponse.code and message;
ensure the checks reference BaseResponse (the data class) and the repository
methods that currently force-unwrap data.

In
`@app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt`:
- Around line 14-30: The code uses the unsafe `!!` on `calendarDataSource`
responses in getCalendarMonthly, getCalendarDaily, and getCalendarEventDowntime
which can produce unclear NPEs; replace the `!!.toModel()` usage with explicit
null handling: retrieve the response, check `response.data` and if null
return/throw a clear error (e.g. throw IllegalStateException or return
Result.failure(Exception("calendarDataSource returned null data for
getCalendarMonthly/date/id: ..."))) otherwise call `toModel()` and return the
success; include the endpoint name and input (year/month or date or id) in the
error message so failures are informative.

In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`:
- Around line 36-67: The onDateClick implementation runs viewModelScope.launch
inside _uiState.updateSuccess causing a race; instead read the current state
first (e.g., val current = _uiState.value or call updateSuccess once to obtain
the snapshot), then branch on whether current.calendarDisplayMode is
CalendarDisplayMode.Downtime: if Downtime, start the coroutine outside
updateSuccess and inside that coroutine call
calendarRepository.getCalendarDaily(...) and then call _uiState.updateSuccess to
apply the response (mapping response.events to ProcedureInfoModel) and set
calendarDisplayMode/selectedDate/procedureInfoList; if not Downtime, call
loadDailyCalendar(date) and synchronously update selectedDate via
_uiState.updateSuccess or by copying the snapshot. Ensure all asynchronous
updates to _uiState happen from the coroutine (using _uiState.updateSuccess)
rather than launching a coroutine from within the updateSuccess lambda.
🧹 Nitpick comments (12)
app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarDailyRequestDto.kt (1)

6-10: LGTM! 표준 DTO 패턴을 잘 따르고 있습니다.

@Serializable 어노테이션과 data class 구조가 적절하게 사용되었습니다.

선택 사항: @SerialName("date")는 프로퍼티 이름이 이미 date이므로 중복입니다. kotlinx.serialization은 기본적으로 프로퍼티 이름을 JSON 키로 사용합니다. 명시성을 위해 유지하는 것도 괜찮지만, 제거해도 동일하게 동작합니다.

♻️ 중복 어노테이션 제거 (선택 사항)
 `@Serializable`
 data class CalendarDailyRequestDto(
-    `@SerialName`("date")
     val date: String
 )
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)

8-14: LocalDateTime.parse()에서 파싱 실패 시 예외 처리가 없습니다.

scheduledAt 문자열이 ISO-8601 형식이 아니거나 유효하지 않은 경우 DateTimeParseException이 발생하여 앱이 크래시될 수 있습니다. 서버 응답이 예상과 다를 경우를 대비한 방어적 처리를 권장합니다.

♻️ 예외 처리 추가 제안
-fun formatProcedureDay(scheduledAt: String): String {
-    val dateTime = LocalDateTime.parse(scheduledAt)
+fun formatProcedureDay(scheduledAt: String): String? {
+    val dateTime = runCatching { LocalDateTime.parse(scheduledAt) }.getOrNull()
+        ?: return null
     val month = dateTime.month.value
     val day = dateTime.dayOfMonth
     val dayOfWeek = dateTime.dayOfWeek.toKoreanName()
     return "${month}월 ${day}일 $dayOfWeek"
 }

또는 호출부에서 기본값 처리가 필요한 경우:

fun formatProcedureDay(scheduledAt: String, default: String = ""): String {
    return runCatching {
        val dateTime = LocalDateTime.parse(scheduledAt)
        val month = dateTime.month.value
        val day = dateTime.dayOfMonth
        val dayOfWeek = dateTime.dayOfWeek.toKoreanName()
        "${month}${day}$dayOfWeek"
    }.getOrDefault(default)
}
app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/CautionDescription.kt (1)

28-29: 하드코딩된 줄바꿈(\n) 사용에 대한 고려

텍스트 내 \n으로 줄바꿈을 강제하면 다양한 화면 크기나 폰트 크기 설정에서 레이아웃이 의도와 다르게 보일 수 있습니다. 디자인 의도라면 유지해도 되지만, 자연스러운 텍스트 래핑을 원한다면 줄바꿈을 제거하는 것을 고려해 보세요.

app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/ProcedureTitleSection.kt (1)

16-43: modifier 파라미터 추가를 권장합니다.

재사용 가능한 컴포저블은 modifier 파라미터를 노출하는 것이 Compose 컨벤션입니다. 현재 구조에서는 호출하는 쪽에서 padding, size 등을 조절할 수 없습니다.

또한 여러 sibling 컴포저블(HorizontalDivider, Column, HorizontalDivider)을 직접 배치하고 있어, modifier 적용 시 첫 번째 요소에만 적용되는 문제가 발생할 수 있습니다. Column으로 감싸는 것을 고려해 보세요.

♻️ 제안하는 수정
 `@Composable`
 fun ProcedureTitleSection(
-    procedureName: String
+    procedureName: String,
+    modifier: Modifier = Modifier
 ) {
-    HorizontalDivider(
-        thickness = 1.dp,
-        color = CherrishTheme.colors.gray500
-    )
-    Column(
-        modifier = Modifier
-            .fillMaxWidth()
-            .background(color = CherrishTheme.colors.gray100)
-            .padding(horizontal = 25.dp, vertical = 20.dp),
-        verticalArrangement = Arrangement.spacedBy(4.dp)
-    ) {
-        Text(
-            text = "$procedureName 관련 시술 리스트",
-            style = CherrishTheme.typography.title1SB18,
-            color = CherrishTheme.colors.gray1000
-        )
-
-        CautionDescription()
+    Column(modifier = modifier) {
+        HorizontalDivider(
+            thickness = 1.dp,
+            color = CherrishTheme.colors.gray500
+        )
+        Column(
+            modifier = Modifier
+                .fillMaxWidth()
+                .background(color = CherrishTheme.colors.gray100)
+                .padding(horizontal = 25.dp, vertical = 20.dp),
+            verticalArrangement = Arrangement.spacedBy(4.dp)
+        ) {
+            Text(
+                text = "$procedureName 관련 시술 리스트",
+                style = CherrishTheme.typography.title1SB18,
+                color = CherrishTheme.colors.gray1000
+            )
+            CautionDescription()
+        }
+        HorizontalDivider(
+            thickness = 1.dp,
+            color = CherrishTheme.colors.gray500
+        )
     }
-    HorizontalDivider(
-        thickness = 1.dp,
-        color = CherrishTheme.colors.gray500
-    )
 }
app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt (1)

121-123: non-nullable Int.let 사용은 불필요

downTimeDuration이 이제 non-nullable Int이므로 .let 블록 없이 직접 사용해도 됩니다.

♻️ 간소화 제안
         Text(
-            text = downTimeDuration.let {
-                if (it == 0) "-" else "다운타임 ${it}일"
-            },
+            text = if (downTimeDuration == 0) "-" else "다운타임 ${downTimeDuration}일",
             style = CherrishTheme.typography.body3R12,
             color = colors.procedureDateText
         )
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt (1)

17-18: cachedProcedureCountByDateImmutableMap 사용을 권장합니다.

procedureInfoListImmutableList를 사용하는 반면, cachedProcedureCountByDate는 일반 Map을 사용하고 있습니다. @Immutable 어노테이션이 있지만, Compose 안정성 일관성을 위해 kotlinx.collections.immutable.ImmutableMap 또는 persistentMapOf()를 사용하는 것이 더 적합합니다.

♻️ 제안된 수정
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.ImmutableMap
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.persistentMapOf
     val procedureInfoList: ImmutableList<ProcedureInfoModel> = persistentListOf(),
-    val cachedProcedureCountByDate: Map<LocalDate, Int> = emptyMap()
+    val cachedProcedureCountByDate: ImmutableMap<LocalDate, Int> = persistentMapOf()
app/src/main/java/com/cherrish/android/data/model/CalendarDownTimeResponseModel.kt (1)

5-21: 날짜 타입 파싱 고려

scheduledAtsensitiveDays/cautionDays/recoveryDays가 String으로 유지되어 있습니다. 현재 구현도 문제없지만, 도메인 모델에서 LocalDateLocalDateTime으로 파싱하면 타입 안전성이 향상되고 날짜 연산이 간편해집니다. 이 변환을 ViewModel이나 UseCase에서 처리하고 있다면 현재 구조도 괜찮습니다.

app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt (1)

12-26: API 경로 포맷 불일치 확인 필요

getCalendarMonthlygetCalendarDaily는 선행 슬래시 없이 "api/calendar/..." 형태로 정의되어 있지만, getCalendarEventDowntime"/api/calendar/events/{id}/downtime"으로 선행 슬래시가 포함되어 있습니다.

Retrofit에서 선행 슬래시가 있으면 base URL의 path를 무시하고 절대 경로로 처리되므로, 일관성 있게 수정하는 것이 좋습니다.

♻️ 경로 포맷 통일 제안
     `@GET`("api/calendar/monthly")
     suspend fun getCalendarMonthly(
         `@Query`("year") year: Int,
         `@Query`("month") month: Int
     ): BaseResponse<CalendarMonthlyResponseDto>

     `@GET`("api/calendar/daily")
     suspend fun getCalendarDaily(
         `@Query`("date") date: String
     ): BaseResponse<CalendarDailyResponseDto>

-    `@GET`("/api/calendar/events/{id}/downtime")
+    `@GET`("api/calendar/events/{id}/downtime")
     suspend fun getCalendarEventDowntime(
         `@Path`("id") id: Long
     ): BaseResponse<CalendarDownTimeResponseDto>
app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt (1)

7-20: LGTM! Repository 인터페이스가 깔끔하게 정의되었습니다.

Result<T> 래퍼를 사용한 에러 핸들링 패턴과 suspend 함수 사용이 적절합니다. Google 권장 Android 아키텍처를 잘 준수하고 있습니다.

선택적 개선 사항: getCalendarDailydate: String 파라미터를 LocalDate로 변경하면 타입 안전성을 높이고 날짜 형식 오류를 컴파일 타임에 방지할 수 있습니다. 다만 이는 API 계층과의 일관성에 따라 결정하시면 됩니다.

app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (3)

79-96: onDateClick과 동일한 패턴 문제가 있습니다.

updateSuccess 내에서 loadDowntimeDetail(코루틴 실행)을 호출하고 currentState를 반환하는 패턴입니다. 위에서 제안한 것처럼 상태를 먼저 읽고 조건 분기하는 방식으로 리팩토링을 권장합니다.

♻️ 제안된 수정
 fun onEventClick(procedureId: Long) {
-    _uiState.updateSuccess { currentState ->
-        val currentMode = currentState.calendarDisplayMode
-
-        if (currentMode is CalendarDisplayMode.Downtime &&
-            currentMode.selectedProcedureId == procedureId
-        ) {
-            currentState.copy(
-                calendarDisplayMode = CalendarDisplayMode.Normal(
-                    procedureCountByDate = currentState.cachedProcedureCountByDate
-                )
+    val currentState = (_uiState.value as? UiState.Success)?.data ?: return
+    val currentMode = currentState.calendarDisplayMode
+
+    if (currentMode is CalendarDisplayMode.Downtime &&
+        currentMode.selectedProcedureId == procedureId
+    ) {
+        _uiState.updateSuccess { state ->
+            state.copy(
+                calendarDisplayMode = CalendarDisplayMode.Normal(
+                    procedureCountByDate = state.cachedProcedureCountByDate
+                )
             )
-        } else {
-            loadDowntimeDetail(procedureId)
-            currentState
         }
+    } else {
+        loadDowntimeDetail(procedureId)
     }
 }

102-140: 에러 발생 시 UI가 Loading 상태에 머물 수 있습니다.

getCalendarMonthly가 실패하면 onLogFailure { }만 호출되고 _uiState는 업데이트되지 않아 UI가 영구적으로 로딩 상태에 머물게 됩니다. 또한 dailyDeferred는 await되지 않고 방치됩니다.

에러 상태를 UI에 반영하거나 최소한 에러 로깅을 추가하는 것을 권장합니다:

♻️ 에러 처리 개선 제안
         }.onLogFailure { }
+            .onFailure {
+                _uiState.update { UiState.Failure(it.message ?: "Unknown error") }
+            }

또는 최소한 onLogFailure에서 에러를 로깅하도록 수정:

-        }.onLogFailure { }
+        }.onLogFailure { throwable ->
+            // 에러 로깅 또는 상태 업데이트
+        }

149-156: ProcedureInfoModel 매핑 로직이 여러 곳에서 중복됩니다.

동일한 이벤트 → ProcedureInfoModel 변환 로직이 라인 49-55, 125-131, 149-154에서 반복됩니다. 확장 함수나 헬퍼 함수로 추출하면 유지보수성이 향상됩니다.

♻️ 헬퍼 함수 추출 제안
// CalendarViewModel.kt 또는 별도 파일에 추가
private fun CalendarDailyModel.Event.toProcedureInfoModel() = ProcedureInfoModel(
    procedureId = userProcedureId,
    procedureName = name,
    procedureDay = formatProcedureDay(scheduledAt),
    downTimeDuration = downtimeDays
)

// 사용 예시
procedureInfoList = response.events.map { it.toProcedureInfoModel() }.toImmutableList()
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 932afeb and c998029.

📒 Files selected for processing (28)
  • .kotlin/sessions/kotlin-compiler-1697198284998868167.salive
  • app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt
  • 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
  • app/src/main/java/com/cherrish/android/data/model/CalendarDailyResponseModel.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarDownTimeResponseModel.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyRequestModel.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyResponseModel.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasource/CalendarDataSource.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/CalendarDataSourceImpl.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/request/.gitkeep
  • app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarDailyRequestDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarMonthlyRequestDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDailyResponseDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDownTimeResponseDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarMonthlyResponseDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt
  • app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt
  • app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/CautionDescription.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/ProcedureTitleSection.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/core/network/BaseResponse.kt
  • app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt
  • app/src/main/java/com/cherrish/android/data/repository/CalendarRepository.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyResponseModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarDownTimeResponseModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt
  • app/src/main/java/com/cherrish/android/data/repositoryimpl/CalendarRepositoryImpl.kt
  • app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarDailyResponseModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/ProcedureTitleSection.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarDailyRequestDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarMonthlyRequestDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDailyResponseDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDownTimeResponseDto.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
  • app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyRequestModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/CautionDescription.kt
  • app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarMonthlyResponseDto.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/CalendarDataSourceImpl.kt
  • app/src/main/java/com/cherrish/android/data/remote/service/CalendarService.kt
  • app/src/main/java/com/cherrish/android/data/remote/datasource/CalendarDataSource.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt
🧠 Learnings (1)
📚 Learning: 2026-01-12T19:49:27.085Z
Learnt from: nhyeonii
Repo: TEAM-Cherrish/Cherrish-Android PR: 41
File: app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt:30-39
Timestamp: 2026-01-12T19:49:27.085Z
Learning: When a Jetpack Compose screen composable receives a Scaffold paddingValues: PaddingValues parameter (commonly from Scaffold), apply it to the root container's modifier first (e.g., .padding(paddingValues)) before applying any additional padding. This ensures content respects system bars (status/navigation) and avoids layout overlap. This pattern should be followed across presentation screens under app/src/main/java/com/cherrish/android/presentation/ to maintain consistent insets handling.

Applied to files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/ProcedureTitleSection.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/CautionDescription.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarUiState.kt
🧬 Code graph analysis (2)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)
  • formatProcedureDay (8-14)
app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/CautionDescription.kt (1)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (38-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PR Build Check
  • GitHub Check: PR Lint Check
🔇 Additional comments (21)
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)

16-24: LGTM!

when 표현식으로 모든 DayOfWeek 값을 exhaustive하게 처리하고 있어 안전합니다. 한글 요일명 매핑이 정확합니다.

app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/CautionDescription.kt (1)

13-34: LGTM - 깔끔한 stateless 컴포넌트입니다.

modifier 파라미터를 올바르게 노출하고 있고, 불필요한 상태 없이 간결하게 구현되었습니다.

app/src/main/java/com/cherrish/android/presentation/calendar/procedure/component/ProcedureTitleSection.kt (1)

45-53: Preview 함수 잘 작성되었습니다.

CherrishTheme으로 감싸고 샘플 데이터를 사용한 Preview가 적절합니다.

app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureInfoItem.kt (1)

53-59: 클릭 가능 여부 조건 로직 LGTM

다운타임이 없을 때(0) 클릭을 비활성화하는 로직이 적절합니다. Modifier.then()을 사용한 조건부 modifier 적용 방식도 올바릅니다.

app/src/main/java/com/cherrish/android/data/remote/dto/request/CalendarMonthlyRequestDto.kt (1)

6-12: LGTM!

DTO 구조가 깔끔하고 kotlinx.serialization 어노테이션이 올바르게 적용되었습니다.

app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyRequestModel.kt (1)

5-13: LGTM!

Model-DTO 분리와 toDto() 확장 함수 패턴이 적절합니다. Clean Architecture 원칙을 잘 따르고 있습니다.

app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarMonthlyResponseDto.kt (1)

6-10: 이 코드는 표준 동작이며 문제가 없습니다

kotlinx.serialization은 Map<Int, Long>을 기본적으로 지원하며, JSON의 문자열 키를 자동으로 Int로 변환합니다. 예를 들어 서버가 {"1": 100, "15": 200} 형식으로 응답하면 자동으로 올바르게 역직렬화됩니다. 커스텀 시리얼라이저나 추가 설정이 필요하지 않으며, 현재 구현은 kotlinx.serialization의 표준 패턴을 따르고 있습니다.

app/src/main/java/com/cherrish/android/data/model/CalendarMonthlyResponseModel.kt (1)

5-11: LGTM!

DTO에서 모델로의 매핑이 깔끔하고 직관적입니다. 확장 함수를 사용한 변환 패턴이 적절합니다.

app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDailyResponseDto.kt (1)

6-28: 필드 null 가능성 확인 필요

모든 필드가 non-null로 선언되어 있습니다. 서버에서 특정 필드가 null이나 누락된 값을 반환하는 경우 역직렬화 오류가 발생할 수 있습니다. API 명세서를 확인하여 optional 필드가 있는지 검토해 주세요.

app/src/main/java/com/cherrish/android/data/remote/dto/response/CalendarDownTimeResponseDto.kt (1)

6-20: LGTM!

DTO 구조가 명확하고 직렬화 설정이 적절합니다.

app/src/main/java/com/cherrish/android/data/remote/datasource/CalendarDataSource.kt (1)

8-12: LGTM!

DataSource 인터페이스가 깔끔하게 정의되어 있으며, CalendarService와 일관된 메서드 시그니처를 제공합니다. 표준적인 데이터 레이어 패턴을 잘 따르고 있습니다.

app/src/main/java/com/cherrish/android/data/di/ServiceModule.kt (1)

14-18: LGTM!

CalendarService를 Singleton 스코프로 제공하는 표준적인 Hilt 모듈 설정입니다. Retrofit 서비스 생성 패턴이 올바르게 적용되었습니다.

app/src/main/java/com/cherrish/android/data/remote/datasourceimpl/CalendarDataSourceImpl.kt (1)

11-29: LGTM!

CalendarDataSource 인터페이스를 올바르게 구현하고 있으며, CalendarService로의 위임 패턴이 깔끔하게 적용되었습니다. @Inject 생성자를 통한 의존성 주입이 적절하게 설정되어 있습니다.

app/src/main/java/com/cherrish/android/data/di/DataSourceModule.kt (1)

14-18: LGTM!

@Binds를 사용한 인터페이스-구현체 바인딩이 올바르게 설정되어 있습니다. Singleton 스코프와 함께 표준적인 Hilt 모듈 패턴을 따르고 있습니다.

app/src/main/java/com/cherrish/android/data/model/CalendarDailyResponseModel.kt (1)

1-32: LGTM! DTO에서 모델로의 매핑이 깔끔합니다.

확장 함수를 통한 DTO-Model 변환 패턴이 적절하며, 코드가 간결하고 읽기 쉽습니다.

app/src/main/java/com/cherrish/android/presentation/calendar/model/ProcedureInfoModel.kt (1)

5-11: LGTM! Non-nullable 타입으로의 변경이 적절합니다.

downTimeDurationInt?에서 Int로 변경한 것은 0을 "다운타임 없음"으로 표현하는 명확한 의미를 부여합니다. @Immutable 어노테이션도 Compose recomposition 최적화에 적합합니다.

app/src/main/java/com/cherrish/android/presentation/calendar/util/ProcedureUtil.kt (1)

47-66: LGTM! Null 체크에서 0 체크로의 변경이 일관성 있게 적용되었습니다.

ProcedureInfoModeldownTimeDuration 타입 변경과 일관되게 로직이 업데이트되었습니다.

다만, downTimeDuration이 음수일 경우 현재 로직에서는 ACTIVE로 처리됩니다. API에서 음수 값이 반환될 가능성이 없는지 확인해 주세요. 필요시 방어적 코드를 추가할 수 있습니다:

if (downTimeDuration <= 0) {
    ProcedureType.ACTIVE_NO_DOWNTIME
}
app/src/main/java/com/cherrish/android/data/di/RepositoryModule.kt (1)

11-19: LGTM!

Hilt DI 바인딩이 표준 패턴을 따르며 @Binds@Singleton 스코프가 적절하게 사용되었습니다. CalendarRepository 바인딩으로의 전환이 깔끔합니다.

app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (3)

25-34: LGTM!

ViewModel 초기화 패턴이 적절합니다. UiState.Loading으로 초기 상태 설정 후 init 블록에서 데이터 로드를 시작하는 것이 올바른 패턴입니다.


69-77: LGTM!

월 변경 로직이 적절합니다. 현재 월이면 오늘 날짜를, 다른 월이면 1일을 선택하는 것은 좋은 UX입니다.


167-177: 날짜가 여러 상태 목록에 중복될 경우 마지막 상태만 적용됩니다.

buildMap에서 put을 순차적으로 호출하므로, 동일 날짜가 sensitiveDays, cautionDays, recoveryDays에 모두 포함되면 recoveryDays의 상태가 최종 적용됩니다. 이것이 의도된 동작인지 확인이 필요합니다.

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

Comment thread app/src/main/java/com/cherrish/android/core/network/BaseResponse.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: 2

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`:
- Around line 79-94: The updateSuccess block in onEventClick reads currentState
and calls loadDowntimeDetail(...) inside that block, causing a race because
loadDowntimeDetail launches coroutines and updates state asynchronously; to fix,
read the current mode from _uiState.value (or capture currentState before
mutating), then perform the synchronous state transition with
_uiState.updateSuccess only when switching CalendarDisplayMode.Downtime ->
CalendarDisplayMode.Normal (using CalendarDisplayMode.Downtime and
CalendarDisplayMode.Normal symbols), and call loadDowntimeDetail(procedureId)
outside of the updateSuccess block when you need to load details so the async
load is not invoked from inside updateSuccess and cannot race with the immediate
state return.
♻️ Duplicate comments (1)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)

36-67: updateSuccess 내부에서 코루틴을 실행하는 패턴에 대한 기존 리뷰 참조

이 race condition 이슈는 이전 리뷰에서 이미 지적되었습니다. 기존 제안대로 현재 상태를 먼저 읽고 조건 분기 후 코루틴을 외부에서 실행하는 패턴으로 수정이 필요합니다.

🧹 Nitpick comments (1)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)

140-159: 에러 처리 로직이 비어있습니다.

API 호출 실패 시 사용자에게 피드백이 없습니다. 최소한 토스트 메시지나 스낵바를 통해 에러를 알리는 것을 고려해주세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a0358a and 0a0b921.

📒 Files selected for processing (1)
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧠 Learnings (1)
📚 Learning: 2026-01-12T19:49:27.085Z
Learnt from: nhyeonii
Repo: TEAM-Cherrish/Cherrish-Android PR: 41
File: app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt:30-39
Timestamp: 2026-01-12T19:49:27.085Z
Learning: When a Jetpack Compose screen composable receives a Scaffold paddingValues: PaddingValues parameter (commonly from Scaffold), apply it to the root container's modifier first (e.g., .padding(paddingValues)) before applying any additional padding. This ensures content respects system bars (status/navigation) and avoids layout overlap. This pattern should be followed across presentation screens under app/src/main/java/com/cherrish/android/presentation/ to maintain consistent insets handling.

Applied to files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧬 Code graph analysis (1)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)
  • formatProcedureDay (8-14)
🔇 Additional comments (3)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (3)

25-34: LGTM!

Hilt 의존성 주입과 초기 상태 설정이 적절합니다. init 블록에서 현재 월의 캘린더를 로드하는 패턴이 올바릅니다.


69-77: LGTM!

현재 월인 경우 오늘 날짜를, 그 외에는 해당 월의 첫 날을 선택하는 로직이 적절합니다.


161-186: 날짜 중복 시 덮어쓰기 동작 확인이 필요합니다.

downtimeByDate 맵 구성 시 동일한 날짜가 sensitiveDays, cautionDays, recoveryDays에 중복으로 존재하면 마지막에 처리된 상태(RECOVERY)로 덮어씌워집니다. 이것이 의도된 동작인지 확인이 필요합니다.

만약 의도된 우선순위라면 코드에 주석을 추가하여 명시하는 것을 권장합니다.

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

Comment thread app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt`:
- Line 27: Remove the unused import painterResource from the top of the file;
locate the ProcedureScheduleCard composable (or its surrounding imports) and
delete the line "import androidx.compose.ui.res.painterResource" so only used
imports remain.
🧹 Nitpick comments (1)
app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt (1)

49-51: LGTM - isDowntimeMode 도입 적절함

displayMode에서 다운타임 모드 여부를 추출하여 하위 컴포넌트에 전달하는 방식이 적절합니다. 파라미터 기반의 간단한 타입 체크이므로 remember가 불필요하며, 현재 구현이 올바릅니다.

선택적으로, 선언 전후의 빈 줄을 하나로 줄이면 코드가 더 깔끔해집니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7339e27 and 72fc82b.

📒 Files selected for processing (1)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt
🧠 Learnings (1)
📚 Learning: 2026-01-12T19:49:27.085Z
Learnt from: nhyeonii
Repo: TEAM-Cherrish/Cherrish-Android PR: 41
File: app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt:30-39
Timestamp: 2026-01-12T19:49:27.085Z
Learning: When a Jetpack Compose screen composable receives a Scaffold paddingValues: PaddingValues parameter (commonly from Scaffold), apply it to the root container's modifier first (e.g., .padding(paddingValues)) before applying any additional padding. This ensures content respects system bars (status/navigation) and avoids layout overlap. This pattern should be followed across presentation screens under app/src/main/java/com/cherrish/android/presentation/ to maintain consistent insets handling.

Applied to files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PR Lint Check
  • GitHub Check: PR Build Check
🔇 Additional comments (1)
app/src/main/java/com/cherrish/android/presentation/calendar/component/ProcedureScheduleCard.kt (1)

113-124: LGTM - 하위 컴포넌트로 모드 전달 적절함

isDowntimeMode 플래그를 ProcedureInfoItem에 전달하여 다운타임 모드에 따른 렌더링 분기를 가능하게 합니다. Boolean은 stable 타입이므로 불필요한 recomposition을 유발하지 않습니다.

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`:
- Around line 108-125: The cached month branch keeps cached.procedureInfoList
even when cached.selectedDate != selectedDate causing the previous day's
procedures to flash; update the _uiState.update call (inside the monthlyCache
check) to set procedureInfoList to an empty list or a loading placeholder when
cached.selectedDate != selectedDate (e.g., in the
UiState.Success(cached.copy(...)) use conditional: if (cached.selectedDate ==
selectedDate) cached.procedureInfoList else emptyList() or a loading state) and
ensure loadDailyCalendar(selectedDate) is still invoked to fetch the real list.
♻️ Duplicate comments (3)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (3)

38-73: updateSuccess 내부에서 코루틴 실행은 상태 레이스 위험
updateSuccess는 즉시 currentState를 반환하는데, 내부 코루틴이 나중에 상태를 다시 갱신하면서 직전 변경을 덮거나 순서가 꼬일 수 있습니다. 상태 스냅샷을 먼저 읽고 비동기 로딩은 블록 밖에서 실행하도록 분리해 주세요.

🛠️ 수정 제안
 fun onDateClick(date: LocalDate) {
-    _uiState.updateSuccess { currentState ->
-        if (currentState.calendarDisplayMode is CalendarDisplayMode.Downtime) {
-            viewModelScope.launch {
-                calendarRepository.getCalendarDaily(
-                    date = date.toString()
-                ).onSuccess { response ->
-                    val procedureList = response.events.map { event ->
-                        ProcedureInfoModel(
-                            procedureId = event.userProcedureId,
-                            procedureName = event.name,
-                            procedureDay = formatProcedureDay(event.scheduledAt),
-                            downTimeDuration = event.downtimeDays
-                        )
-                    }.toImmutableList()
-
-                    _uiState.updateSuccess { state ->
-                        val newState = state.copy(
-                            calendarDisplayMode = CalendarDisplayMode.Normal(
-                                procedureCountByDate = state.cachedProcedureCountByDate
-                            ),
-                            selectedDate = date,
-                            procedureInfoList = procedureList
-                        )
-
-                        monthlyCache[newState.selectedYearMonth] = newState
-                        newState
-                    }
-                }.onLogFailure { }
-            }
-            currentState
-        } else {
-            loadDailyCalendar(date)
-            currentState.copy(selectedDate = date)
-        }
-    }
+    val currentState = (_uiState.value as? UiState.Success)?.data ?: return
+    if (currentState.calendarDisplayMode is CalendarDisplayMode.Downtime) {
+        viewModelScope.launch {
+            calendarRepository.getCalendarDaily(date = date.toString())
+                .onSuccess { response ->
+                    val procedureList = response.events.map { event ->
+                        ProcedureInfoModel(
+                            procedureId = event.userProcedureId,
+                            procedureName = event.name,
+                            procedureDay = formatProcedureDay(event.scheduledAt),
+                            downTimeDuration = event.downtimeDays
+                        )
+                    }.toImmutableList()
+
+                    _uiState.updateSuccess { state ->
+                        val newState = state.copy(
+                            calendarDisplayMode = CalendarDisplayMode.Normal(
+                                procedureCountByDate = state.cachedProcedureCountByDate
+                            ),
+                            selectedDate = date,
+                            procedureInfoList = procedureList
+                        )
+                        monthlyCache[newState.selectedYearMonth] = newState
+                        newState
+                    }
+                }.onLogFailure { }
+        }
+    } else {
+        _uiState.updateSuccess { it.copy(selectedDate = date) }
+        loadDailyCalendar(date)
+    }
 }

86-101: onEventClick도 동일한 비동기 레이스 패턴입니다.
loadDowntimeDetail가 코루틴을 시작하는데 updateSuccess 안에서 호출되어 상태 갱신 순서가 비결정적입니다. 스냅샷을 밖에서 가져와 분기하고, 모드 전환만 updateSuccess로 처리하는 편이 안전합니다.

🛠️ 수정 제안
 fun onEventClick(procedureId: Long) {
-    _uiState.updateSuccess { currentState ->
-        val currentMode = currentState.calendarDisplayMode
-
-        if (currentMode is CalendarDisplayMode.Downtime) {
-            currentState.copy(
-                calendarDisplayMode = CalendarDisplayMode.Normal(
-                    procedureCountByDate = currentState.cachedProcedureCountByDate
-                )
-            )
-        } else {
-            loadDowntimeDetail(procedureId)
-            currentState
-        }
-    }
+    val currentState = (_uiState.value as? UiState.Success)?.data ?: return
+    val currentMode = currentState.calendarDisplayMode
+
+    if (currentMode is CalendarDisplayMode.Downtime) {
+        _uiState.updateSuccess {
+            it.copy(
+                calendarDisplayMode = CalendarDisplayMode.Normal(
+                    procedureCountByDate = it.cachedProcedureCountByDate
+                )
+            )
+        }
+    } else {
+        loadDowntimeDetail(procedureId)
+    }
 }

129-165: 월/일 동시 로드 실패 시 대기 코루틴이 남고 로딩이 해제되지 않습니다.
월별 호출이 실패하면 dailyDeferred가 취소되지 않고, 일별 호출 실패 시에도 UiState가 갱신되지 않아 로딩이 지속될 수 있습니다. 실패 경로에서 취소 및 에러 상태 업데이트를 추가해 주세요.

🛠️ 수정 제안
 viewModelScope.launch {
     val dailyDeferred = async {
         calendarRepository.getCalendarDaily(date = selectedDate.toString())
     }

     calendarRepository.getCalendarMonthly(
         year = yearMonth.year,
         month = yearMonth.monthValue
     ).onSuccess { response ->
         val procedureCountByDate = response.dailyProcedureCounts.mapKeys { (day, _) ->
             yearMonth.atDay(day)
         }.mapValues { it.value.toInt() }

         dailyDeferred.await().onSuccess { dailyResponse ->
             val procedureList = dailyResponse.events.map { event ->
                 ProcedureInfoModel(
                     procedureId = event.userProcedureId,
                     procedureName = event.name,
                     procedureDay = formatProcedureDay(event.scheduledAt),
                     downTimeDuration = event.downtimeDays
                 )
             }.toImmutableList()

             val newState = CalendarUiState(
                 selectedYearMonth = yearMonth,
                 calendarDisplayMode = CalendarDisplayMode.Normal(procedureCountByDate),
                 selectedDate = selectedDate,
                 procedureInfoList = procedureList,
                 cachedProcedureCountByDate = procedureCountByDate
             )

             monthlyCache[yearMonth] = newState

             _uiState.update { UiState.Success(newState) }
-        }
-    }.onLogFailure { }
+        }.onLogFailure {
+            _uiState.update { UiState.Error(it) } // 프로젝트의 에러 상태에 맞게 조정
+        }
+    }.onLogFailure {
+        dailyDeferred.cancel()
+        _uiState.update { UiState.Error(it) } // 프로젝트의 에러 상태에 맞게 조정
+    }
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30c8d3a and 99bf2c8.

📒 Files selected for processing (1)
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧠 Learnings (1)
📚 Learning: 2026-01-12T19:49:27.085Z
Learnt from: nhyeonii
Repo: TEAM-Cherrish/Cherrish-Android PR: 41
File: app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt:30-39
Timestamp: 2026-01-12T19:49:27.085Z
Learning: When a Jetpack Compose screen composable receives a Scaffold paddingValues: PaddingValues parameter (commonly from Scaffold), apply it to the root container's modifier first (e.g., .padding(paddingValues)) before applying any additional padding. This ensures content respects system bars (status/navigation) and avoids layout overlap. This pattern should be followed across presentation screens under app/src/main/java/com/cherrish/android/presentation/ to maintain consistent insets handling.

Applied to files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧬 Code graph analysis (1)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)
  • formatProcedureDay (8-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PR Lint Check
  • GitHub Check: PR Build Check

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

Comment thread app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt`:
- Line 208: 현재 CalendarViewModel의 API 호출 체인 끝에 있는 .onLogFailure { }가 비어 있어 실패 시
아무런 사용자 피드백을 주지 않습니다; onLogFailure 블록 안에서 CalendarViewModel의 상태를 업데이트하거나 에러 이벤트를
발행하도록 변경하세요 — 예를 들어 calendarUiState 또는 _uiState를 변경해 isError/message 필드를 설정하거나
emit(OneShotEvent.ShowToast(errorMessage)) 식의 단발성 이벤트(또는 LiveData/SharedFlow)를
발행하도록 구현하고, 오류 메시지는 Throwable 또는 응답의 에러 메시지에서 추출해 전달하세요 (참조: CalendarViewModel,
.onLogFailure).
♻️ Duplicate comments (3)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (3)

41-76: 이전 리뷰에서 지적된 race condition 패턴이 여전히 존재합니다.

updateSuccess 블록 내부에서 viewModelScope.launch를 호출하고 currentState를 즉시 반환하는 패턴은 동시성 문제를 유발합니다. 이전 리뷰의 제안대로 현재 상태를 먼저 읽고 조건에 따라 로직을 분기하는 패턴으로 수정이 필요합니다.


88-103: 이전 리뷰에서 지적된 race condition 패턴이 여전히 존재합니다.

loadDowntimeDetail(procedureId)updateSuccess 블록 내부에서 호출되고 currentState가 즉시 반환되어 동시성 문제가 발생할 수 있습니다. 이전 리뷰의 제안대로 수정이 필요합니다.


132-173: 에러 처리 개선이 필요합니다 (이전 리뷰 참조).

이전 리뷰에서 지적된 대로:

  1. getCalendarMonthly 실패 시 dailyDeferred가 대기 중인 상태로 남습니다.
  2. getCalendarDaily 실패 시 UI가 Loading 상태로 유지됩니다.
  3. .onLogFailure { } 블록이 비어있어 사용자에게 에러 피드백이 없습니다.
🧹 Nitpick comments (2)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (2)

34-35: 캐시에 대한 스레드 안전성 문제가 있습니다.

mutableMapOf는 스레드 안전하지 않습니다. 여러 코루틴에서 동시에 읽기/쓰기가 발생할 수 있어 ConcurrentModificationException 또는 데이터 손상이 발생할 수 있습니다.

♻️ ConcurrentHashMap 사용 권장
-    private val monthlyCache = mutableMapOf<YearMonth, Map<LocalDate, Int>>()
-    private val dailyCache = mutableMapOf<LocalDate, ImmutableList<ProcedureInfoModel>>()
+    private val monthlyCache = java.util.concurrent.ConcurrentHashMap<YearMonth, Map<LocalDate, Int>>()
+    private val dailyCache = java.util.concurrent.ConcurrentHashMap<LocalDate, ImmutableList<ProcedureInfoModel>>()

48-55: ProcedureInfoModel 매핑 로직이 중복됩니다.

동일한 매핑 로직이 onDateClick, loadMonthlyCalendarWithDate, loadDailyCalendar에서 반복됩니다. 헬퍼 함수로 추출하면 유지보수성이 향상됩니다.

♻️ 매핑 헬퍼 함수 추출 제안
private fun CalendarDailyResponseModel.Event.toProcedureInfoModel() = ProcedureInfoModel(
    procedureId = userProcedureId,
    procedureName = name,
    procedureDay = formatProcedureDay(scheduledAt),
    downTimeDuration = downtimeDays
)

// 사용 예시
response.events.map { it.toProcedureInfoModel() }.toImmutableList()
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99bf2c8 and 0983606.

📒 Files selected for processing (1)
  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

⚙️ CodeRabbit configuration file

**/*.kt: - Jetpack Compose 구조, 상태 관리, recomposition 최적화에 집중

  • ViewModel, UiState, 단방향 데이터 흐름 패턴 검토
  • 불필요한 recomposition 가능성, remember/derivedStateOf 적절한 사용 확인
  • 네이밍 컨벤션, 가독성, Google 권장 Android 아키텍처 준수 여부

Files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧠 Learnings (1)
📚 Learning: 2026-01-12T19:49:27.085Z
Learnt from: nhyeonii
Repo: TEAM-Cherrish/Cherrish-Android PR: 41
File: app/src/main/java/com/cherrish/android/presentation/challenge/ChallengeScreen.kt:30-39
Timestamp: 2026-01-12T19:49:27.085Z
Learning: When a Jetpack Compose screen composable receives a Scaffold paddingValues: PaddingValues parameter (commonly from Scaffold), apply it to the root container's modifier first (e.g., .padding(paddingValues)) before applying any additional padding. This ensures content respects system bars (status/navigation) and avoids layout overlap. This pattern should be followed across presentation screens under app/src/main/java/com/cherrish/android/presentation/ to maintain consistent insets handling.

Applied to files:

  • app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt
🧬 Code graph analysis (1)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (1)
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)
  • formatProcedureDay (8-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PR Build Check
  • GitHub Check: PR Lint Check
🔇 Additional comments (2)
app/src/main/java/com/cherrish/android/presentation/calendar/CalendarViewModel.kt (2)

78-86: LGTM!

현재 월이면 오늘 날짜를, 그렇지 않으면 해당 월의 첫날을 선택하는 로직이 명확하고 적절합니다.


216-226: 동일 날짜가 여러 리스트에 존재할 경우 덮어쓰기 동작을 확인해 주세요.

buildMap에서 sensitiveDayscautionDaysrecoveryDays 순서로 put을 호출하므로, 동일 날짜가 여러 리스트에 포함된 경우 마지막으로 처리된 상태(RECOVERY)가 최종 값이 됩니다. 이 동작이 의도된 것인지 확인이 필요합니다.

만약 우선순위가 다르게 적용되어야 한다면(예: SENSITIVE > CAUTION > RECOVERY), 순서를 역순으로 변경하거나 putIfAbsent를 사용하세요:

♻️ 우선순위 보장을 위한 수정 예시
                     val downtimeByDate = buildMap<LocalDate, DownTimeStatus> {
-                        response.sensitiveDays.forEach { dateString ->
-                            put(LocalDate.parse(dateString), DownTimeStatus.SENSITIVE)
-                        }
-                        response.cautionDays.forEach { dateString ->
-                            put(LocalDate.parse(dateString), DownTimeStatus.CAUTION)
-                        }
                         response.recoveryDays.forEach { dateString ->
                             put(LocalDate.parse(dateString), DownTimeStatus.RECOVERY)
                         }
+                        response.cautionDays.forEach { dateString ->
+                            put(LocalDate.parse(dateString), DownTimeStatus.CAUTION)
+                        }
+                        response.sensitiveDays.forEach { dateString ->
+                            put(LocalDate.parse(dateString), DownTimeStatus.SENSITIVE)
+                        }
                     }

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

Copy link
Copy Markdown
Contributor

@sohee6989 sohee6989 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!!
넘 빠르시네여

return "${month}월 ${day}일 $dayOfWeek"
}

fun DayOfWeek.toKoreanName(): String = when (this) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 이거 공통으로 빼주시면 안되나여?!!
이거 다른 화면에서도 쓰여가지구요!!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

조씁니다 ㅎ.ㅎ !!!

}

fun onDateClick(date: LocalDate) {
fun onMonthChanged(yearMonth: YearMonth) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: (단순 질문) 그 click은 onxxClick으로 컨벤션 맞춰주자고 하셨는데 다른 경우에는 상관없는 건가요??

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.

얘도 컨벤션 맞출게요 !!!

year: Int,
month: Int
): Result<CalendarMonthlyResponseModel> =
runCatching {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 여기 suspendRunCahtching 쓰시려고 따로 만들어두신거 아니신가여??!

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.

헉 마자요......

}

private fun loadMonthlyCalendar(yearMonth: YearMonth) {
loadMonthlyCalendarWithDate(yearMonth, LocalDate.now())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 여기 loadMonthlyCalendarWithDate 함수 가독성을 위해 묶어주신거가요??
하나로 합쳐줘도 괜찮을 것 같다는 생각도 들긴 합니다..!ㅎㅎㅎ

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 +114 to +118
procedureInfoList = if (cached.selectedDate == selectedDate) {
cached.procedureInfoList
} else {
cached.procedureInfoList
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p1: 여기 if문 처리안하나 하나 똑같은 동작인거 아닌가여??


private fun loadMonthlyCalendarWithDate(yearMonth: YearMonth, selectedDate: LocalDate) {
val cached = monthlyCache[yearMonth]
if (cached != null) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 여기 cached != null로만 분기처리해도 괜찮나여??

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.

열심히 읽엇으용
시술 플로우 API 연결할 때 참고해서 진행하면 좋을 것 같으네용 ~~ 😻👍🐒
수고햇쭁!!
다 끝내신거 너무 부럽네요
저도 얼른 끝내서 pr 올릴게요..
bba16be88c9b96a9d1f9b749f53256d5

getProcedureColors(procedureType, themeColors)
}

val isClickable = isDowntimeMode || downTimeDuration != 0
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.

P99: 규일이가 말햇던거처럼 매직 넘버 제거 어떠세욤 ㅎㅎ
private const val NO_DOWNTIME = 0

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.

지금 우리가 이런거 매직 넘버를 다 설정 안해쥬는 쪽이라 얘만 NO_DOWNTIME 상수를 추가하면 맥락을 한 번 더 따라가야해서 현재는 0을 직접 사용하는 쪽이 더 직관적이라고 생각합니다요잉 ~~ 어떠케 생각하세여

month: Int
): Result<CalendarMonthlyResponseModel> =
suspendRunCatching {
calendarDataSource.getCalendarMonthly(year = year, month = month).data!!.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.

P3: (단순 궁금) data!! 사용은 NullPointerException 위험이 있다는데 괜찮은가용~/!?!?

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.

data!! 사용 시 NPE 가능성은 잇지용 ~~ 다만 앱 크래시는 안 나고, 에러 흐름은 통제할 수 잇어 이런식으로 사용했엉요 ! 해당 API는 스펙상 성공 응답일 경우 data가 항상 내려오도록 보장되어 있고 null인 경우는 서버 계약 위반 또는 비정상 응답으로 판단하고 있답니닿ㅎㅎㅎ 그래서 suspendRunCatching을 통해 실패로 감싸 상위 레이어에서 공통 에러 처리하도록 의도햇어요 !! 그냥 runCatching이 아니라 suspendRunCatching util 함수를 사용하는 이유랍니도 !

@nhyeonii nhyeonii merged commit 22112b3 into develop Jan 16, 2026
3 checks passed
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 연결

3 participants