-
Notifications
You must be signed in to change notification settings - Fork 0
[Feature/#54] 일간, 주간 퀴즈 리포트를 구현합니다. #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Walkthrough리포트 기능을 추가합니다. 일일, 주간, 월간 리포트 조회 UI 화면과 ViewModel, 비즈니스 로직(Use Cases), 데이터 계층(Service, Repository, DataSource)을 구현하고, 네비게이션 및 디자인 시스템에 통합합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant Screen as ReportScreen<br/>(UI)
participant ViewModel as ReportViewModel
participant UseCase as FetchDailyReportUseCase
participant Repository as ReportRepository
participant DataSource as ReportDataSource
participant Service as ReportService<br/>(Retrofit)
participant API as Report API
User->>Screen: 일일 리포트 조회 (날짜 선택)
Screen->>ViewModel: setReportCycle(DAILY)
ViewModel->>UseCase: invoke(dateString)
UseCase->>Repository: fetchDailyReport(dateString)
Repository->>DataSource: fetchDailyReport(dateString)
DataSource->>Service: fetchDailyReport(dateString)
Service->>API: GET /api/v1/report/daily?date=...
rect rgb(200, 220, 255)
Note over API,DataSource: 네트워크 요청
API-->>Service: NetworkResult<DailyReportResponse>
end
Service-->>DataSource: Result<DailyReportResponse?>
DataSource-->>Repository: Result<DailyReportResponse?>
Repository->>Repository: response.toDomain()
Repository-->>UseCase: Result<DailyReport?>
UseCase-->>ViewModel: Result<DailyReport?>
rect rgb(200, 255, 220)
Note over ViewModel: UI 상태 업데이트
ViewModel->>ViewModel: dailyReport.toUiModel()
ViewModel-->>Screen: uiState.collect()
end
Screen->>Screen: DailyReportContent 렌더링
Screen-->>User: 일일 점수, 조언 표시
sequenceDiagram
participant User as 사용자
participant Screen as ReportScreen<br/>(UI)
participant ViewModel as ReportViewModel
participant UseCase as FetchWeeklyReportUseCase
participant Repository as ReportRepository
participant DataSource as ReportDataSource
participant Service as ReportService
User->>Screen: 주간 리포트 보기
Screen->>ViewModel: setReportCycle(WEEKLY)
ViewModel->>ViewModel: 현재 주 날짜 계산
ViewModel->>UseCase: invoke(year, month, week)
UseCase->>Repository: fetchWeeklyReport(year, month, week)
Repository->>DataSource: fetchWeeklyReport(year, month, week)
DataSource->>Service: fetchWeeklyReport(year, month, week)
Service-->>DataSource: Result<WeeklyReportResponse?>
DataSource-->>Repository: Result<WeeklyReportResponse?>
Repository->>Repository: response.toDomain()
Repository-->>UseCase: Result<WeeklyReport?>
UseCase-->>ViewModel: Result<WeeklyReport?>
rect rgb(200, 255, 220)
ViewModel->>ViewModel: weeklyReport.toUiModel()
ViewModel-->>Screen: uiState (WeeklyReportUiModel)
end
Screen->>Screen: QuizCategorySelector 렌더링
User->>Screen: 카테고리 선택
Screen->>ViewModel: weeklyReportQuizCategorySelected(category)
ViewModel->>ViewModel: chartData 계산
Screen->>Screen: MaBarChart 및<br/>ReportDetail 렌더링
Screen-->>User: 주간 차트, 진단, 전략 표시
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
♻️ Duplicate comments (1)
core/designsystem/src/main/res/drawable/ic_circle_arrow_right.xml (1)
6-11: 주제색과 테마 속성을 사용하여 색상을 정의하세요 (ic_circle_arrow_left.xml과 동일한 문제).ic_circle_arrow_left.xml 검토에서 지적한 것과 동일하게, 이 파일도 하드코딩된 색상값(
#E6E6E6,#9C9C9C)을@color/리소스 또는?attr/테마 속성으로 변경해야 합니다. 두 파일 모두 일관성 있게 수정되어야 합니다.
🧹 Nitpick comments (14)
feature/report/src/test/java/com/moa/app/feature/report/ExampleUnitTest.kt (1)
1-16: 예제 테스트 코드를 실제 리포트 기능 테스트로 교체해주세요.이 파일은 Android Studio가 생성한 플레이스홀더 테스트입니다. 일간/주간 리포트 기능에 대한 실제 단위 테스트로 교체하거나 제거하는 것이 좋습니다.
리포트 기능에 대한 단위 테스트 코드 생성을 도와드릴까요?
feature/report/src/main/java/com/moa/app/feature/report/component/ReportSummary.kt (2)
39-83: 중복된 Column 구조를 재사용 가능한 컴포넌트로 추출하는 것을 고려하세요.완료율과 정답률 카드가 거의 동일한 구조를 가지고 있습니다. 이를 별도의 private composable로 추출하면 코드 중복을 줄이고 유지보수성을 높일 수 있습니다.
🔎 제안된 리팩토링
@Composable private fun StatCard( value: Int, label: String, modifier: Modifier = Modifier, ) { Column( modifier = modifier .clip(RoundedCornerShape(12.dp)) .background(MoaTheme.colors.coolGray99) .padding(vertical = 24.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { Text( text = "$value%", color = MoaTheme.colors.black, style = MoaTheme.typography.title2Bold, modifier = Modifier.padding(bottom = 2.dp), ) Text( text = label, color = MoaTheme.colors.coolGray40, style = MoaTheme.typography.body2Semibold, ) } }그 후 Row 내부에서 사용:
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { StatCard(value = completeRate, label = "완료율", modifier = Modifier.weight(1f)) StatCard(value = correctRate, label = "정답률", modifier = Modifier.weight(1f)) }
108-116: Preview에 MoaTheme wrapper가 누락되었습니다.MoaTheme을 사용하는 composable의 preview에서는 테마를 래핑해야 색상과 타이포그래피가 올바르게 적용됩니다.
🔎 제안된 수정
@Preview(showBackground = true) @Composable private fun PreviewReportSummary() { + MoaTheme { ReportSummary( summary = "이번 주에는 참여율은 꾸준했지만,\n정답률이 지난주보다 조금 떨어졌습니다.", completeRate = 80, correctRate = 50, ) + } }core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/chart/MaBarChart.kt (2)
175-179: 데이터 클래스가 파일 하단에 정의되어 있습니다.
BarChartItem데이터 클래스를 파일 상단이나 별도 파일로 이동하면 가독성과 재사용성이 향상됩니다. 공개 API의 일부이므로 쉽게 찾을 수 있어야 합니다.
30-129: 차트에 접근성(Accessibility) 시맨틱이 누락되었습니다.스크린 리더 사용자를 위해 Canvas에
contentDescription을 제공하거나semantics블록을 추가하는 것을 고려하세요.🔎 제안된 수정
+import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics - Canvas(modifier = modifier) { + Canvas( + modifier = modifier.semantics { + contentDescription = "카테고리별 점수 비교 차트" + } + ) {domain/src/main/kotlin/com/moa/app/domain/report/usecase/FetchDailyReportUseCase.kt (1)
10-12: 날짜 형식에 대한 문서화 또는 타입 안전성 고려
date파라미터가String타입으로 되어 있어 호출자가 어떤 형식(예: "yyyy-MM-dd")을 사용해야 하는지 명확하지 않습니다. API 스펙에 맞는 날짜 형식을 KDoc으로 문서화하거나, 향후LocalDate타입 사용을 고려해 보세요.feature/report/src/main/java/com/moa/app/feature/report/ReportScreen.kt (2)
102-128: 접근성 개선: contentDescription 추가화살표 이미지의
contentDescription이null로 설정되어 있어 스크린 리더 사용자가 버튼의 용도를 알 수 없습니다. 접근성을 위해 의미 있는 설명을 추가하세요.🔎 수정 제안
Image( imageVector = ImageVector.vectorResource(R.drawable.ic_circle_arrow_left), - contentDescription = null, + contentDescription = "이전", modifier = Modifier.clickable( ... ), ) ... Image( imageVector = ImageVector.vectorResource(R.drawable.ic_circle_arrow_right), - contentDescription = null, + contentDescription = "다음", modifier = Modifier.clickable( ... ), )
133-181: 리포트 사이클 전환 시 스크롤 상태 유지 고려현재
rememberScrollState()가 각 분기 내부에서 생성되어, 리포트 사이클 변경 시 스크롤 위치가 항상 초기화됩니다. 의도된 동작이라면 괜찮지만, 사용자 경험상 스크롤 위치 유지가 필요하다면when블록 외부로 이동을 고려해 보세요.feature/report/src/main/java/com/moa/app/feature/report/component/WeeklyReportContent.kt (1)
100-176: Preview 샘플 데이터를 별도 객체로 추출하는 것을 고려해보세요.현재 Preview 함수가 70줄 이상으로 길어서 가독성이 떨어집니다. 샘플 데이터를 companion object나 별도 파일로 분리하면 유지보수성이 향상됩니다.
🔎 제안된 리팩토링
// 별도 파일 또는 companion object에 샘플 데이터 정의 private val previewWeeklyReportUiModel = WeeklyReportUiModel( oneLineReview = "이번 주에는 참여율은 꾸준했지만,\n정답률이 지난주보다 조금 떨어졌습니다.", completeRate = 80, correctRate = 70, selectedCategory = QuizCategory.ALL, scores = /* ... */, diagnosis = /* ... */, nextWeekStrategy = /* ... */, ) @Preview(showBackground = true) @Composable private fun PreviewWeeklyReportContent() { var selectedCategory by remember { mutableStateOf(QuizCategory.ALL) } WeeklyReportContent( modifier = Modifier.fillMaxSize(), onCategorySelected = { selectedCategory = it }, weeklyReport = previewWeeklyReportUiModel.copy(selectedCategory = selectedCategory), ) }data/src/main/kotlin/com/moa/app/data/report/model/response/WeeklyReportResponse.kt (1)
39-45:WeeklyScoreDto.toDomain()함수의 가시성을private으로 변경하는 것을 고려해보세요.
DailyReportResponse.kt에서DailyQuizScoreDto.toDomain()은private으로 선언되어 있습니다. 일관성을 위해 이 함수도private으로 변경하는 것이 좋습니다.🔎 제안된 수정
-fun WeeklyScoreDto.toDomain(): WeeklyScore { +private fun WeeklyScoreDto.toDomain(): WeeklyScore { return WeeklyScore( dayOfWeek = this.dayOfWeek, score = this.score, lastWeekScore = this.lastWeekScore, ) }feature/report/src/main/java/com/moa/app/feature/report/component/DailyReportContent.kt (2)
90-94: 전체 점수가 없을 때 기본값 1은 의미상 부적절합니다.
quizAll이 null일 때total의 기본값으로 1을 사용하면, 의미 없는 진행률(0/1)이 표시됩니다.quizAll이 null인 경우 진행 표시기를 렌더링하지 않거나, 전체 영역을 숨기는 것이 더 적절합니다.🔎 제안하는 수정안
- MaLinerProgressIndicator( - correct = quizAll?.correctNumber ?: 0, - total = quizAll?.totalNumber ?: 1, - modifier = Modifier.fillMaxWidth(), - ) + quizAll?.let { + MaLinerProgressIndicator( + correct = it.correctNumber, + total = it.totalNumber, + modifier = Modifier.fillMaxWidth(), + ) + }
127-143: LazyColumn 사용을 고려해보세요.현재
forEachIndexed를 사용하여 조언 목록을 렌더링하고 있습니다. 리스트가 작다면 문제없지만, Compose에서는LazyColumn과items()를 사용하는 것이 더 일관되고 확장 가능합니다. 향후 리스트가 커질 경우를 대비하여 LazyColumn으로 전환하는 것을 권장합니다.feature/report/src/main/java/com/moa/app/feature/report/ReportViewModel.kt (2)
96-96: 빈 블록에 주석을 추가해주세요.월간 리포트는 PR 설명에 따라 의도적으로 구현이 지연되었지만, 빈 블록에 이를 설명하는 주석이 있으면 코드를 읽는 사람에게 더 명확할 것입니다.
🔎 제안하는 수정안
private fun fetchReport() { when (_uiState.value.reportCycle) { ReportCycle.DAILY -> fetchDailyReport() ReportCycle.WEEKLY -> fetchWeeklyReport() - ReportCycle.MONTHLY -> {} + ReportCycle.MONTHLY -> { + // TODO: 월간 리포트는 API 스펙 변경 가능성으로 인해 별도로 구현 예정 + } } }
100-130: 로딩 및 에러 상태 관리를 추가하는 것을 고려해주세요.현재 API 호출 중 로딩 상태나 에러 상태가 UI에 노출되지 않습니다. 사용자는 리포트가 로딩 중인지, 에러가 발생했는지 알 수 없습니다. MVP 이후에라도
ReportUiState에isLoading이나error필드를 추가하여 더 나은 사용자 경험을 제공하는 것을 권장합니다.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (5)
core/designsystem/src/main/res/drawable-hdpi/img_attention_quiz.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-mdpi/img_attention_quiz.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xhdpi/img_attention_quiz.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxhdpi/img_attention_quiz.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxxhdpi/img_attention_quiz.pngis excluded by!**/*.png
📒 Files selected for processing (69)
app/build.gradle.ktsapp/src/main/kotlin/com/moa/app/main/MainActivity.ktcore/designsystem/build.gradle.ktscore/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/chip/MaChip.ktcore/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinerProgressIndicator.ktcore/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/chart/MaBarChart.ktcore/designsystem/src/main/res/drawable-hdpi/img_linguistic_quiz.webpcore/designsystem/src/main/res/drawable-hdpi/img_memory_quiz.webpcore/designsystem/src/main/res/drawable-hdpi/img_persistence_quiz.webpcore/designsystem/src/main/res/drawable-hdpi/img_space_quiz.webpcore/designsystem/src/main/res/drawable-mdpi/img_linguistic_quiz.webpcore/designsystem/src/main/res/drawable-mdpi/img_memory_quiz.webpcore/designsystem/src/main/res/drawable-mdpi/img_persistence_quiz.webpcore/designsystem/src/main/res/drawable-mdpi/img_space_quiz.webpcore/designsystem/src/main/res/drawable-xhdpi/img_linguistic_quiz.webpcore/designsystem/src/main/res/drawable-xhdpi/img_memory_quiz.webpcore/designsystem/src/main/res/drawable-xhdpi/img_persistence_quiz.webpcore/designsystem/src/main/res/drawable-xhdpi/img_space_quiz.webpcore/designsystem/src/main/res/drawable-xxhdpi/img_linguistic_quiz.webpcore/designsystem/src/main/res/drawable-xxhdpi/img_memory_quiz.webpcore/designsystem/src/main/res/drawable-xxhdpi/img_persistence_quiz.webpcore/designsystem/src/main/res/drawable-xxhdpi/img_space_quiz.webpcore/designsystem/src/main/res/drawable-xxxhdpi/img_linguistic_quiz.webpcore/designsystem/src/main/res/drawable-xxxhdpi/img_memory_quiz.webpcore/designsystem/src/main/res/drawable-xxxhdpi/img_persistence_quiz.webpcore/designsystem/src/main/res/drawable-xxxhdpi/img_space_quiz.webpcore/designsystem/src/main/res/drawable/ic_circle_arrow_left.xmlcore/designsystem/src/main/res/drawable/ic_circle_arrow_right.xmlcore/navigation/src/main/java/com/moa/app/navigation/AppRoute.ktdata/src/main/kotlin/com/moa/app/data/di/DataSourceModule.ktdata/src/main/kotlin/com/moa/app/data/di/RepositoryModule.ktdata/src/main/kotlin/com/moa/app/data/di/ServiceModule.ktdata/src/main/kotlin/com/moa/app/data/report/datasource/ReportDataSource.ktdata/src/main/kotlin/com/moa/app/data/report/datasourceImpl/ReportDataSourceImpl.ktdata/src/main/kotlin/com/moa/app/data/report/model/response/DailyReportResponse.ktdata/src/main/kotlin/com/moa/app/data/report/model/response/MonthlyReportResponse.ktdata/src/main/kotlin/com/moa/app/data/report/model/response/WeeklyReportResponse.ktdata/src/main/kotlin/com/moa/app/data/report/repositoryImpl/ReportRepositoryImpl.ktdata/src/main/kotlin/com/moa/app/data/report/service/ReportService.ktdomain/src/main/kotlin/com/moa/app/domain/quiz/model/QuizCategory.ktdomain/src/main/kotlin/com/moa/app/domain/report/model/DailyReport.ktdomain/src/main/kotlin/com/moa/app/domain/report/model/MonthlyReport.ktdomain/src/main/kotlin/com/moa/app/domain/report/model/WeeklyReport.ktdomain/src/main/kotlin/com/moa/app/domain/report/repository/ReportRepository.ktdomain/src/main/kotlin/com/moa/app/domain/report/usecase/FetchDailyReportUseCase.ktdomain/src/main/kotlin/com/moa/app/domain/report/usecase/FetchMonthlyReportUseCase.ktdomain/src/main/kotlin/com/moa/app/domain/report/usecase/FetchWeeklyReportUseCase.ktfeature/report/.gitignorefeature/report/build.gradle.ktsfeature/report/consumer-rules.profeature/report/proguard-rules.profeature/report/src/androidTest/java/com/moa/app/feature/report/ExampleInstrumentedTest.ktfeature/report/src/main/AndroidManifest.xmlfeature/report/src/main/java/com/moa/app/feature/report/ReportScreen.ktfeature/report/src/main/java/com/moa/app/feature/report/ReportViewModel.ktfeature/report/src/main/java/com/moa/app/feature/report/component/DailyReportContent.ktfeature/report/src/main/java/com/moa/app/feature/report/component/EmptyReportContent.ktfeature/report/src/main/java/com/moa/app/feature/report/component/QuizCategorySelector.ktfeature/report/src/main/java/com/moa/app/feature/report/component/ReportSummary.ktfeature/report/src/main/java/com/moa/app/feature/report/component/WeeklyReportContent.ktfeature/report/src/main/java/com/moa/app/feature/report/extension/QuizCategoryEtx.ktfeature/report/src/main/java/com/moa/app/feature/report/model/DailyReportUiModel.ktfeature/report/src/main/java/com/moa/app/feature/report/model/WeeklyReportUiModel.ktfeature/report/src/main/java/com/moa/app/feature/report/util/DateUtils.ktfeature/report/src/test/java/com/moa/app/feature/report/ExampleUnitTest.ktfeature/senior/src/main/kotlin/com/moa/app/feature/senior/home/SeniorHomeViewModel.ktfeature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/QuizCategoryScreen.ktfeature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/model/QuizCategoryExtension.ktsettings.gradle.kts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-21T09:07:31.486Z
Learnt from: wjdrjs00
Repo: team-memory-care/MoA-Android PR: 31
File: core/network/src/main/kotlin/com/moa/app/network/adapter/NetworkResultCall.kt:36-37
Timestamp: 2025-11-21T09:07:31.486Z
Learning: The MoA-Android project exclusively uses Retrofit suspend functions for network calls in the core/network module. The NetworkResultCall.execute() method intentionally throws UnsupportedOperationException because synchronous execution is not needed.
Applied to files:
data/src/main/kotlin/com/moa/app/data/report/service/ReportService.kt
🧬 Code graph analysis (4)
feature/report/src/main/java/com/moa/app/feature/report/component/WeeklyReportContent.kt (3)
feature/report/src/main/java/com/moa/app/feature/report/component/ReportSummary.kt (2)
ReportSummary(19-86)ReportDetail(88-106)feature/report/src/main/java/com/moa/app/feature/report/component/QuizCategorySelector.kt (1)
QuizCategorySelector(20-48)core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/chart/MaBarChart.kt (2)
MaBarChart(30-130)ChartLegendItems(132-173)
feature/report/src/main/java/com/moa/app/feature/report/ReportScreen.kt (4)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/topbar/MaTopAppBar.kt (1)
MaTopAppBar(20-52)core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/chip/MaChip.kt (1)
MaBasicChip(25-53)feature/report/src/main/java/com/moa/app/feature/report/component/DailyReportContent.kt (1)
DailyReportContent(38-145)feature/report/src/main/java/com/moa/app/feature/report/component/WeeklyReportContent.kt (1)
WeeklyReportContent(28-98)
feature/report/src/main/java/com/moa/app/feature/report/component/QuizCategorySelector.kt (1)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/chip/MaChip.kt (1)
MaBasicChip(25-53)
app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)
feature/report/src/main/java/com/moa/app/feature/report/ReportScreen.kt (1)
ReportScreen(40-54)
⏰ 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: Run Unit Tests
- GitHub Check: Run ktlint
🔇 Additional comments (41)
feature/report/src/main/java/com/moa/app/feature/report/util/DateUtils.kt (3)
8-10: 객체 구조와 포매터 상수 정의가 적절합니다.싱글톤 객체와 private 포매터 상수를 사용하여 효율적인 구조를 구현했습니다. 한국 로케일을 일관되게 사용하고 있어 좋습니다.
12-14: formatToMonthDay 구현이 올바릅니다.간결하고 명확한 구현입니다.
24-26: formatToYearMonth 구현이 올바릅니다.간결하고 명확한 구현입니다.
core/designsystem/src/main/res/drawable/ic_circle_arrow_left.xml (1)
6-11: 이 아이콘의 색상 정의 방식은 디자인 시스템 전체에서 표준 패턴입니다.이 프로젝트는 다크 모드를 지원하지 않으며 라이트 테마만 사용합니다. 디자인 시스템의 모든 drawable 리소스는 하드코딩된 색상값을 사용하는 것이 표준 방식이므로, ic_circle_arrow_left.xml의 구현은 프로젝트 패턴과 일치합니다.
app/build.gradle.kts (1)
80-80: 새로운 리포트 기능 모듈 의존성이 올바르게 추가되었습니다.다른 feature 모듈들과 일관된 위치에 배치되어 있습니다.
feature/report/.gitignore (1)
1-1: 표준 .gitignore 설정이 올바릅니다.빌드 디렉토리를 무시하는 기본 설정입니다.
feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/QuizCategoryScreen.kt (1)
92-92: ALL 카테고리를 UI에서 올바르게 제외했습니다.ALL 카테고리는 필터링/선택 용도로 사용되므로 퀴즈 카테고리 카드로 렌더링하지 않는 것이 적절합니다.
feature/report/src/main/AndroidManifest.xml (1)
1-4: 표준 feature 모듈 매니페스트 파일입니다.feature 모듈에 적합한 빈 매니페스트 구성입니다.
core/designsystem/build.gradle.kts (1)
9-12: 불변 컬렉션 라이브러리 추가가 적절합니다.리포트 기능의 UI 모델에서 사용되는 ImmutableList를 지원하기 위한 올바른 의존성 추가입니다.
feature/report/proguard-rules.pro (1)
1-21: 표준 ProGuard 템플릿 파일입니다.모든 규칙이 주석 처리된 템플릿 상태이며, 초기 개발 단계에서는 적절합니다. 향후 필요 시 실제 규칙을 추가할 수 있습니다.
core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt (1)
54-56: LGTM!새로운
Report라우트가 기존 패턴을 따르며 올바르게 추가되었습니다.feature/senior/src/main/kotlin/com/moa/app/feature/senior/home/SeniorHomeViewModel.kt (1)
59-61: LGTM!
navigateToReport함수가 다른 네비게이션 메서드와 동일한 패턴으로AppRoute.Report를 사용하도록 올바르게 수정되었습니다.settings.gradle.kts (1)
43-43: LGTM!새로운
feature:report모듈이 올바르게 추가되었습니다.app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)
31-31: LGTM!Report 화면이 기존 네비게이션 패턴을 따라 올바르게 통합되었습니다.
ReportScreen이 내부적으로hiltViewModel()을 사용하므로 추가 설정이 필요하지 않습니다.Also applies to: 88-88
data/src/main/kotlin/com/moa/app/data/di/DataSourceModule.kt (1)
33-36: LGTM!
ReportDataSource바인딩이 기존 패턴(@Binds,@Singleton)을 따라 일관되게 추가되었습니다.feature/report/src/main/java/com/moa/app/feature/report/model/DailyReportUiModel.kt (1)
24-30: 이 코드는 올바릅니다. 추가 조치가 필요하지 않습니다.
DailyReport.advices는 이미ImmutableList<String>타입이므로toImmutableList()로 변환할 필요가 없습니다.dailyQuizScore와 달리advices는 타입 변환이 필요하지 않으며, 이미 불변 리스트이므로 직접 할당해도 Compose 안정성 문제가 발생하지 않습니다.feature/senior/src/main/kotlin/com/moa/app/feature/senior/quiz/category/model/QuizCategoryExtension.kt (2)
17-17:ALL케이스에 대한 빈 문자열 반환 확인 필요
title,description,loadDescription에서ALL이 빈 문자열을 반환합니다. UI에서ALL카테고리가 이 프로퍼티들이 필요한 컨텍스트에서 사용될 경우 빈 텍스트나 레이아웃 문제가 발생할 수 있습니다. 의도된 동작인지 확인해 주세요.Also applies to: 27-27, 59-59
38-38:ALL케이스가PERSISTENCE와 동일한 색상/아이콘 사용
ALL이PERSISTENCE와 동일한 리소스(blue400,img_quiz_list_1등)를 사용하고 있습니다. 전체 카테고리를 나타내는ALL에 고유한 시각적 스타일이 필요한지 확인해 주세요.Also applies to: 49-49, 70-70, 81-81, 92-92
data/src/main/kotlin/com/moa/app/data/di/ServiceModule.kt (1)
39-42: LGTM!기존 서비스 제공자들과 동일한 패턴으로
ReportService를 올바르게 주입하고 있습니다.core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinerProgressIndicator.kt (1)
34-53: LGTM!접근성 시맨틱과 애니메이션 처리가 잘 구현되어 있습니다.
ProgressBarRangeInfo를 통해 스크린 리더 지원을 제공하고, 시각적 너비는animateFloatAsState로 부드럽게 애니메이션됩니다.feature/report/build.gradle.kts (1)
1-24: LGTM!프로젝트 컨벤션에 맞게 feature 모듈이 잘 구성되어 있습니다. 필요한 의존성들이 적절히 선언되었습니다.
domain/src/main/kotlin/com/moa/app/domain/report/usecase/FetchWeeklyReportUseCase.kt (1)
7-12: LGTM!유즈케이스 패턴을 잘 따르고 있으며 리포지토리에 위임하는 구조가 적절합니다. 입력값 검증(예: week가 1-5 범위인지)은 필요시 나중에 추가할 수 있습니다.
domain/src/main/kotlin/com/moa/app/domain/report/usecase/FetchMonthlyReportUseCase.kt (1)
7-12: LGTM!PR 설명에 따르면 월간 리포트는 API 스펙 변경 가능성으로 별도 구현 예정이지만, 유즈케이스 자체는 올바르게 구성되어 있습니다.
data/src/main/kotlin/com/moa/app/data/report/datasource/ReportDataSource.kt (1)
7-11: LGTM!데이터 소스 인터페이스가 깔끔하게 정의되어 있습니다.
Result<T?>를 사용하여 에러 처리와 데이터 없음 상태를 모두 표현할 수 있습니다.domain/src/main/kotlin/com/moa/app/domain/quiz/model/QuizCategory.kt (1)
12-20:fromString제거는 문제가 없습니다코드베이스 전체에서
QuizCategory.fromString사용 사례가 없으며, 현재 코드에도 이 함수가 존재하지 않습니다. 모든QuizCategory참조는QuizCategory.ALL,QuizCategory.PERSISTENCE등의 직접적인 enum 상수만 사용하고 있어, 제거된 함수에 대한 의존성이 없습니다.feature/report/src/main/java/com/moa/app/feature/report/component/EmptyReportContent.kt (1)
15-38: LGTM!빈 상태 UI 컴포넌트가 깔끔하게 구현되었습니다. MoaTheme을 활용한 스타일링과 컴포넌트 구조가 적절합니다.
feature/report/src/main/java/com/moa/app/feature/report/component/QuizCategorySelector.kt (1)
20-48: LGTM!카테고리 선택 UI가 잘 구현되었습니다.
ImmutableList사용과key설정이 적절하며, 선택 상태에 따른 스타일 변경도 올바르게 처리되고 있습니다.domain/src/main/kotlin/com/moa/app/domain/report/model/WeeklyReport.kt (1)
5-18: LGTM!도메인 모델이 명확하게 정의되었습니다.
QuizCategory를 키로 하는Map구조가 다른 리포트 모델과 일관성 있게 설계되었습니다.data/src/main/kotlin/com/moa/app/data/report/service/ReportService.kt (1)
10-29: LGTM!Retrofit 서비스 인터페이스가 프로젝트 패턴에 맞게 잘 정의되었습니다. 모든 엔드포인트가
suspend함수를 사용하고NetworkResult로 래핑된 것이 기존 네트워크 레이어 패턴과 일관성이 있습니다. Based on learnings, 이 프로젝트는 Retrofit suspend 함수만 사용합니다.domain/src/main/kotlin/com/moa/app/domain/report/repository/ReportRepository.kt (1)
7-11: LGTM!Repository 인터페이스가 깔끔하게 정의되었습니다.
Result<T?>패턴으로 에러 처리와 데이터 부재 상황을 모두 처리할 수 있습니다.domain/src/main/kotlin/com/moa/app/domain/report/model/DailyReport.kt (1)
6-16: LGTM! 깔끔한 도메인 모델 구현입니다.
ImmutableList를 사용하여 불변성을 보장하고, 관심사 분리가 잘 되어 있습니다. 도메인 레이어에 적합한 순수 데이터 모델입니다.data/src/main/kotlin/com/moa/app/data/di/RepositoryModule.kt (1)
33-36: LGTM! 기존 패턴을 따르는 DI 바인딩입니다.다른 리포지토리 바인딩(Auth, User, Quiz)과 동일한 패턴으로
@Binds @Singleton을 적용하여 일관성이 유지됩니다.data/src/main/kotlin/com/moa/app/data/report/repositoryImpl/ReportRepositoryImpl.kt (1)
11-33: LGTM! 깔끔한 리포지토리 구현입니다.
mapCatching을 사용하여toDomain()매핑 중 발생할 수 있는 예외를 안전하게 처리합니다. 데이터 소스에서 도메인 모델로의 변환이 명확하고 간결합니다.feature/report/src/main/java/com/moa/app/feature/report/component/WeeklyReportContent.kt (1)
28-98: LGTM! 주간 리포트 UI 컴포넌트가 잘 구성되어 있습니다.섹션별로 명확하게 분리되어 있고, 디자인 시스템 컴포넌트를 적절히 활용합니다.
onCategorySelected콜백을 통해 카테고리 선택 상태를 상위 컴포넌트에서 관리하는 패턴이 적절합니다.feature/report/src/main/java/com/moa/app/feature/report/extension/QuizCategoryEtx.kt (1)
28-39:ALL카테고리의 아이콘이PERSISTENCE와 동일합니다.
QuizCategory.ALL이img_persistence_quiz아이콘을 사용하고 있어 "전체 평균"을 나타내기에 의미적으로 적절하지 않을 수 있습니다. 별도의 "전체" 아이콘이 있다면 사용을 고려해보세요.디자인 의도에 따른 것이라면 무시해도 됩니다.
data/src/main/kotlin/com/moa/app/data/report/datasourceImpl/ReportDataSourceImpl.kt (1)
11-31: LGTM! 데이터 소스 구현이 적절합니다.API 호출을
ReportService에 위임하고,toResult확장 함수로Result타입으로 변환합니다. 도메인 매핑은 리포지토리 레이어에서 수행하므로, 여기서는 identity 함수{ it }를 사용하는 것이 올바릅니다.data/src/main/kotlin/com/moa/app/data/report/model/response/DailyReportResponse.kt (1)
29-43: LGTM! 도메인 매핑 함수가 명확합니다.
toDomain()확장 함수가 DTO를 도메인 모델로 깔끔하게 변환하며,toImmutableList()를 사용하여 불변 컬렉션을 생성합니다.data/src/main/kotlin/com/moa/app/data/report/model/response/WeeklyReportResponse.kt (1)
9-17:Map<QuizCategory, List<WeeklyScoreDto>>직렬화 확인이 필요합니다.kotlinx.serialization에서 enum을 Map 키로 사용할 때 기본적으로 enum name이 사용됩니다. API 응답의 키가
QuizCategoryenum 값과 정확히 일치하는지 확인해주세요 (예:"ALL","PERSISTENCE"등).feature/report/src/main/java/com/moa/app/feature/report/model/WeeklyReportUiModel.kt (1)
1-63: 전체적으로 잘 구현되었습니다.UI 모델과 도메인 모델 간의 매핑이 명확하고, 불변 컬렉션을 적절히 사용하고 있습니다.
currentCategoryScores와chartData의 계산 프로퍼티 구현도 깔끔합니다.core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/chip/MaChip.kt (1)
41-46: 리플 효과 제거가 접근성에 영향을 줄 수 있습니다.
indication = null로 설정하여 클릭 시 시각적 피드백(리플 효과)이 제거됩니다. 이는 디자인 의도일 수 있지만, 사용자가 버튼이 눌렸다는 것을 인지하기 어려울 수 있습니다. 특히 접근성 측면에서 시각적 피드백은 중요합니다. 디자인 요구사항을 확인하고, 필요하다면 최소한의 시각적 피드백(예: 약한 리플 또는 색상 변경)을 추가하는 것을 고려해보세요.feature/report/src/main/java/com/moa/app/feature/report/ReportViewModel.kt (1)
119-120: 로케일이 하드코딩되어 있습니다.
Locale.KOREA가 하드코딩되어 있어, 한국 외 지역의 사용자에게는 주 계산이 올바르지 않을 수 있습니다. 앱이 한국 사용자만을 대상으로 한다면 문제없지만, 다국어를 지원할 계획이라면Locale.getDefault()또는 사용자의 시스템 로케일을 사용하는 것이 좋습니다.
| import com.moa.app.designsystem.theme.MoaTheme | ||
|
|
||
| @Composable | ||
| fun MaLinerProgressIndicator( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오타: "Liner" → "Linear"
MaLinerProgressIndicator에 오타가 있습니다. MaLinearProgressIndicator가 올바른 명칭입니다.
🔎 수정 제안
@Composable
-fun MaLinerProgressIndicator(
+fun MaLinearProgressIndicator(
correct: Int,
total: Int,
modifier: Modifier = Modifier,
shape: RoundedCornerShape = RoundedCornerShape(50)
) {파일명도 MaLinearProgressIndicator.kt로 변경하고, 이 컴포넌트를 사용하는 곳도 함께 수정해야 합니다.
📝 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.
| fun MaLinerProgressIndicator( | |
| @Composable | |
| fun MaLinearProgressIndicator( | |
| correct: Int, | |
| total: Int, | |
| modifier: Modifier = Modifier, | |
| shape: RoundedCornerShape = RoundedCornerShape(50) | |
| ) { |
🤖 Prompt for AI Agents
In
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinerProgressIndicator.kt
around line 22, the component name contains a typo ("MaLinerProgressIndicator")
and should be renamed to "MaLinearProgressIndicator"; rename the file to
MaLinearProgressIndicator.kt, update the Kotlin function/class name accordingly,
search and update all imports/usages across the codebase to the new name, and
run a build to ensure no references remain (optionally add a temporary typealias
or deprecated wrapper with the old name to ease migration).
| val step = (maxRange / 5).toInt() | ||
| for (i in 0..maxRange.toInt() step step) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maxRange가 5 미만일 때 step이 0이 되어 무한 루프가 발생할 수 있습니다.
step이 (maxRange / 5).toInt()로 계산되므로, maxRange가 5 미만이면 step이 0이 됩니다. 이 경우 step step 루프가 무한히 실행됩니다.
🔎 제안된 수정
- val step = (maxRange / 5).toInt()
+ val step = (maxRange / 5).toInt().coerceAtLeast(1)
for (i in 0..maxRange.toInt() step step) {📝 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.
| val step = (maxRange / 5).toInt() | |
| for (i in 0..maxRange.toInt() step step) { | |
| val step = (maxRange / 5).toInt().coerceAtLeast(1) | |
| for (i in 0..maxRange.toInt() step step) { |
🤖 Prompt for AI Agents
In
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/product/chart/MaBarChart.kt
around lines 56–57, the computation val step = (maxRange / 5).toInt() can
produce 0 when maxRange < 5, causing the for (... step step) loop to hang;
change the calculation to ensure step is at least 1 (e.g., clamp or use max(1,
(maxRange / 5).toInt()) or handle small maxRange by using step = 1) so the loop
always progresses and avoid division-by-zero/zero-step infinite loop.
data/src/main/kotlin/com/moa/app/data/report/model/response/DailyReportResponse.kt
Show resolved
Hide resolved
data/src/main/kotlin/com/moa/app/data/report/model/response/MonthlyReportResponse.kt
Outdated
Show resolved
Hide resolved
domain/src/main/kotlin/com/moa/app/domain/report/model/MonthlyReport.kt
Outdated
Show resolved
Hide resolved
feature/report/src/androidTest/java/com/moa/app/feature/report/ExampleInstrumentedTest.kt
Outdated
Show resolved
Hide resolved
feature/report/src/main/java/com/moa/app/feature/report/component/DailyReportContent.kt
Show resolved
Hide resolved
| import androidx.compose.ui.semantics.Role | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deprecated import 사용
androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel은 deprecated되었습니다. androidx.hilt.navigation.compose.hiltViewModel을 사용해야 합니다.
🔎 수정 제안
-import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import androidx.hilt.navigation.compose.hiltViewModel📝 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.
| import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel | |
| import androidx.hilt.navigation.compose.hiltViewModel |
🤖 Prompt for AI Agents
In feature/report/src/main/java/com/moa/app/feature/report/ReportScreen.kt
around line 28, the import
androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel is deprecated; replace
it with androidx.hilt.navigation.compose.hiltViewModel and update any references
if necessary so the composable ViewModel retrieval uses the new import path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
관련 문서 링크를 제공해줄 수 있어?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel deprecated androidx.hilt.navigation.compose
💡 Result:
Short answer: The hiltViewModel() in androidx.hilt.navigation.compose is deprecated — it was moved to the androidx.hilt.lifecycle.viewmodel.compose package. Use the hilt-lifecycle-viewmodel-compose artifact and import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel instead. [1][2]
Example (Gradle + import):
- implementation "androidx.hilt:hilt-lifecycle-viewmodel-compose:" or use the Compose libraries page recommendation for current version. [2][3]
- import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
- val vm: MyViewModel = hiltViewModel()
Sources: Android API docs for androidx.hilt.navigation.compose (deprecated) and androidx.hilt.lifecycle.viewmodel.compose (current). [1][2]
@wjdrjs00, 죄송합니다. 제 초기 리뷰 코멘트가 잘못되었습니다. 😅
실제로는 정반대입니다:
- ❌
androidx.hilt.navigation.compose.hiltViewModel→ deprecated됨 - ✅
androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel→ 현재 권장되는 방식
현재 코드가 이미 올바른 import를 사용하고 있으므로 변경이 필요하지 않습니다!
공식 Android 문서에 따르면, hiltViewModel()은 androidx.hilt.navigation.compose에서 androidx.hilt.lifecycle.viewmodel.compose 패키지로 이동되었습니다.
관련 문서:
- Android API 문서 - androidx.hilt.navigation.compose (deprecated)
- Android API 문서 - androidx.hilt.lifecycle.viewmodel.compose (current)
- Compose libraries 페이지에서 최신 버전 확인 가능
혼란을 드려 죄송합니다! 🙏
| @@ -0,0 +1,183 @@ | |||
| package com.moa.app.feature.report | |||
|
|
|||
| import android.util.Log | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
일관된 로깅 라이브러리를 사용해주세요.
android.util.Log와 Timber를 혼용하고 있습니다. 126번 라인에서 Log.d를 사용하고 있지만, 다른 곳에서는 Timber.d를 사용합니다. 일관성을 위해 전체적으로 Timber를 사용하도록 통일해주세요.
🔎 제안하는 수정안
-import android.util.Log
...
private fun fetchWeeklyReport() {
viewModelScope.launch {
...
fetchWeeklyReportUseCase(year, month, week).fold(
onSuccess = { weeklyReport ->
_uiState.update { it.copy(weeklyReport = weeklyReport?.toUiModel()) }
},
onFailure = {
- Log.d("ReportViewModel", "fetchWeeklyReport: $it")
+ Timber.d("fetchWeeklyReport: $it")
},
)
}
}Also applies to: 126-126
🤖 Prompt for AI Agents
In feature/report/src/main/java/com/moa/app/feature/report/ReportViewModel.kt
around line 3 (and the Log.d usage at line 126), remove the android.util.Log
import and any Log.d usages and replace them with Timber: add/import
timber.log.Timber at the top, change Log.d(...) to Timber.d(...), and ensure
Timber is initialized in the app (if not already) so logging remains consistent
across the module.
feature/report/src/main/java/com/moa/app/feature/report/util/DateUtils.kt
Show resolved
Hide resolved
There was a problem hiding this 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
🧹 Nitpick comments (3)
domain/src/main/kotlin/com/moa/app/domain/report/model/MonthlyReport.kt (2)
5-12: 검증 로직 추가를 고려해보세요.
completeRate와correctRate가 퍼센트 값(0-100)을 나타낸다면, 도메인 모델에 범위 검증을 추가하는 것이 좋습니다.init블록이나 별도의 팩토리 함수에서 유효성 검사를 수행할 수 있습니다.🔎 제안하는 검증 로직 예시
data class MonthlyReport( val oneLineReview: String, val completeRate: Int, val correctRate: Int, val diagnosis: String, val score: Map<QuizCategory, MonthReportScore>, val longTermStrategy: String, -) +) { + init { + require(completeRate in 0..100) { "completeRate must be between 0 and 100" } + require(correctRate in 0..100) { "correctRate must be between 0 and 100" } + } +}
14-18: 주 인덱스 범위 검증을 고려해보세요.
weekIndex가 월의 주차를 나타낸다면, 유효한 범위(예: 1-5)를 벗어나는 값을 방지하기 위한 검증을 추가하는 것이 좋습니다.🔎 제안하는 검증 로직 예시
data class MonthReportScore( val weekIndex: Int, val score: Long, val lastMonthScore: Long, -) +) { + init { + require(weekIndex in 1..5) { "weekIndex must be between 1 and 5" } + } +}core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinearProgressIndicator.kt (1)
22-27: 입력 파라미터에 대한 검증 로직 추가를 권장합니다.현재
correct와total에 대한 명시적인 검증이 없어 음수 값이나correct > total상황이 처리되지 않습니다. 공개 API이므로 파라미터 제약사항을 KDoc으로 문서화하거나require문을 사용한 검증 추가를 고려해주세요.🔎 제안하는 개선사항
+/** + * 정답 개수와 전체 문항 수를 기반으로 진행률을 표시하는 선형 프로그레스 인디케이터입니다. + * + * @param correct 정답 개수 (0 이상) + * @param total 전체 문항 수 (0 이상) + * @param modifier 컴포저블에 적용할 Modifier + * @param shape 프로그레스 바의 모서리 형태 + */ @Composable fun MaLinearProgressIndicator( correct: Int, total: Int, modifier: Modifier = Modifier, shape: RoundedCornerShape = RoundedCornerShape(50) ) { + require(correct >= 0) { "correct must be non-negative" } + require(total >= 0) { "total must be non-negative" } + val progressRatio = if (total > 0) correct.toFloat() / total.toFloat() else 0f
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinearProgressIndicator.ktdata/src/main/kotlin/com/moa/app/data/report/model/response/MonthlyReportResponse.ktdomain/build.gradle.ktsdomain/src/main/kotlin/com/moa/app/domain/quiz/model/QuizCategory.ktdomain/src/main/kotlin/com/moa/app/domain/report/model/MonthlyReport.ktfeature/report/src/main/java/com/moa/app/feature/report/component/DailyReportContent.ktfeature/report/src/main/java/com/moa/app/feature/report/util/DateUtils.kt
🚧 Files skipped from review as they are similar to previous changes (2)
- domain/src/main/kotlin/com/moa/app/domain/quiz/model/QuizCategory.kt
- feature/report/src/main/java/com/moa/app/feature/report/component/DailyReportContent.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: Run ktlint
- GitHub Check: Run Unit Tests
🔇 Additional comments (8)
feature/report/src/main/java/com/moa/app/feature/report/util/DateUtils.kt (4)
9-11: 이전 리뷰 피드백이 잘 반영되었습니다.
WEEK_FIELDS_KOREA를 private 상수로 캐싱하여 매 호출마다WeekFields.of(Locale.KOREA)를 생성하는 비용을 제거했습니다. 성능 개선이 적절하게 적용되었습니다.
13-15: 구현이 올바릅니다.한국 로케일에 맞는 "M월 d일" 형식으로 날짜를 포맷하는 로직이 명확하고 정확합니다.
17-22: 0주차 문제가 해결되었습니다.
coerceAtLeast(1)을 사용하여 월의 첫 며칠이 이전 주에 속하는 경우에도 최소 1주차로 표시되도록 보장합니다. 이전 리뷰에서 지적된 엣지 케이스가 적절하게 처리되었습니다.
24-26: 구현이 올바릅니다.한국 로케일에 맞는 "yyyy년 M월" 형식으로 날짜를 포맷하는 로직이 명확하고 정확합니다.
data/src/main/kotlin/com/moa/app/data/report/model/response/MonthlyReportResponse.kt (1)
26-43: 깔끔한 매핑 구현입니다.DTO에서 도메인 모델로의 변환 로직이 명확하고 올바르게 구현되어 있습니다. 특히 Line 32의
mapValues { it.value.toDomain() }를 통한 중첩 맵 변환이 적절합니다.core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinearProgressIndicator.kt (2)
28-32: 진행률 계산 로직이 올바릅니다.0으로 나누기 방지와 범위 제한(
coerceIn)이 적절히 처리되어 있고,animateFloatAsState를 사용한 애니메이션 구현도 적절합니다.
46-52: 애니메이션 구현이 올바릅니다.
fillMaxWidth(animatedProgress)를 사용하여 부드러운 진행률 애니메이션을 구현했습니다. 색상과 클리핑도 적절하게 적용되었습니다.domain/build.gradle.kts (1)
8-8: kotlinx.serialization.json 1.9.0은 최신 안정 버전으로 보안상 문제없습니다.도메인 모듈에 kotlinx.serialization.json 의존성 추가는 적절합니다. 버전 카탈로그에 정의된 1.9.0은 최신 안정 버전(2025년 6월 27일 릴리스)이며, 공개된 보안 취약점이 없습니다. 또한 이 의존성은 이미 core/datastore, core/network, data 모듈에서 사용 중이므로 프로젝트 전체 패턴과도 일치합니다.
| .semantics { | ||
| progressBarRangeInfo = ProgressBarRangeInfo( | ||
| current = progressRatio, | ||
| range = 0f..1f | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
접근성 시맨틱 값이 시각적 애니메이션과 불일치합니다.
Line 41에서 progressRatio를 사용하고 있어, 접근성 도구에는 즉시 최종 값이 전달되지만 시각적으로는 animatedProgress가 부드럽게 애니메이션됩니다. 이로 인해 스크린 리더 사용자와 시각적 사용자 간 경험이 달라질 수 있습니다.
🔎 제안하는 수정
.semantics {
progressBarRangeInfo = ProgressBarRangeInfo(
- current = progressRatio,
+ current = animatedProgress,
range = 0f..1f
)
}📝 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.
| .semantics { | |
| progressBarRangeInfo = ProgressBarRangeInfo( | |
| current = progressRatio, | |
| range = 0f..1f | |
| ) | |
| } | |
| .semantics { | |
| progressBarRangeInfo = ProgressBarRangeInfo( | |
| current = animatedProgress, | |
| range = 0f..1f | |
| ) | |
| } |
🤖 Prompt for AI Agents
In
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/indicator/MaLinearProgressIndicator.kt
around lines 39 to 44, the semantics currently expose progressRatio (the static
target) while the UI shows animatedProgress, causing accessibility tools to
report a value that doesn't match the visual animation; update the semantics to
use the animated progress value instead (e.g., use animatedProgress.coerceIn(0f,
1f) as the ProgressBarRangeInfo current) so screen readers see the same
interpolated value as users see, and keep the range as 0f..1f.
Related issue 🛠
Work Description ✏️
Screenshot 📸
Uncompleted Tasks 😅
Summary by CodeRabbit
릴리스 노트
새로운 기능
UI 개선
✏️ Tip: You can customize this high-level summary in your review settings.