Skip to content

[FEAT/#5] 캘린더 컴포넌트 구현#15

Merged
nhyeonii merged 26 commits intodevelopfrom
feat/#5-calendar-component
Jan 10, 2026
Merged

[FEAT/#5] 캘린더 컴포넌트 구현#15
nhyeonii merged 26 commits intodevelopfrom
feat/#5-calendar-component

Conversation

@nhyeonii
Copy link
Copy Markdown
Contributor

@nhyeonii nhyeonii commented Jan 9, 2026

Related issue 🛠

Work Description ✏️

  • 캘린더 컴포넌트(노멀, 다운타임)를 구현했습니다.

Screenshot 📸

Figma Calendar Interaction
figma calendar
2026-01-09.5.03.14.mov

Uncompleted Tasks 😅

  • N/A

To Reviewers 📢

캘린더에 두가지 모드가 있어서 Normal / DownTime 으로 구분하여 접근하도록 작성했습니다. 캘린더 구현에 필요한 유틸이나 함수들이 좀 필요해서 500줄을 넘어버렷네요,, 이해 부탁드립니다 우리 아부지 캘린더 구현 피알 요 PR 참고해서 공부하면서 구현했어요! 저도 캘린더는 처음이라 미숙하니 좋은 개선 방향이 보인다면 리뷰 많이 부탁드립니다 ㅎㅎ

Summary by CodeRabbit

  • 새로운 기능
    • 월 탐색 및 날짜 선택 가능한 캘린더 UI 추가(기본 달력·체리시 캘린더)
    • 일반 모드(절차 개수 표시)와 휴게 모드(상태별 배경·테두리 색상) 지원
    • 로케일 기반 요일 헤더 및 반응형 정사각 일자 그리드
    • 일자 유형 3종(비어있음/일반/휴게), 선택 표시 및 최대 3개 절차 도트 표시
    • 월 헤더 좌/우 내비게이션 아이콘(벡터 리소스) 추가

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

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

coderabbitai Bot commented Jan 9, 2026

Walkthrough

헤더 네비게이션, 월 그리드, 날짜 셀을 포함한 Jetpack Compose 기반 캘린더 컴포넌트와 관련 도메인 모델, 날짜/색상 유틸리티, 네비게이션 벡터 리소스를 추가합니다.

Changes

Cohort / File(s) 주요 변경사항
Calendar Components
app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt, app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt, app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt, app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt, app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt
새로운 Composable UI 추가: 월 렌더링(BasicCalendar), 전체 캘린더 및 헤더 네비게이션(CherrishCalendar), 날짜 셀(DayItem — Empty/Normal/Downtime), 요일 제목(DaysOfWeekTitle), 월 헤더(CalendarMonthHeader). Preview 포함.
Calendar Data Models
app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarDay.kt, app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarDisplayMode.kt, app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarMonth.kt, app/src/main/java/com/cherrish/android/presentation/calendar/model/DownTimeStatus.kt
캘린더 도메인 모델 추가: CalendarDay(Empty/Date->Normal/Downtime), CalendarDisplayMode(Normal/Downtime), CalendarMonth(YearMonth + 주별 일배열), DownTimeStatus enum. Immutable 어노테이션 적용.
Calendar Utilities
app/src/main/java/com/cherrish/android/presentation/calendar/util/DateExtensions.kt, app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt, app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt, app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt
날짜/달 데이터 및 색상 유틸 추가: 지역 기반 요일 리스트, YearMonth/LocalDate 확장, DayOfWeek.daysUntil, generateMonthData로 주·일 구조 생성, 다운타임 상태별 색상 매핑.
Drawable Resources
app/src/main/res/drawable/ic_calendar_left.xml, app/src/main/res/drawable/ic_calendar_right.xml
캘린더 네비게이션용 좌/우 벡터 드로어블 추가.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CherrishCalendar
    participant CalendarMonthHeader
    participant MonthDataGenerator as MonthData
    participant BasicCalendar
    participant DayItem

    User->>CherrishCalendar: 초기 렌더링(yearMonth, displayMode, selectedDate)
    CherrishCalendar->>CalendarMonthHeader: 헤더 렌더 요청 (yearMonth)
    CherrishCalendar->>MonthData: generateMonthData(yearMonth, firstDayOfWeek, displayMode)
    MonthData-->>CherrishCalendar: CalendarMonth(weekDays)
    CherrishCalendar->>BasicCalendar: 그리드 렌더(weekDays)
    BasicCalendar->>DayItem: 각 날짜 셀 렌더링(각 CalendarDay)
    User->>CalendarMonthHeader: 이전/다음 클릭
    CalendarMonthHeader->>CherrishCalendar: onMonthChange 콜백
    CherrishCalendar->>MonthData: 새로운 month 데이터 요청
    User->>DayItem: 날짜 클릭
    DayItem->>CherrishCalendar: onDateClick 콜백
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 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 제목이 주요 변경사항을 명확하게 요약하고 있으며, 캘린더 컴포넌트 구현이라는 핵심 목표를 정확히 반영합니다.
Description check ✅ Passed PR 설명이 템플릿 구조를 따르고 있으며 관련 이슈, 작업 설명, 스크린샷, 검토자에 대한 설명을 포함하고 있습니다.
Linked Issues check ✅ Passed 구현된 코드가 issue #5의 목표인 커스텀 캘린더 컴포넌트 구현을 충족하며, Normal과 DownTime 두 가지 모드 지원과 필요한 유틸 함수들을 모두 포함합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 캘린더 컴포넌트 구현과 관련된 범위 내의 변경사항으로, 불필요한 범위를 벗어난 수정이 없습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 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 ddc7118 and 932e136.

📒 Files selected for processing (2)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.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

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

🤖 Fix all issues with AI agents
In
@app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarHeader.kt:
- Around line 24-60: In CalendarHeader, replace the raw Icon + noRippleClickable
usages for the left/right arrows with IconButton (or an Icon wrapped with
Modifier.clickable + semantics { role = Role.Button }) so the controls expose
proper accessibility role and touch target; provide non-null contentDescription
values (e.g., "Previous month"/"Next month" or localized equivalents) and ensure
the touch target meets the 48.dp minimum (use IconButton default sizing or add
Modifier.size(48.dp) / minimumTouchTarget). Remove noRippleClickable on those
icons and keep the existing onLeftArrowClick/onRightArrowClick handlers wired to
the new clickable/button components.

In
@app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt:
- Around line 87-99: The downtimeByDate map inside the CherrishCalendar
CalendarDisplayMode.Downtime contains duplicate keys for today (the entries in
the downtimeByDate literal overwrite each other), so update the map in
CherrishCalendar to use unique LocalDate keys (e.g., fix the unintended
duplicate at the two today entries by replacing one with the intended date such
as a different offset like today.minusDays(3) or the correct specific date) so
each DownTimeStatus maps to a distinct date; then run the preview/unit check to
confirm the statuses render as expected.

In
@app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt:
- Around line 96-112: DowntimeDateContent calls getDowntimeColors on every
recomposition; memoize the result by using remember inside the
DowntimeDateContent composable so colors are only recalculated when relevant
inputs change (e.g., day.status and the theme colors). Replace the direct call
with a remembered value like remember(day.status, CherrishTheme.colors) {
getDowntimeColors(day.status, CherrishTheme.colors) } inside DowntimeDateContent
and ensure androidx.compose.runtime.remember is imported.
🧹 Nitpick comments (9)
app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt (1)

5-5: 문서화 추가를 권장합니다.

이 유틸리티 함수는 간단하지만, KDoc을 추가하면 사용 의도와 예시가 명확해집니다. 또한 성능 최적화를 위해 inline modifier를 고려해보세요.

📝 문서화 및 최적화 제안
+/**
+ * 현재 요일에서 다른 요일까지의 일수를 계산합니다.
+ * 
+ * @param other 목표 요일
+ * @return 0-6 사이의 일수 (0은 같은 요일을 의미)
+ * 
+ * 예시:
+ * - MONDAY.daysUntil(WEDNESDAY) = 2
+ * - SATURDAY.daysUntil(MONDAY) = 2
+ */
-fun DayOfWeek.daysUntil(other: DayOfWeek) = (other.value - value + 7) % 7
+inline fun DayOfWeek.daysUntil(other: DayOfWeek) = (other.value - value + 7) % 7
app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt (1)

40-47: 테마 색상 및 타이포그래피 사용을 권장합니다.

Color.Black을 직접 하드코딩하는 대신 디자인 시스템의 테마 색상을 사용하고, Text에 명시적인 타이포그래피 스타일을 적용하면 일관성과 유지보수성이 향상됩니다.

🎨 테마 적용 제안
                Text(
                    text = dayOfWeek.getDisplayName(
                        TextStyle.SHORT,
                        Locale.getDefault()
                    ),
+                   style = MaterialTheme.typography.labelMedium,
                    textAlign = TextAlign.Center,
-                   color = Color.Black
+                   color = MaterialTheme.colorScheme.onSurface
                )
app/src/main/res/drawable/ic_chevron_right.xml (1)

6-13: fill과 stroke 속성이 중복 사용되었습니다.

동일한 색상(#1B1D1F)으로 fillColorstrokeColor를 모두 설정하는 것은 일반적으로 불필요합니다. 벡터 드로어블은 보통 fill 또는 stroke 중 하나만 사용합니다. 의도적인 디자인이 아니라면 하나만 유지하는 것을 권장합니다.

🎨 최적화 제안
  <path
      android:pathData="M9.146,6.146C8.951,6.342 8.951,6.658 9.146,6.854L14.793,12.5L9.146,18.146C8.951,18.342 8.951,18.658 9.146,18.854C9.342,19.049 9.658,19.049 9.854,18.854L15.854,12.854C16.049,12.658 16.049,12.342 15.854,12.146L9.854,6.146C9.658,5.951 9.342,5.951 9.146,6.146Z"
-     android:strokeLineJoin="round"
-     android:strokeWidth="0.4"
      android:fillColor="#1B1D1F"
      android:fillType="evenOdd"
-     android:strokeColor="#1B1D1F"
-     android:strokeLineCap="round"/>
+     />

디자인 의도상 stroke가 필요하다면 현재 구현을 유지하세요.

app/src/main/res/drawable/ic_chevron_left.xml (1)

6-13: fill과 stroke 속성이 중복 사용되었습니다.

ic_chevron_right.xml과 동일하게, 동일한 색상으로 fillColorstrokeColor를 모두 설정하는 것은 일반적으로 불필요합니다. 의도적인 디자인이 아니라면 하나만 유지하는 것을 권장합니다.

🎨 최적화 제안
  <path
      android:pathData="M14.854,6.146C15.049,6.342 15.049,6.658 14.854,6.854L9.207,12.5L14.854,18.146C15.049,18.342 15.049,18.658 14.854,18.854C14.658,19.049 14.342,19.049 14.146,18.854L8.146,12.854C7.951,12.658 7.951,12.342 8.146,12.146L14.146,6.146C14.342,5.951 14.658,5.951 14.854,6.146Z"
-     android:strokeLineJoin="round"
-     android:strokeWidth="0.4"
      android:fillColor="#1B1D1F"
      android:fillType="evenOdd"
-     android:strokeColor="#1B1D1F"
-     android:strokeLineCap="round"/>
+     />

디자인 의도상 stroke가 필요하다면 현재 구현을 유지하세요.

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

7-10: DownTimeColors@Immutable(또는 안정성 전략) 추가를 고려해 주세요.
UI 모델로 반복 사용될 가능성이 높아서 Compose에 “이 값은 불변”임을 더 명확히 알려주는 편이 좋아 보입니다. (Color는 값 타입이지만, 이 클래스 자체에 어노테이션이 있으면 추론에 도움)


12-34: getDowntimeColors는 표현식 본문으로 단순화 가능(선택).
기능상 문제는 없어 보이고, 가독성만 약간 개선할 수 있습니다.

제안 diff
 fun getDowntimeColors(
     status: DownTimeStatus,
     colors: CherrishColors
-): DownTimeColors {
-    return when (status) {
+): DownTimeColors =
+    when (status) {
         DownTimeStatus.CAUTION -> DownTimeColors(
             background = colors.red500,
             border = colors.red700
         )
         DownTimeStatus.SENSITIVE -> DownTimeColors(
             background = colors.red300,
             border = colors.red500
         )
         DownTimeStatus.RECOVERY -> DownTimeColors(
             background = colors.red200,
             border = colors.red400
         )
         DownTimeStatus.NONE -> DownTimeColors(
             background = Color.Transparent,
             border = Color.Transparent
         )
     }
-}
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarHeader.kt (1)

44-51: 월 타이틀 문자열은 recomposition 비용 + i18n 관점에서 정리 여지가 있습니다.
Line 45-48에서 Locale.getDefault()/getDisplayName(...) 호출이 매 recomposition마다 발생합니다(핫 패스면 누적). remember(yearMonth, locale)로 title을 만들고, "년" 같은 문구는 가능하면 stringResource로 분리하는 편이 안전합니다.

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

5-13: Compose에서 쓰는 모델이면 Map의 불변성/안정성 보장이 필요합니다.
Map이 mutable backing이면 갱신 누락/예상치 못한 UI 상태가 생길 수 있어, 생성 시 방어적 복사 또는 kotlinx.collections.immutable로 고정하는 것을 권장합니다. 또한 CalendarDisplayMode/각 구현에 @Immutable을 붙이는 것도 고려해 주세요(실제 불변일 때).

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

6-27: 모델 구조는 깔끔합니다(Empty vs Date 변형이 명확).
다만 procedureCount는 음수 입력이 의미적으로 이상하니(Line 19) 필요하면 init { require(procedureCount >= 0) } 같은 불변식 방어를 추가하는 것을 고려해 주세요. Date 네이밍이 도메인에서 혼동될 여지가 있으면(예: CalendarDate)도 선택적으로 검토 가능합니다.

📜 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 edeca4c and a0ae568.

📒 Files selected for processing (15)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarHeader.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarDay.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarDisplayMode.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarMonth.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/DownTimeStatus.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DateExtensions.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt
  • app/src/main/res/drawable/ic_chevron_left.xml
  • app/src/main/res/drawable/ic_chevron_right.xml
🧰 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/CherrishCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarDisplayMode.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/CalendarCalculator.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarMonth.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarHeader.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/DownTimeStatus.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DateExtensions.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/model/CalendarDay.kt
🧬 Code graph analysis (4)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt (4)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarHeader.kt (1)
  • CalendarHeader (24-60)
app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt (1)
  • BasicCalendar (24-58)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (1)
  • DayItem (29-58)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (38-61)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarHeader.kt (1)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (38-61)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (2)
app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt (1)
  • getDowntimeColors (12-34)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (38-61)
app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt (3)
app/src/main/java/com/cherrish/android/presentation/calendar/util/MonthData.kt (1)
  • generateMonthData (53-61)
app/src/main/java/com/cherrish/android/presentation/calendar/util/DateExtensions.kt (1)
  • daysOfWeek (9-13)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt (1)
  • DaysOfWeekTitle (24-51)
⏰ 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 (16)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt (1)

24-51: 코드 구조가 잘 설계되었습니다.

ImmutableList를 사용하여 불필요한 recomposition을 방지하고, weightaspectRatio를 활용해 균일한 정사각형 레이아웃을 구현한 점이 좋습니다. Jetpack Compose 모범 사례를 잘 따르고 있습니다.

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

3-8: 깔끔한 enum 구현입니다.

다운타임 상태를 명확하게 표현하는 간단하고 적절한 enum 클래스입니다. 네이밍 컨벤션을 잘 따르고 있으며 특별한 개선사항이 없습니다.

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

6-10: @Immutable + List<List<CalendarDay>> 조합은 안전합니다.

현재 구현에서 MonthData.kt의 생성 로직을 보면, chunked(7)map()을 통해 새로운 리스트를 생성하고 있어 이미 defensive copying이 이루어지고 있습니다. 또한 CalendarDay는 모든 서브클래스가 @Immutable로 표시되어 있으며, 모든 필드(LocalDate, Int, 열거형)가 불변입니다. 외부에서 뮤터블 backing 리스트를 전달받거나 참조를 공유하지 않으므로 @Immutable 계약이 만족됩니다.

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

23-53: LGTM! 상태 hoisting 패턴이 잘 적용되었습니다.

CherrishCalendar 컴포넌트가 모든 상태(yearMonth, selectedDate)를 외부에서 받고 이벤트를 콜백으로 전달하는 구조로, Compose의 단방향 데이터 흐름을 잘 따르고 있습니다. 재사용성과 테스트 용이성이 우수합니다.


55-77: 프리뷰 구성이 적절합니다.

인터랙티브한 프리뷰를 위해 로컬 상태를 사용한 것이 좋습니다. 개발 중 컴포넌트 동작을 쉽게 확인할 수 있습니다.

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

24-58: LGTM! Recomposition 최적화가 잘 되어 있습니다.

remember의 키(yearMonth, displayMode, firstDayOfWeek)가 적절하게 설정되어 불필요한 재계산을 방지하고 있습니다. dayContent 슬롯 API를 통해 날짜 아이템 렌더링을 외부에 위임하여 컴포넌트의 재사용성과 유연성이 우수합니다.


60-87: 그리드 레이아웃 구현이 적절합니다.

RowColumn을 사용한 주간 그리드 구조가 명확하며, weight(1f)aspectRatio(1f) 조합으로 반응형 정사각형 셀을 구현한 점이 좋습니다.

app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (4)

29-58: LGTM! 타입 안전한 분기 처리가 잘 되어 있습니다.

CalendarDay의 sealed class 구조를 when 표현식으로 처리하여 타입 안전성을 확보했습니다. isSelected 상태를 외부에서 제어하는 것도 적절합니다.


60-94: 선택 상태 처리가 적절합니다.

isSelected에 따른 배경색과 테두리 적용이 명확합니다. procedureCount에 따라 하단에 절차 점을 표시하는 로직도 잘 구현되었습니다.


114-145: 헬퍼 composable들이 잘 구조화되어 있습니다.

DateTextProcedureDots가 단일 책임을 가진 private composable로 잘 분리되었습니다. ProcedureDots에서 최대 3개로 제한하는 로직도 명확합니다.


147-180: 프리뷰가 다양한 상태를 잘 보여줍니다.

Normal, Selected, Downtime(Caution, Recovery) 등 여러 상태의 DayItem을 보여주어 개발 중 시각적 확인이 용이합니다.

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

9-16: LGTM! 로케일 기반 요일 재정렬이 적절합니다.

firstDayOfWeekFromLocale을 사용해 로케일에 따른 주 시작 요일을 가져오고, daysOfWeek 함수에서 이를 기준으로 요일 배열을 재정렬하는 로직이 올바릅니다. 국제화 지원이 잘 되어 있습니다.


18-27: 확장 함수들이 코드 가독성을 높입니다.

YearMonthLocalDate에 대한 확장 함수/프로퍼티들이 명확하고 직관적입니다. 표준 Java Time API를 감싸 도메인 특화 API를 제공하여 캘린더 코드의 가독성을 높였습니다.

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

10-27: LGTM! 캘린더 그리드 계산 로직이 정확합니다.

totalWeeks를 계산할 때 (totalDays + 6) / 7 패턴으로 올림 나눗셈을 구현했고, totalCells를 7의 배수로 맞춰 완전한 주 그리드를 생성하는 로직이 올바릅니다. chunked(7)로 주별 행을 만드는 것도 명확합니다.


29-50: Display mode별 날짜 타입 매핑이 적절합니다.

CalendarDisplayMode에 따라 CalendarDay.Date.Normal 또는 CalendarDay.Date.Downtime을 생성하는 로직이 명확합니다. Downtime 모드에서 status가 NONE일 때 Normal 타입으로 폴백하는 것도 합리적인 설계입니다.


53-61: daysUntil 확장 함수는 같은 패키지의 CalendarCalculator.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/component/CalendarMonthHeader.kt:
- Around line 53-58: The Icon in CalendarMonthHeader sets contentDescription =
null which breaks accessibility; replace the null with a localized
stringResource (e.g., stringResource(id = R.string.calendar_next_month)) so the
onRightArrowClick navigation is announced by screen readers, and add the
corresponding <string name="calendar_next_month">다음 달</string> to strings.xml;
update the Icon call (the ImageVector.vectorResource +
modifier.noRippleClickable(onClick = onRightArrowClick)) to pass the
stringResource as contentDescription.
- Around line 37-42: Replace the null contentDescription on the navigation Icon
in CalendarMonthHeader.kt with a localized string resource (e.g., use
stringResource(R.string.calendar_previous_month)) so screen readers announce the
button; add the suggested entry to strings.xml ("calendar_previous_month" -> "이전
달") and import/function-call stringResource in the composable, keeping the rest
of the Icon (tint, modifier/noRippleClickable and onLeftArrowClick) unchanged.
🧹 Nitpick comments (1)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt (1)

44-51: 로케일 처리 및 문자열 하드코딩 개선 권장

현재 구현에서 두 가지 개선 사항을 고려해보세요:

  1. Locale.getDefault() 사용: 앱이 특정 로케일(한국어)을 강제해야 하는 경우 Locale.KOREAN을 명시적으로 사용하는 것이 일관성 있는 동작을 보장합니다.
  2. "년" 문자열 하드코딩: 문자열 리소스로 분리하면 유지보수성과 향후 다국어 지원이 용이해집니다.
♻️ 개선 제안

방법 1: 명시적 로케일 사용

         Text(
-            text = "${yearMonth.year}년 ${yearMonth.month.getDisplayName(
-                TextStyle.SHORT,
-                Locale.getDefault()
-            )}",
+            text = "${yearMonth.year}년 ${yearMonth.month.getDisplayName(
+                TextStyle.SHORT,
+                Locale.KOREAN
+            )}",
             color = CherrishTheme.colors.gray1000,
             style = CherrishTheme.typography.title2M16
         )

방법 2: 문자열 리소스 사용 (더 권장)

strings.xml에 추가:

<string name="calendar_year_month_format">%d년 %s</string>

코드 수정:

Text(
    text = stringResource(
        R.string.calendar_year_month_format,
        yearMonth.year,
        yearMonth.month.getDisplayName(TextStyle.SHORT, Locale.KOREAN)
    ),
    color = CherrishTheme.colors.gray1000,
    style = CherrishTheme.typography.title2M16
)
📜 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 a0ae568 and 290477c.

📒 Files selected for processing (4)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt
  • app/src/main/res/drawable/ic_calendar_left.xml
  • app/src/main/res/drawable/ic_calendar_right.xml
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/res/drawable/ic_calendar_left.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.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/CalendarMonthHeader.kt
🧬 Code graph analysis (1)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.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). (1)
  • GitHub Check: PR Build Check
🔇 Additional comments (2)
app/src/main/res/drawable/ic_calendar_right.xml (1)

1-14: LGTM - 아이콘 리소스가 올바르게 정의되었습니다.

벡터 드로어블이 표준 형식으로 잘 정의되어 있으며, 네비게이션 아이콘으로 사용하기에 적합합니다.

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

24-60: 전반적인 구조는 우수합니다

컴포넌트가 stateless로 잘 설계되어 있고, 콜백 기반의 단방향 데이터 흐름을 따르고 있습니다. 불필요한 recomposition 트리거도 없으며, modifier 순서도 올바릅니다. 위에서 언급한 접근성과 로케일 처리만 개선하면 프로덕션 준비가 완료됩니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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

23-53: 전체적으로 깔끔한 구현입니다.

단방향 데이터 흐름 패턴을 잘 따르고 있으며, 상태와 콜백을 명확하게 분리했습니다.

다만, 47번 라인의 isSelected 계산이 각 날짜 셀마다 반복 실행됩니다. 월별로 35-42개의 셀이 있으므로 최적화를 고려할 수 있습니다.

♻️ 선택적 최적화: isSelected 계산 메모이제이션

BasicCalendar 호출 전에 선택된 날짜를 미리 계산하여 재사용할 수 있습니다:

+    val selectedDateValue = selectedDate
+
     BasicCalendar(
         yearMonth = yearMonth,
         displayMode = displayMode,
         dayContent = { day ->
             DayItem(
                 day = day,
-                isSelected = day is CalendarDay.Date && day.date == selectedDate,
+                isSelected = day is CalendarDay.Date && day.date == selectedDateValue,
                 onDateClick = onDateClick
             )
         }
     )

또는 remember로 람다 자체를 메모이제이션:

+    val dayContent = remember(selectedDate, onDateClick) {
+        { day: CalendarDay ->
+            DayItem(
+                day = day,
+                isSelected = day is CalendarDay.Date && day.date == selectedDate,
+                onDateClick = onDateClick
+            )
+        }
+    }
+
     BasicCalendar(
         yearMonth = yearMonth,
         displayMode = displayMode,
-        dayContent = { day ->
-            DayItem(
-                day = day,
-                isSelected = day is CalendarDay.Date && day.date == selectedDate,
-                onDateClick = onDateClick
-            )
-        }
+        dayContent = dayContent
     )
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (1)

37-59: 중복된 타입 체크를 제거하면 더 깔끔합니다.

40-43번 라인에서 enabled 파라미터가 이미 day is CalendarDay.Date를 체크하고 있으므로, onClick 람다 내부의 동일한 체크(42번 라인)는 불필요합니다.

♻️ 제안: 중복 체크 제거
     Box(
         modifier = modifier
             .aspectRatio(1f)
             .noRippleClickable(
                 enabled = day is CalendarDay.Date,
-                onClick = { if (day is CalendarDay.Date) onDateClick(day.date) }
+                onClick = { onDateClick((day as CalendarDay.Date).date) }
             ),
         contentAlignment = Alignment.Center
     ) {

enabled가 false일 때는 onClick이 호출되지 않으므로, 람다 내부에서는 안전하게 타입 캐스팅할 수 있습니다.

📜 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 290477c and 5d7708f.

📒 Files selected for processing (2)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.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/CherrishCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt
🧬 Code graph analysis (2)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt (4)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt (1)
  • CalendarMonthHeader (24-60)
app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt (1)
  • BasicCalendar (24-58)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (1)
  • DayItem (30-59)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (38-61)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (2)
app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt (1)
  • getDowntimeColors (12-34)
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 (5)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt (1)

57-103: Preview 함수들이 잘 구현되었습니다.

두 모드(Normal, Downtime)를 각각 시연하며, remembermutableStateOf를 적절히 사용하여 인터랙티브한 상태를 표현하고 있습니다. Preview 내부에서의 로컬 상태 관리가 올바릅니다.

app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (4)

61-95: NormalDateContent 구현이 깔끔합니다.

조건부 modifier를 .then() 패턴으로 처리한 것이 적절하며, procedureCount > 0일 때만 ProcedureDots를 렌더링하는 최적화도 좋습니다. 선택 상태에 따른 배경과 테두리 처리 로직이 명확합니다.


97-116: Color 계산 메모이제이션이 잘 적용되었습니다.

101-104번 라인에서 remember를 사용하여 getDowntimeColors() 계산 결과를 메모이제이션하고, day.statuscherrishColors를 키로 지정한 것이 좋습니다. 불필요한 재계산을 방지하는 효과적인 최적화입니다.


118-149: Helper composable들이 잘 구현되었습니다.

DateText는 간결하며, ProcedureDots는 132번 라인에서 min(count, 3)으로 최대 3개까지만 표시하도록 제한하여 UI 일관성을 유지합니다. repeat()를 사용한 반복 렌더링이 적절합니다.


151-184: Preview가 다양한 상태를 잘 시연하고 있습니다.

여러 날짜 유형(Normal 기본/선택/절차 표시, Downtime CAUTION/RECOVERY)을 포괄적으로 보여주어 컴포넌트의 동작을 효과적으로 확인할 수 있습니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt (2)

7-10: Compose 재구성 최적화를 위해 DownTimeColors에 안정성 애노테이션(예: @Immutable) 고려
지금도 동작엔 문제 없지만, Compose에서 값 타입으로 자주 전달될 가능성이 높아서 안정성 힌트를 주는 게 도움이 됩니다. (또한 외부 공개가 필요 없으면 internal도 고려)

제안 diff
 import androidx.compose.ui.graphics.Color
+import androidx.compose.runtime.Immutable
 import com.cherrish.android.core.designsystem.theme.CherrishColors
 import com.cherrish.android.presentation.calendar.model.DownTimeStatus

+@Immutable
-data class DownTimeColors(
+internal data class DownTimeColors(
     val background: Color,
     val border: Color
 )

12-34: 매핑 함수는 확장함수 형태로 바꾸면 호출부 가독성이 좋아집니다(선택)
status.toDownTimeColors(colors) 형태가 모드 분리/의도 전달에 더 직접적입니다. 또한 호출부(Compose)에서 반복 호출된다면 remember(status, colors)로 감싸졌는지도 확인해 주세요.

제안 diff
-fun getDowntimeColors(
-    status: DownTimeStatus,
-    colors: CherrishColors
-): DownTimeColors {
-    return when (status) {
+internal fun DownTimeStatus.toDownTimeColors(colors: CherrishColors): DownTimeColors {
+    return when (this) {
         DownTimeStatus.SENSITIVE -> DownTimeColors(
             background = colors.red500,
             border = colors.red700
         )
         DownTimeStatus.CAUTION -> DownTimeColors(
             background = colors.red300,
             border = colors.red500
         )
         DownTimeStatus.RECOVERY -> DownTimeColors(
             background = colors.red200,
             border = colors.red400
         )
         DownTimeStatus.NONE -> DownTimeColors(
             background = Color.Transparent,
             border = Color.Transparent
         )
     }
 }
app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt (1)

23-53: 단방향 데이터 흐름과 컴포넌트 구조가 잘 설계되었습니다.

상태를 파라미터로 받고 이벤트를 콜백으로 전달하는 구조가 명확하며, CalendarMonthHeader와 BasicCalendar로의 책임 분리도 적절합니다.

선택적 개선 사항

1. dayContent 람다 안정성 개선

현재 dayContent 람다(44-50줄)는 CherrishCalendar가 recompose될 때마다 재생성됩니다. 성능에 민감한 상황이라면 remember로 메모이제이션을 고려할 수 있습니다:

+    val dayContent: @Composable (CalendarDay) -> Unit = remember(selectedDate, onDateClick) {
+        { day ->
+            DayItem(
+                day = day,
+                isSelected = day is CalendarDay.Date && day.date == selectedDate,
+                onDateClick = onDateClick
+            )
+        }
+    }
+
     BasicCalendar(
         yearMonth = yearMonth,
         displayMode = displayMode,
-        dayContent = { day ->
-            DayItem(
-                day = day,
-                isSelected = day is CalendarDay.Date && day.date == selectedDate,
-                onDateClick = onDateClick
-            )
-        }
+        dayContent = dayContent
     )

2. firstDayOfWeek 파라미터 노출 고려

BasicCalendarfirstDayOfWeek 파라미터를 지원하지만(기본값: SUNDAY), CherrishCalendar에서 이를 노출하지 않아 유연성이 제한됩니다. 국제화 요구사항이 있다면 추가 고려가 필요할 수 있습니다.

📜 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 5d7708f and c00dcc6.

📒 Files selected for processing (2)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.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/util/DownTimeColors.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt
🧬 Code graph analysis (1)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt (4)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt (1)
  • CalendarMonthHeader (24-60)
app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt (1)
  • BasicCalendar (24-58)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (1)
  • DayItem (30-59)
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 Lint Check
  • GitHub Check: PR Build Check
🔇 Additional comments (2)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CherrishCalendar.kt (2)

55-77: 프리뷰 구현이 적절합니다.

로컬 상태 관리가 올바르게 구현되었고, Normal 모드의 동작을 효과적으로 시연합니다.


79-103: Downtime 모드 프리뷰가 잘 구현되었습니다.

다양한 DownTimeStatus 값을 활용한 샘플 데이터로 Downtime 모드의 시각적 동작을 효과적으로 보여줍니다.

Copy link
Copy Markdown
Contributor

@hyeminililo hyeminililo left a comment

Choose a reason for hiding this comment

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

코드 진짜 깔끔한 것 같아요 ,,!! 수고하셨습니다 ! !

shape = RoundedCornerShape(8.dp)
)
} else {
Modifier
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.

P4: 이렇게 되면 어떻게 구현되는지 알 수 있을까요 ? 이 부분 코드에 대해 조금 더 자세한 설명 듣고 싶습니다 :)

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.

이 부분은 isSelected 상태에 따라 조건부로 테두리를 추가하는 부분이에용 !! 컴포즈에서 Modifier는 체이닝 방식으로 연결되는데, 조건에 따라 특정 Modifier를 적용하거나 생략하고 싶을 때 .then() 메서드를 사용한답니도 ㅎㅎ ~~ 얘는 isSelectedtrue일 때는 1.dp 두께의 회색 테두리를 추가하고, false일 때는 아무것도 추가하지 않는 빈 Modifier를 반환해용 그래서 선택된 날짜에만 테두리가 표시되어 사용자가 현재 선택한 날짜를 시각적으로 구분할 수 있게 해주는 코드랍니도 ~~

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.

커스텀 캘린더라니..!!! 이걸 보고 잇자니 제가 예전에 만들었던 밤티 캘린더가 생각이 나는군요...
많이 배워갑니다 !! 너무 수고하셨습니다 굿 👍♥️

background = colors.red200,
border = colors.red400
)
DownTimeStatus.NONE -> DownTimeColors(
Copy link
Copy Markdown
Contributor

@usuuhyn usuuhyn Jan 10, 2026

Choose a reason for hiding this comment

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

P2 : MonthData.getDay 기준으로 보면 NONENormal로 내려가서,
현재 구조에서는 이 NONE 케이스가 실제로 쓰이지 않을 것 같더라구요!
확장 대비용인지 아니면 정리해도 될 부분인지 궁금합니다옹 ! 틀린 내용이라면 편히 알려주세용 🫶😻

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.

만약 NONE을 제거하게 되면 DownTimeStatus가 nullable(DownTimeStatus?)이 되어야 하고 그 경우 이후 로직 전반에서 null 안정성을 보장하기 위한 분기 처리가 계속 추가되어야 해용 이렇게 되면 상태 분기 로직이 여기저기 흩어지고 UI에서도 항상 null이 올 수 있다는 전제를 가지고 코드를 작성해야 해서 실수로 케이스를 놓칠 가능성이 커진다고 판단햇더용 ! 이를 방지하기 위해 DownTimeStatus는 항상 non-null로 유지하고 그 대신 명시적인 NONE 케이스를 두는 쪽을 선택했답니도 ㅎㅎ

Copy link
Copy Markdown
Contributor

@usuuhyn usuuhyn Jan 10, 2026

Choose a reason for hiding this comment

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

P5 : DayItem 커스텀 너무 흥미롭군... 재밋엉..

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.

수고하셨습니다!!
커스텀 구현이 쉽지 않으셨을텐데 너무 깔끔하게 잘 짜주셔서 재미있게 읽으면서 공부했습니다!!

modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
daysOfWeek.forEach { dayOfWeek ->
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.

p2: 요기 key값 활용하기??!

) {
Text(
text = dayOfWeek.getDisplayName(
TextStyle.SHORT,
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.

요거 짧게 보여주는 라이브러리 기능 신기하네여~~!!

TextStyle.SHORT,
Locale.getDefault()
),
textAlign = TextAlign.Center,
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: 여기 궁금한게 TextAlign.Center 없어도 박스에 contentAlignment 때문에 동일하지 않나요??

추가로 텍스트의 영역이 텍스트의 크기만큼 적용되어서 textAlign = TextAlign.Center로 가운데 정렬을 원하시면 fillMaxWidth를 사용해서 영역을 정해주고 그 영역에서 가운데 정렬해주는 방식이 의도에 더 맞을 것 같습니다!

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.

허거덩 textAlign = TextAlign.Center 요거 빼야겟다 !!! 짚어주셔서 넘 감사합니다잉 !~~~


Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(2.dp)
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: 여기 점들 사이 간격이 4.dp인 것 같습니다! 확인 한 번만 부탁드립니다!
(틀린거면 미리 죄송합니다..ㅎㅎ)

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.

허거덩 맞네요 !!!!!! 반영하겟습니다 ㅎㅎ

isSelected: Boolean = false
) {
Box(
modifier = Modifier
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.

p2: 컴포넌트로 만든다는게 가독성을 높인 동시에 저는 개인적으로 재사용성이라는 가능성에 대해서 조금이라도 열어두는 의미라고 생각합니다.
그래서 여기도 modifier 뚫어놓는거 어떠신가요??

근데 한 가지 고민되는건 제가 말한 식이면 컴포넌트 분리했을 경우 다 modifier를 뚫어놓는게 맞는건데 다 그렇게 뚫어놓는게 굳이 싶은 일인가 싶기도 하네여...ㅎㅎ

이것 관련해서 나현님 의견이 궁금합니다!

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.

저도 컴포넌트로 분리한다는 게 단순히 파일을 나누는 목적보다는 가독성을 높이면서도 재사용 가능성에 대한 여지를 조금이라도 열어두는 의미라고 생각해서 modifier를 열어두자는 의견 자체에는 공감합니다 !!!

다만 개인적으로는 모든 컴포넌트에 일괄적으로 modifier를 받도록 하기보다는 이 컴포넌트가 실제로 외부에서 레이아웃이나 크기 위치 제어가 필요할 가능성이 있는지를 기준으로 판단하는 편이에용 ㅎㅎㅎ

이번 경우의 NormalDateContentDowntimeDateContentDayItem 내부에서만 사용되는 구성 요소이구 실제 크기나 정렬, 클릭 영역 같은 레이아웃 책임은 상위인 DayItem이 가지고 있다고 생각했어요. 그래서 이 컴포넌트들은 내부 표현(UI 디테일)에 집중하는 역할로 두고 현 시점에서는 modifier를 굳이 열지 않아도 역할이 충분히 명확하다고 판단햇습니당.. !!!

말씀 주신 것처럼 컴포넌트를 분리할 때마다 전부 modifier를 열어두면 재사용성 측면에서는 유연해질 수 있지만, 동시에 불필요하게 커지거나 컴포넌트의 책임 경계가 흐려질 수 있다는 고민도 들었어유... !!!

그래서 저는 외부에서 배치나 크기 조절이 필요할 가능성이 높은 공개 성격의 컴포넌트에는 modifier를 열어두고 내부 구현용이나 조합용 컴포넌트에는 굳이 열지 않는 쪽으로 구현해보앗습니다 ㅎㅎㅎ

Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier.fillMaxWidth()
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: 여기 modifier에 2개 쓰고 있는데..... fillMaxWidth도 한칸 띄어서 써주시면 안되나여...??🥹

@Composable
private fun CalendarMonthGrid(
month: CalendarMonth,
dayContent: @Composable (CalendarDay) -> Unit,
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.

p2: 이거 modifier 순서 확인 부탁드립나다~!!

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 YearMonth.atStartOfMonth(): LocalDate = this.atDay(1)

val LocalDate.yearMonth: YearMonth
get() = YearMonth.of(year, month)
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: 지금 캘린더 쪽 코드 보면 날짜를 다 LocalDate 기반으로 사용하고 계신 거 같은데요!
그래서 연,월을 직접 조합하기 보다는 YearMonth의 from(LocalDate)을 써서 연,월을 추출해서 사용하는 것이 자연스러울 수 있겠다는 생각이 들었습니다.
혹시 나현님께서 of로 이 부분 코드 구현하신 이유가 있으실까요??

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.

오오오옹 !!! from을 사용하는 것도 좋네요 ㅎㅎ YearMonth.from(LocalDate)를 사용하는 게 흐름상 더 자연스러울 수 있을 것 같아요 !! 저는 LocalDate가 이미 year, month 값을 가지고 있어서 이를 명시적으로 조합하는 형태(of)로 표현하고 싶었던 의도가 있었어요. 말씀 주신 것처럼 from을 쓰는 쪽이 의도와 일관성 면에서 더 깔끔해 보여서 해당 방향으로 수정하는 것도 충분히 좋다고 생각합니다 ㅎㅎㅎ 반영해둘게요 !!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt (2)

38-43: 접근성(Accessibility) 개선 필요: contentDescription 누락

네비게이션 아이콘에 contentDescription = null이 설정되어 있어 스크린 리더 사용자가 버튼의 기능을 알 수 없습니다. 이전/다음 월 이동 기능을 설명하는 문자열을 추가해 주세요.

♻️ 제안하는 수정
         Icon(
             imageVector = ImageVector.vectorResource(id = R.drawable.ic_calendar_left),
-            contentDescription = null,
+            contentDescription = "이전 달",
             tint = Color.Unspecified,
             modifier = Modifier.noRippleClickable(onClick = onLeftArrowClick)
         )
         Icon(
             imageVector = ImageVector.vectorResource(id = R.drawable.ic_calendar_right),
-            contentDescription = null,
+            contentDescription = "다음 달",
             tint = Color.Unspecified,
             modifier = Modifier.noRippleClickable(onClick = onRightArrowClick)
         )

45-52: 로케일 일관성 검토 필요

월(month)은 Locale.getDefault()를 사용하지만, 연도 뒤의 "년"은 하드코딩되어 있습니다. 다국어 지원 시 일관성 문제가 발생할 수 있습니다.

현재 앱이 한국어만 지원한다면 괜찮지만, 향후 다국어 지원 계획이 있다면 DateTimeFormatter나 리소스 문자열 사용을 고려해 주세요.

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

40-43: 클릭 핸들러 중복 체크 개선 가능

enabled = day is CalendarDay.Date로 클릭을 비활성화했음에도 onClick 내부에서 다시 타입 체크를 수행합니다. enabled가 false면 onClick이 호출되지 않으므로 내부 체크는 불필요합니다.

♻️ 제안하는 수정
         .noRippleClickable(
             enabled = day is CalendarDay.Date,
-            onClick = { if (day is CalendarDay.Date) onDateClick(day.date) }
+            onClick = { (day as? CalendarDay.Date)?.date?.let(onDateClick) }
         ),

또는 smart cast를 활용:

.noRippleClickable(
    enabled = day is CalendarDay.Date,
    onClick = { onDateClick((day as CalendarDay.Date).date) }
)

127-149: Kotlin 관용적 표현 사용 제안

min(count, 3) 대신 coerceAtMost(3)를 사용하면 더 Kotlin스러운 코드가 됩니다.

♻️ 제안하는 수정
-import kotlin.math.min
...
 @Composable
 private fun ProcedureDots(
     count: Int,
     modifier: Modifier = Modifier
 ) {
-    val displayCount = min(count, 3)
+    val displayCount = count.coerceAtMost(3)
 
     Row(
📜 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 c00dcc6 and ddc7118.

📒 Files selected for processing (5)
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DateExtensions.kt
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/java/com/cherrish/android/presentation/calendar/util/DateExtensions.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DaysOfWeekTitle.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/BasicCalendar.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/CalendarMonthHeader.kt
  • app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt
🧬 Code graph analysis (2)
app/src/main/java/com/cherrish/android/presentation/calendar/component/CalendarMonthHeader.kt (1)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Theme.kt (1)
  • CherrishTheme (38-61)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (2)
app/src/main/java/com/cherrish/android/presentation/calendar/util/DownTimeColors.kt (1)
  • getDowntimeColors (12-34)
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 Lint Check
  • GitHub Check: PR Build Check
🔇 Additional comments (2)
app/src/main/java/com/cherrish/android/presentation/calendar/component/DayItem.kt (2)

97-116: LGTM - 적절한 remember 사용

rememberday.statuscherrishColors를 키로 사용하여 불필요한 재계산을 방지했습니다. 상태 변경 시에만 getDowntimeColors가 호출되어 recomposition 최적화에 적합합니다.


61-95: NormalDateContent 구현 검토

구조는 적절하나, 선택 상태에 따른 조건부 스타일링이 다소 장황합니다. Modifier.then()을 통한 조건부 border 적용은 정상 작동하지만, 가독성을 위해 별도 함수로 추출하는 것도 고려해 볼 수 있습니다.

현재 구현은 기능적으로 문제없습니다.

@nhyeonii nhyeonii merged commit f29bcf7 into develop Jan 10, 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] 캘린더 컴포넌트 구현

4 participants