feat: 홈 화면 UI 구현#23
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough홈 화면의 API·도메인 데이터 계약, 저장소와 ViewModel 흐름, 예약·통화 기록·알림 Compose UI, 시간·이미지 표시 유틸리티, 디자인 리소스와 앱 레이아웃이 추가되었습니다. Changes홈 화면 기능
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant HomeScreen
participant HomeViewModel
participant HomeRepository
participant HomeScreenContent
HomeScreen->>HomeViewModel: HomeState 구독
HomeViewModel->>HomeRepository: 홈 요약·예약·통화 기록 조회
HomeRepository-->>HomeViewModel: Result 도메인 데이터 반환
HomeViewModel->>HomeScreenContent: UI 상태와 알림 Flow 전달
HomeScreenContent-->>HomeScreen: 헤더·예약·히스토리 UI 렌더링
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Gemini AI 코드리뷰안녕하세요! GitHub Pull Request 리뷰 요청해주셔서 감사합니다. 시니어 Android 개발자의 관점에서 PR diff를 꼼꼼히 검토했습니다. 전반적으로 코드 구조가 깔끔하고, 다만, 몇 가지 개선 사항과 잠재적 문제점을 발견하여 아래와 같이 상세한 피드백을 드립니다. 1.
|
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (1)
feature/home/impl/src/main/java/kr/co/call/impl/mapper/CallHistoryUiMapper.kt (1)
19-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
toCallHistoryType()의else브랜치를 명시적으로 작성하세요.
else가RECEIVE_MISSED로 매핑되는데, 이는sender == CallSender.AI && status == CallStatus.MISSED케이스를 암시적으로 처리합니다. 향후CallStatus에 새 값(예:CANCELLED)이 추가되면 의도치 않게RECEIVE_MISSED로 분류됩니다.♻️ 제안하는 수정
private fun CallHistory.toCallHistoryType(): CallHistoryType = when { sender == CallSender.USER && status == CallStatus.COMPLETED -> CallHistoryType.SEND sender == CallSender.AI && status == CallStatus.COMPLETED -> CallHistoryType.RECEIVED sender == CallSender.USER && status == CallStatus.MISSED -> CallHistoryType.SEND_MISSED - else -> + sender == CallSender.AI && status == CallStatus.MISSED -> CallHistoryType.RECEIVE_MISSED + + else -> + CallHistoryType.RECEIVE_MISSED // 또는 예외 처리 }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/home/impl/src/main/java/kr/co/call/impl/mapper/CallHistoryUiMapper.kt` around lines 19 - 32, Update CallHistory.toCallHistoryType() to explicitly match the AI sender with CallStatus.MISSED and return CallHistoryType.RECEIVE_MISSED. Remove the catch-all else branch so future CallStatus values cannot be implicitly classified as missed calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/data/src/main/java/kr/co/call/data/di/RepositoryModule.kt`:
- Around line 35-48: Update the mock reservation returned by getReservations in
RepositoryModule so scheduledAt is in the future relative to the current
mock/test date, or use the project’s test Clock to define that reference time.
Keep the reservation status and response structure unchanged while ensuring
HomeReservationCard cannot render this fixture as a past appointment.
In
`@core/data/src/main/java/kr/co/call/data/repositoryImpl/HomeRepositoryImpl.kt`:
- Around line 14-27: Update getReservations, getCallHistories, and getSummary to
validate each API wrapper’s isSuccess before mapping result data. For
unsuccessful responses, convert the failure using the response code and message
into the repository’s domain-level error result, while preserving the existing
mappings for successful responses and DataSource exception conversion.
In `@core/domain/src/main/java/kr/co/call/domain/model/home/HomeModel.kt`:
- Line 8: Remove the nested HomeModel.CallHistory declaration and ensure
HomeModel.callHistories resolves to the top-level
kr.co.call.domain.model.home.CallHistory type. Keep the existing
CallHistoryMapper and CallMockData return types unchanged so HomeModel
construction accepts their results.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeHeader.kt`:
- Around line 49-52: Update HomeHeader to accept callbacks for the main-lover
replacement and phone actions, then invoke them from the corresponding header
buttons instead of no-ops. Pass these callbacks from the parent screen and
connect them to the appropriate ViewModel intent or side-effect handling,
preserving the existing UI behavior.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeReservationCard.kt`:
- Around line 202-209: Update the CompositionLocalProvider surrounding the “시간
변경” Button in HomeReservationCard so LocalMinimumInteractiveComponentSize no
longer overrides the minimum to 0.dp. Preserve the button’s 27.dp visual height
while restoring the standard minimum 48.dp touch target.
In `@feature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.kt`:
- Around line 68-104: HomeScreenContent currently leaves the time-change,
record-view, and callback actions unconnected. In
feature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.kt:68-104, add
these callbacks to the screen contract and pass them through HomeReservationCard
and HomeHistorySection; in
feature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeReservationCard.kt:43-46
and
feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt:40-69,
remove the no-op default callbacks; in
feature/home/impl/src/main/java/kr/co/call/impl/component/history/HomeHistorySection.kt:16-39,
accept and forward the history and call callbacks; in
feature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.kt:49-87,
replace the hardcoded empty onCallClick with the propagated external callback.
In `@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/HomeViewModel.kt`:
- Around line 18-19: HomeViewModel이 직접 수행하는 세 Repository 호출과 HomeModel 조합을
core:domain의 GetHomeUseCase로 이동하세요. GetHomeUseCase가 필요한 Repository를 호출해
HomeModel을 반환하도록 구성하고, HomeViewModel은 이를 주입받아 호출한 뒤 UI 상태 변환만 담당하게 하세요.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeReservationState.kt`:
- Line 1: Update the UI state data-class defaults in HomeReservationState,
HomeSummaryState, and HomeState to neutral values: use null or an empty string
for reservation name fields, 0 for summary counters, false for
hasUnreadNotification, and null for scheduledAt. Keep mock data in dedicated
mock-data layers such as HomeMockData.kt rather than state defaults.
- Around line 10-12: HomeReservationState의 scheduledAt과 firstName 기본값에 하드코딩된
mock 데이터를 제거하고 각각 null 등 중성 기본값으로 변경하세요. 실제 mock 데이터는 HomeMockData.kt 같은 별도 mock
레이어에서 제공하며, API 로딩 전이나 빈 응답에서는 가짜 값이 표시되지 않도록 하세요.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeState.kt`:
- Line 12: Update the hasUnreadNotification property in HomeState so its default
value is false; preserve updating it with the actual notification state after
data loading.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeSummaryState.kt`:
- Around line 5-8: HomeSummaryState의 firstName, relationshipDays,
totalCallCount, callStreakDays 기본값에서 하드코딩된 mock 데이터를 제거하고 각각 "" 또는 0의 중성값을
사용하세요. mock 데이터가 필요하면 State 외부의 별도 레이어에서 제공하도록 유지하세요.
---
Nitpick comments:
In
`@feature/home/impl/src/main/java/kr/co/call/impl/mapper/CallHistoryUiMapper.kt`:
- Around line 19-32: Update CallHistory.toCallHistoryType() to explicitly match
the AI sender with CallStatus.MISSED and return CallHistoryType.RECEIVE_MISSED.
Remove the catch-all else branch so future CallStatus values cannot be
implicitly classified as missed calls.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e1a7c511-1914-4a64-ba81-d500c6c298e8
⛔ Files ignored due to path filters (5)
core/designsystem/src/main/res/drawable/img_call_logo.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable/img_home_call_missed.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable/img_home_call_received.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable/img_home_call_send.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable/img_profile_url_default.pngis excluded by!**/*.png
📒 Files selected for processing (53)
core/common/src/main/java/kr/co/call/core/common/util/TimeUtil.ktcore/data/build.gradle.ktscore/data/src/main/java/kr/co/call/data/di/RepositoryModule.ktcore/data/src/main/java/kr/co/call/data/mapper/CallHistoryMapper.ktcore/data/src/main/java/kr/co/call/data/repositoryImpl/HomeRepositoryImpl.ktcore/designsystem/build.gradle.ktscore/designsystem/src/main/java/kr/co/call/designsystem/component/ProfileImageWithIcon.ktcore/designsystem/src/main/java/kr/co/call/designsystem/component/gitkeepcore/designsystem/src/main/res/drawable/ic_home_alarm.xmlcore/designsystem/src/main/res/drawable/ic_home_call.xmlcore/designsystem/src/main/res/drawable/ic_home_call_btn.xmlcore/designsystem/src/main/res/drawable/ic_home_record.xmlcore/domain/src/main/java/kr/co/call/domain/model/home/.gitkeepcore/domain/src/main/java/kr/co/call/domain/model/home/CallHistory.ktcore/domain/src/main/java/kr/co/call/domain/model/home/CallSender.ktcore/domain/src/main/java/kr/co/call/domain/model/home/CallStatus.ktcore/domain/src/main/java/kr/co/call/domain/model/home/CallType.ktcore/domain/src/main/java/kr/co/call/domain/model/home/HomeModel.ktcore/domain/src/main/java/kr/co/call/domain/repository/HomeRepository.ktcore/network/src/main/java/kr/co/call/network/api/HomeApi.ktcore/network/src/main/java/kr/co/call/network/dto/CallHistoryListResponseDto.ktcore/network/src/main/java/kr/co/call/network/dto/HomeSummaryResponseDto.ktcore/network/src/main/java/kr/co/call/network/dto/ReservationListResponseDto.ktfeature/home/impl/build.gradle.ktsfeature/home/impl/src/main/java/kr/co/call/impl/component/.gitkeepfeature/home/impl/src/main/java/kr/co/call/impl/component/UnreadIndicator.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeHeader.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeReservationCard.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/RelationshipSummaryCard.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/HomeHistorySection.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.ktfeature/home/impl/src/main/java/kr/co/call/impl/entry/.gitkeepfeature/home/impl/src/main/java/kr/co/call/impl/mapper/CallHistoryUiMapper.ktfeature/home/impl/src/main/java/kr/co/call/impl/mapper/HomeUiMapper.ktfeature/home/impl/src/main/java/kr/co/call/impl/mock/CallMockData.ktfeature/home/impl/src/main/java/kr/co/call/impl/screen/.gitkeepfeature/home/impl/src/main/java/kr/co/call/impl/screen/HomeMockData.ktfeature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.ktfeature/home/impl/src/main/java/kr/co/call/impl/tab/HomeHistoryTab.ktfeature/home/impl/src/main/java/kr/co/call/impl/tab/HomeHistoryTabBar.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/.gitkeepfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/CallHistoryType.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/HomeIntent.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/HomeSideEffect.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/HomeViewModel.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/NotificationType.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/CallHistoryState.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeCallHistoryItemState.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeNotificationState.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeReservationState.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeState.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeSummaryState.kt
💤 Files with no reviewable changes (1)
- core/designsystem/src/main/java/kr/co/call/designsystem/component/gitkeep
| override suspend fun getReservations(): Result<HomeModel.Reservations> = | ||
| runCatching { | ||
| homeApi.getReservations().result.toDomain() | ||
| } | ||
|
|
||
| override suspend fun getCallHistories(): Result<List<CallHistory>> = | ||
| runCatching { | ||
| homeApi.getCallHistories(size = CALL_HISTORY_SIZE).result.content.map { it.toDomain() } | ||
| } | ||
|
|
||
| override suspend fun getSummary(characterId: Long): Result<HomeModel.Summary> = | ||
| runCatching { | ||
| homeApi.getSummary(characterId).result.toDomain(characterId) | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
업무 실패 응답을 성공 데이터로 매핑하지 마세요.
세 메서드 모두 wrapper의 isSuccess를 무시하고 result를 바로 변환합니다. HTTP 200이지만 업무적으로 실패한 응답은 성공으로 처리되어 잘못된 홈 상태를 렌더링할 수 있습니다. 매핑 전에 isSuccess를 검증하고, 실패 시 code와 message를 포함한 실패 결과로 변환하세요.
As per path instructions, “DataSource(Remote/Local) 예외를 Repository에서 도메인 레벨 에러로 적절히 변환”해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@core/data/src/main/java/kr/co/call/data/repositoryImpl/HomeRepositoryImpl.kt`
around lines 14 - 27, Update getReservations, getCallHistories, and getSummary
to validate each API wrapper’s isSuccess before mapping result data. For
unsuccessful responses, convert the failure using the response code and message
into the repository’s domain-level error result, while preserving the existing
mappings for successful responses and DataSource exception conversion.
Source: Path instructions
| internal fun HomeHeader( | ||
| summaryState: HomeSummaryState, | ||
| hasUnreadNotification: Boolean, | ||
| modifier: Modifier = Modifier, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
헤더 액션을 상위 계층으로 전달하세요.
메인 연인 교체와 전화하기 버튼이 모두 no-op이라 사용자가 탭해도 아무 동작이 없습니다. 콜백을 HomeHeader 파라미터로 받고, 부모 화면에서 ViewModel intent/side effect로 연결하세요.
수정 예시
internal fun HomeHeader(
summaryState: HomeSummaryState,
hasUnreadNotification: Boolean,
+ onMainPartnerChangeClick: () -> Unit,
+ onCallClick: () -> Unit,
modifier: Modifier = Modifier,
) {
// ...
Button(
- onClick = {},
+ onClick = onMainPartnerChangeClick,
) {
// ...
}
FilledIconButton(
- onClick = {},
+ onClick = onCallClick,
) {
// ...
}
}Also applies to: 77-78, 162-163
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeHeader.kt`
around lines 49 - 52, Update HomeHeader to accept callbacks for the main-lover
replacement and phone actions, then invoke them from the corresponding header
buttons instead of no-ops. Pass these callbacks from the parent screen and
connect them to the appropriate ViewModel intent or side-effect handling,
preserving the existing UI behavior.
| @@ -0,0 +1,13 @@ | |||
| package kr.co.call.impl.viewmodel.state | |||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
UI State 기본값에 Mock 데이터가 하드코딩된 공통 문제. 세 곳 모두 동일한 근본 원인: State data class의 기본값이 중성값("", 0, false, null)이 아닌 mock 데이터로 설정되어 있어, API 로딩 전이나 빈 응답 시 UI에 가짜 데이터가 표시됩니다. Mock 데이터는 HomeMockData.kt 등 별도 레이어에서만 제공해야 합니다.
feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeReservationState.kt#L10-12:scheduledAt기본값을null로,firstName기본값을null또는""로 변경feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeSummaryState.kt#L5-8:firstName를"",relationshipDays/totalCallCount/callStreakDays를0으로 변경feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeState.kt#L12:hasUnreadNotification기본값을false로 변경
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeReservationState.kt`
at line 1, Update the UI state data-class defaults in HomeReservationState,
HomeSummaryState, and HomeState to neutral values: use null or an empty string
for reservation name fields, 0 for summary counters, false for
hasUnreadNotification, and null for scheduledAt. Keep mock data in dedicated
mock-data layers such as HomeMockData.kt rather than state defaults.
| val profileImageUrl: String? = "", | ||
| val scheduledAt: LocalDateTime? = LocalDateTime.of(2026, 6, 30, 21, 0), | ||
| val firstName: String? = "민준", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Mock 데이터가 State 기본값에 하드코딩되어 있습니다.
scheduledAt과 firstName의 기본값이 실제 데이터가 아닌 mock 값으로 설정되어 있습니다. API 로딩 전이나 빈 응답 시 UI에 가짜 데이터가 표시됩니다. Mock 데이터는 HomeMockData.kt 등 별도 레이어에서 제공하고, State 기본값은 중성값(null, "")을 사용해야 합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeReservationState.kt`
around lines 10 - 12, HomeReservationState의 scheduledAt과 firstName 기본값에 하드코딩된
mock 데이터를 제거하고 각각 null 등 중성 기본값으로 변경하세요. 실제 mock 데이터는 HomeMockData.kt 같은 별도 mock
레이어에서 제공하며, API 로딩 전이나 빈 응답에서는 가짜 값이 표시되지 않도록 하세요.
| val summary: HomeSummaryState = HomeSummaryState(), | ||
| val reservation: HomeReservationState = HomeReservationState(), | ||
| val callHistory: CallHistoryState = CallHistoryState(), | ||
| val hasUnreadNotification: Boolean = true, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
hasUnreadNotification 기본값이 true로 하드코딩되어 있습니다.
실제 알림 상태와 무관하게 항상 읽지 않음 표시가 렌더링됩니다. 기본값은 false여야 하며, 실제 값은 데이터 로드 후 반영되어야 합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeState.kt`
at line 12, Update the hasUnreadNotification property in HomeState so its
default value is false; preserve updating it with the actual notification state
after data loading.
| val firstName: String = "수현", | ||
| val relationshipDays: Int = 30, | ||
| val totalCallCount: Int = 24, | ||
| val callStreakDays: Int = 12, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Mock 데이터가 State 기본값에 하드코딩되어 있습니다.
firstName, relationshipDays, totalCallCount, callStreakDays의 기본값이 mock 값입니다. API 로딩 전이나 빈 응답 시 가짜 데이터가 표시됩니다. 중성 기본값("", 0)을 사용하고 mock 데이터는 별도 레이어에서 제공하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeSummaryState.kt`
around lines 5 - 8, HomeSummaryState의 firstName, relationshipDays,
totalCallCount, callStreakDays 기본값에서 하드코딩된 mock 데이터를 제거하고 각각 "" 또는 0의 중성값을
사용하세요. mock 데이터가 필요하면 State 외부의 별도 레이어에서 제공하도록 유지하세요.
Gemini AI 코드리뷰안녕하세요, 시니어 안드로이드 개발자로서 PR 리뷰를 시작하겠습니다. 이번 PR은 홈 화면의 주요 컴포넌트들을 구성하고 데이터 레이어를 연결하는 중요한 작업으로 보입니다. 전반적으로 클린 아키텍처 원칙을 잘 따르고 있으며, Jetpack Compose와 Hilt 사용에 대한 이해도가 높은 것으로 평가됩니다. 하지만 몇 가지 개선이 필요한 부분과, 아직 PR에 포함되지 않아 검토가 불가능한 중요한 영역(특히 ViewModel)에 대한 코멘트가 있습니다. 전반적인 피드백 (General Observations)
세부 코드 리뷰 (Detailed Code Review)1.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt (1)
150-156: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
HorizontalDivider너비가 380dp로 고정되어 있습니다.화면 폭이 380dp보다 좁은 기기에서는 divider가 오버플로우되고, 넓은 기기에서는 분할선이 끝까지 닿지 않습니다.
fillMaxWidth()를 사용하여 부모 너비에 맞추는 것이 안전합니다.♻️ 제안 수정
HorizontalDivider( modifier = Modifier .align(Alignment.BottomStart) - .width(380.dp), + .fillMaxWidth(), thickness = 1.dp, color = CallTheme.colors.gray100, )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt` around lines 150 - 156, Update the HorizontalDivider modifier in the CallHistoryList layout to use fillMaxWidth() instead of the fixed width(380.dp), allowing the divider to match its parent width on all screen sizes while preserving its bottom-start alignment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/data/src/main/java/kr/co/call/data/mapper/CallHistoryMapper.kt`:
- Around line 48-56: Update ReservationDto.toDomain to parse scheduledAt through
TimeUtil.parseLocalDateTime instead of LocalDateTime.parse, matching
CallHistoryDto.toDomain and supporting the API’s non-ISO date formats.
In `@core/domain/src/main/java/kr/co/call/domain/model/home/CallReservation.kt`:
- Around line 14-16: Update ReservationStatus and the parsing flow in
CallHistoryMapper so unknown API status values do not reach a direct
ReservationStatus.valueOf(status) call. Add and use a safe fromString-style
parser that returns SCHEDULED when no enum entry matches, while preserving
normal parsing for known values.
---
Nitpick comments:
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt`:
- Around line 150-156: Update the HorizontalDivider modifier in the
CallHistoryList layout to use fillMaxWidth() instead of the fixed width(380.dp),
allowing the divider to match its parent width on all screen sizes while
preserving its bottom-start alignment.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 22ace0f7-8f85-45e5-8aa0-ee8047299c16
📒 Files selected for processing (23)
core/data/src/main/java/kr/co/call/data/mapper/CallHistoryMapper.ktcore/data/src/main/java/kr/co/call/data/repositoryImpl/HomeRepositoryImpl.ktcore/domain/src/main/java/kr/co/call/domain/model/home/CallHistory.ktcore/domain/src/main/java/kr/co/call/domain/model/home/CallReservation.ktcore/domain/src/main/java/kr/co/call/domain/model/home/CallReservations.ktcore/domain/src/main/java/kr/co/call/domain/model/home/HomeSummary.ktcore/domain/src/main/java/kr/co/call/domain/repository/HomeRepository.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeHeader.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeReservationCard.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/RelationshipSummaryCard.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/HomeHistorySection.ktfeature/home/impl/src/main/java/kr/co/call/impl/mapper/CallHistoryUiMapper.ktfeature/home/impl/src/main/java/kr/co/call/impl/mapper/HomeUiMapper.ktfeature/home/impl/src/main/java/kr/co/call/impl/mock/CallMockData.ktfeature/home/impl/src/main/java/kr/co/call/impl/screen/HomeMockData.ktfeature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/HomeViewModel.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/model/CallHistoryIconType.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/model/CallHistoryUiModel.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/model/HomeReservationUiModel.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/model/HomeSummaryUiModel.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeState.kt
🚧 Files skipped from review as they are similar to previous changes (5)
- feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/state/HomeState.kt
- core/domain/src/main/java/kr/co/call/domain/repository/HomeRepository.kt
- feature/home/impl/src/main/java/kr/co/call/impl/viewmodel/HomeViewModel.kt
- feature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeHeader.kt
- feature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.kt
| private fun ReservationDto.toDomain(): CallReservation = | ||
| CallReservation( | ||
| id = callReservationId, | ||
| characterId = characterId, | ||
| firstName = firstName, | ||
| imageUrl = imageUrl, | ||
| scheduledAt = LocalDateTime.parse(scheduledAt), | ||
| status = ReservationStatus.valueOf(status), | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
날짜 파싱 방식이 CallHistoryDto와 일치하지 않습니다.
CallHistoryDto.toDomain()는 TimeUtil.parseLocalDateTime(startedAt)를 사용하지만, ReservationDto.toDomain()는 LocalDateTime.parse(scheduledAt)를 직접 호출합니다. API가 ISO-8601 외 포맷(예: "2026-06-30 21:00:00")을 반환하면 예약 매핑에서 DateTimeParseException이 발생합니다. TimeUtil.parseLocalDateTime을 일관되게 사용하세요.
🔧 제안 수정
- scheduledAt = LocalDateTime.parse(scheduledAt),
+ scheduledAt = TimeUtil.parseLocalDateTime(scheduledAt),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private fun ReservationDto.toDomain(): CallReservation = | |
| CallReservation( | |
| id = callReservationId, | |
| characterId = characterId, | |
| firstName = firstName, | |
| imageUrl = imageUrl, | |
| scheduledAt = LocalDateTime.parse(scheduledAt), | |
| status = ReservationStatus.valueOf(status), | |
| ) | |
| private fun ReservationDto.toDomain(): CallReservation = | |
| CallReservation( | |
| id = callReservationId, | |
| characterId = characterId, | |
| firstName = firstName, | |
| imageUrl = imageUrl, | |
| scheduledAt = TimeUtil.parseLocalDateTime(scheduledAt), | |
| status = ReservationStatus.valueOf(status), | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@core/data/src/main/java/kr/co/call/data/mapper/CallHistoryMapper.kt` around
lines 48 - 56, Update ReservationDto.toDomain to parse scheduledAt through
TimeUtil.parseLocalDateTime instead of LocalDateTime.parse, matching
CallHistoryDto.toDomain and supporting the API’s non-ISO date formats.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
core/designsystem/src/main/java/kr/co/call/designsystem/component/ProfileImage.kt (1)
28-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winBoolean 매개변수명을 Kotlin 컨벤션에 맞추세요.
showCallBadge는 Boolean인데is/has/should접두사를 사용하지 않습니다.isCallBadgeVisible또는shouldShowCallBadge로 변경하고 호출부도 함께 갱신하세요.As per path instructions, Boolean 변수/함수는
is/has/should접두사를 사용해야 합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/designsystem/src/main/java/kr/co/call/designsystem/component/ProfileImage.kt` around lines 28 - 31, Rename the Boolean parameter showCallBadge in ProfileImageWithIcon to use an is/has/should prefix, such as isCallBadgeVisible or shouldShowCallBadge, and update every call site and internal reference accordingly.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/CharacterChangeDialog.kt`:
- Around line 6-11: CharacterChangeDialog is currently an empty composable, so
implement its character-option list, selection state, and close/confirm event
handling so the character-change flow is usable. Expose callbacks and state
through the CharacterChangeDialog API as needed, and render the dialog UI using
the provided modifier. If the flow cannot be implemented yet, remove or restrict
the callable API instead of leaving an empty public composable.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt`:
- Around line 52-56: The CallHistoryList and NotificationList click callbacks
are not consistently connected to real parent actions. In
feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt
lines 52-56, make onRecordClick required and connect it to the parent’s
detail-navigation or event-handling flow; in
feature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.kt
lines 64-74, replace the empty onCallClick lambda with a callback that forwards
the notification identifier to the parent re-call action.
---
Nitpick comments:
In
`@core/designsystem/src/main/java/kr/co/call/designsystem/component/ProfileImage.kt`:
- Around line 28-31: Rename the Boolean parameter showCallBadge in
ProfileImageWithIcon to use an is/has/should prefix, such as isCallBadgeVisible
or shouldShowCallBadge, and update every call site and internal reference
accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: ca84cb1b-4dbc-40a6-a95c-9bc77e5e7d4a
📒 Files selected for processing (12)
core/designsystem/build.gradle.ktscore/designsystem/src/main/java/kr/co/call/designsystem/component/ProfileImage.ktcore/domain/src/main/java/kr/co/call/domain/model/home/HomeOverview.ktfeature/home/impl/build.gradle.ktsfeature/home/impl/src/main/java/kr/co/call/impl/component/CharacterChangeDialog.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeReservationCard.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/HomeHistorySection.ktfeature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.ktfeature/home/impl/src/main/java/kr/co/call/impl/mock/CharacterMockData.ktfeature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.ktfeature/home/impl/src/main/java/kr/co/call/impl/viewmodel/model/CharacterOptionUiModel.kt
💤 Files with no reviewable changes (2)
- feature/home/impl/build.gradle.kts
- feature/home/impl/src/main/java/kr/co/call/impl/component/history/HomeHistorySection.kt
🚧 Files skipped from review as they are similar to previous changes (3)
- core/designsystem/build.gradle.kts
- feature/home/impl/src/main/java/kr/co/call/impl/component/header/HomeReservationCard.kt
- feature/home/impl/src/main/java/kr/co/call/impl/screen/HomeScreen.kt
| @Composable | ||
| fun CharacterChangeDialog( | ||
| modifier: Modifier = Modifier, | ||
| ){ | ||
|
|
||
| } No newline at end of file |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
다이얼로그 본문과 이벤트를 구현하세요.
현재 CharacterChangeDialog는 호출되어도 아무 UI도 그리지 않아 캐릭터 변경 흐름이 동작하지 않습니다. 옵션 목록, 선택 상태, 닫기·확정 이벤트를 구현하거나, 구현 전이라면 호출 가능한 API로 노출하지 않아야 합니다.
🧰 Tools
🪛 detekt (1.23.8)
[warning] 9-11: This empty block of code can be removed.
(detekt.empty-blocks.EmptyFunctionBlock)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/CharacterChangeDialog.kt`
around lines 6 - 11, CharacterChangeDialog is currently an empty composable, so
implement its character-option list, selection state, and close/confirm event
handling so the character-change flow is usable. Expose callbacks and state
through the CharacterChangeDialog API as needed, and render the dialog UI using
the provided modifier. If the flow cannot be implemented yet, remove or restrict
the callable API instead of leaving an empty public composable.
Source: Linters/SAST tools
| histories.forEach { history -> | ||
| CallHistoryCard( | ||
| callHistory = history, | ||
| onRecordClick = { onRecordClick(history.callId) }, | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
사용자 액션 콜백을 실제 이벤트에 연결하세요.
현재 기록 보기와 재통화 UI가 모두 클릭 가능하게 보이지만 상위 호출부에서 콜백을 전달하지 않거나 빈 람다를 사용해 실제 동작이 없습니다.
feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt#L52-L56:onRecordClick을 필수 콜백으로 만들고 실제 상세 화면 이동 또는 이벤트 처리까지 연결하세요.feature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.kt#L64-L74:onCallClick = {}를 제거하고 알림 식별자를 상위 재통화 이벤트로 전달하세요.
📍 Affects 2 files
feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt#L52-L56(this comment)feature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.kt#L64-L74
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt`
around lines 52 - 56, The CallHistoryList and NotificationList click callbacks
are not consistently connected to real parent actions. In
feature/home/impl/src/main/java/kr/co/call/impl/component/history/CallHistoryList.kt
lines 52-56, make onRecordClick required and connect it to the parent’s
detail-navigation or event-handling flow; in
feature/home/impl/src/main/java/kr/co/call/impl/component/history/NotificationList.kt
lines 64-74, replace the empty onCallClick lambda with a callback that forwards
the notification identifier to the parent re-call action.
# Conflicts: # core/data/src/main/java/kr/co/call/data/di/RepositoryModule.kt
Gemini AI 코드리뷰안녕하세요, 시니어 안드로이드 개발자입니다. 새로운 Home Feature PR 잘 봤습니다. 전체적으로 클린 아키텍처 원칙을 잘 지키려 노력했고, Compose 컴포넌트 분리도 깔끔하게 진행하신 점이 인상 깊습니다. 몇 가지 개선 사항과 피드백을 공유 드립니다. 🔍 코드 리뷰1. Kotlin & General
2. Jetpack Compose
3. Repository/DataSource 레이어
4. ViewModel (리뷰 불가)
5. FCM/SSE/실시간 통신 (해당 없음)
✅ 총평 및 다음 단계 제안새로운 Home 기능의 도입은 전체적인 아키텍처 원칙을 잘 지키며 진행되었고, 특히 몇 가지 주요 개선 사항은 다음과 같습니다:
이 피드백들이 PR을 더 완벽하게 만드는 데 도움이 되기를 바랍니다. 궁금한 점이 있으면 언제든지 질문해 주세요. 계속해서 좋은 코드 기대하겠습니다! |
HomeViewModel이 HomeRepository를 직접 호출하지 않고 HomeUseCase를 경유하도록 변경. develop에서 병합된 characterId/ReservationStatus 필드는 사용하지 않아 제거.
KateteDeveloper
left a comment
There was a problem hiding this comment.
지나가는 나그네 입니다....
KDOC 도입을 적극적으로 도입해보면 더욱 컨벤션에 도움이 될 것 같아요 😊
너무 잘하는데요 LGTM👍
| import java.time.LocalDateTime | ||
| import java.time.format.DateTimeFormatter | ||
| import java.util.Locale | ||
|
|
There was a problem hiding this comment.
지나가는 안드 나그네 입니다...!
이건 core common에서 공통적으로 사용하는 유틸이기에 KDOC로 주석 달면 편하게 볼 수 있을 것 같아요:)
Gemini AI 코드리뷰안녕하세요! 시니어 Android 개발자입니다. 이번 PR에서 Home 기능을 새롭게 추가하신 것으로 보입니다. 전반적으로 최신 Android 개발 트렌드와 모범 사례를 잘 적용하려고 노력한 점이 인상 깊습니다. 특히 Jetpack Compose, Orbit MVI, 클린 아키텍처 원칙을 잘 따르고 있습니다. 몇 가지 개선점에 대한 피드백을 드리겠습니다. PR 요약새로운
전반적인 피드백이 PR은 주요 개선점은 다음과 같습니다:
세부 코드 리뷰1. Kotlin 코드
2. Jetpack Compose 코드
3. Repository/DataSource 레이어
4. ViewModel
5. FCM/SSE/실시간 통신 관련 코드
정리 및 제안
위 제안사항들을 반영하면 더욱 안정적이고 유지보수하기 쉬운 애플리케이션이 될 것입니다. 전반적으로 훌륭한 PR입니다. 계속해서 좋은 코드 기대합니다! |
| import kr.co.call.domain.repository.HomeRepository | ||
|
|
||
| class HomeUseCase( | ||
| private val homeRepository: HomeRepository, |
There was a problem hiding this comment.
이거 힐트로 주입시키는 게 좋을듯 모듈도 만들어놨으니
| * - 전화 버튼과 관계 요약 카드 포함 | ||
| */ | ||
| @Composable | ||
| internal fun HomeHeader( |
There was a problem hiding this comment.
internal은 빼도될듯 짜피 피쳐모듈 서로모름
| * - 약속이 있으면 상대 정보와 시간 변경 버튼 표시 | ||
| */ | ||
| @Composable | ||
| internal fun HomeReservationCard( |
| val isLoading: Boolean | ||
| get() = loadStatus == LoadStatus.Loading | ||
| } |
There was a problem hiding this comment.
이거 따로 한 이유 있음? 필드로 같이 넣는 게 나을듯?
| val title: String, | ||
| val content: String, | ||
| val isRead: Boolean, | ||
| val createdAt: LocalDateTime, |
There was a problem hiding this comment.
화면에 보여질 상태라면 이 단계에서는 그냥 String인 게 나을 듯?
HomeNotificationState/NotificationType을 core:domain의 HomeNotification으로 이동하고, UI 모델(HomeNotificationUiModel)과 매퍼를 추가해 시간 포맷팅을 매퍼로 옮김. 알림/통화기록 리스트를 HomeScreen의 단일 LazyColumn으로 통합. ProfileImageWithIcon 함수명을 ProfileImage로 변경.
Gemini AI 코드리뷰안녕하세요, PR 올려주셔서 감사합니다. 새로운 홈 화면 기능 도입과 관련된 PR이네요. 전반적으로 코드 구조와 Compose 원칙을 잘 따르려 노력한 점이 인상 깊습니다. 몇 가지 개선 사항과 코멘트를 드리니 참고하여 반영해주시면 좋을 것 같습니다. 📝 전반적인 피드백새로운 다만, 몇 가지 개선이 필요한 부분이 있습니다. 특히 UI 상태 관리(로딩/에러)와 하드코딩된 문자열 처리, 그리고 API 에러 핸들링 전략에 대한 명확성이 필요합니다. 🔍 파일별 상세 리뷰1.
|
Gemini AI 코드리뷰안녕하세요! PR을 잘 리뷰해 보았습니다. 새로운 홈 기능이 추가되었고, 구조적으로 잘 분리하려는 노력이 엿보입니다. 특히 몇 가지 개선할 점과 논의해 볼 부분이 있어 코멘트 남깁니다. 1. Kotlin 코드 및 공통 유틸리티 (TimeUtil, Mapper)
2. Jetpack Compose 코드이번 PR의 Compose 코드 전반에 걸쳐 공통적으로 발견되는 중요한 개선점들이 있습니다.
3. Repository/DataSource 레이어
4. ViewModel (간접 추론)이 PR에서는 ViewModel 코드가 직접적으로 보이지 않지만, UI 모델(
정리 및 제안
이러한 개선 사항들을 적용한다면 더욱 견고하고 유지보수하기 쉬운 코드가 될 것입니다. 수고 많으셨습니다! |
📄 작업 내용 요약
2026-07-17.10.24.13.mov
📎 Issue 번호
✅ 작업 목록
📝 기타 참고사항
Summary by CodeRabbit