[Refactor/#107] 추천 루틴 화면 리다자인 변경 사항을 반영합니다.#113
Conversation
Walkthrough추천 루틴의 데이터·도메인·프리젠테이션 계층을 개편했습니다. DTO에 recommendedRoutineType을 추가하고 도메인/매핑을 갱신했으며, UI는 화면 리디자인(아이템 구조, 버튼 위치/노출 조건, 난이도 표기, 빈 상태 뷰, 스크롤 동작 등)을 반영했습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Screen as RecommendRoutineScreen
participant VM as ViewModel
participant UI as UI Components
User->>Screen: 카테고리 선택/변경
Screen->>Screen: LaunchedEffect -> listState.scrollToItem(0)
Screen->>VM: 선택 상태 업데이트
VM-->>Screen: uiState (selectedCategory, selectedRecommendLevel, currentRoutines)
alt selectedCategory == PERSONALIZED && emotionMarbleType == null
Screen->>UI: EmotionRecommendRoutineButton 표시
else
Screen->>UI: 버튼 숨김
end
alt currentRoutines.isEmpty && selectedRecommendLevel != null
Screen->>UI: EmptyRecommendRoutineView 표시
else
Screen->>UI: LazyColumn (RecommendRoutineItem(routine) ...)
end
User->>UI: CTA(추천받기) 클릭
UI->>Screen: onClick 콜백 전달
Note right of Screen: 네비게이션/등록 로직은 기존 핸들러에 위임
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Assessment against linked issues
Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (15)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (5)
29-41: 행 전체에서 CTA만 클릭 가능하도록 변경된 상호작용 범위 — 의도 확인 및 대안 제안리디자인 의도라면 OK. 다만 기존 대비 접근성/발견성(탭 영역 축소) 저하 가능성이 있습니다. 리스트 아이템 성격이라면 행 전체를 눌러도 onClick 되도록 유지하는 게 사용자 기대치에 부합합니다. 필요 시 아래처럼 Row에 onClick을 주고, CTA는 비클릭으로 두는 방안 고려해 주세요.
Row( modifier = modifier .fillMaxWidth() .background( color = BitnagilTheme.colors.coolGray10, shape = RoundedCornerShape(12.dp), ) .padding( vertical = 14.dp, horizontal = 16.dp, - ), + ) + .clickableWithoutRipple { onClick() }, verticalAlignment = Alignment.CenterVertically, ) {그리고 CTA Box에서는 클릭 제거:
- .clickableWithoutRipple { onClick() }
51-54: 문자열 하드코딩 제거: stringResource로 국제화/번역 대응현재 한글 하드코딩입니다. 리소스로 분리해 번역과 테스트 용이성을 확보하는 게 좋습니다.
- Text( - text = "내 기분에 맞는 루틴 추천받기", + Text( + text = stringResource(PR.string.emotion_recommend_routine_title), color = BitnagilTheme.colors.white, style = BitnagilTheme.typography.body2SemiBold, ) ... - Text( - text = "추천받기", + Text( + text = stringResource(PR.string.get_recommendation), color = BitnagilTheme.colors.white, style = BitnagilTheme.typography.caption1SemiBold, )필요 import 및 R alias(디자인시스템 R과 충돌 방지):
import androidx.compose.ui.res.stringResource import com.threegap.bitnagil.presentation.R as PRstrings.xml 예시(제안):
<resources> <string name="emotion_recommend_routine_title">내 기분에 맞는 루틴 추천받기</string> <string name="get_recommendation">추천받기</string> </resources>Also applies to: 70-73
43-45: 이미지 contentDescription 확인contentDescription = null은 장식용 이미지일 때 적절합니다. 해당 아이콘이 의미 전달(예: 감정 관련)를 한다면 stringResource 기반 설명을 제공해 주세요. 장식용이 확실하면 현 상태 유지 OK.
예시:
contentDescription = stringResource(PR.string.default_marble_desc)
78-84: Preview에 테마 적용 권장디자인 토큰(BitnagilTheme)을 사용하는 컴포저블은 Preview에서도 테마로 감싸주는 게 안전합니다.
@Preview @Composable private fun EmotionRecommendRoutineButtonPreview() { - EmotionRecommendRoutineButton( - onClick = {}, - ) + BitnagilTheme { + EmotionRecommendRoutineButton( + onClick = {}, + ) + } }
17-22: 리소스 import 정리(문자열/아이콘 R 충돌 방지)stringResource 도입 시 디자인시스템 R과 앱/프레젠테이션 R 충돌이 날 수 있으니 alias 사용을 권장합니다.
import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource ... import com.threegap.bitnagil.designsystem.R +import com.threegap.bitnagil.presentation.R as PRdomain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt (1)
12-16: 도메인 계층에서의 한글 문자열 반환은 i18n/레이어링 관점에서 재고 필요toKoreanLevel()은 UI 표시 목적의 한국어 문자열을 도메인에 고정합니다. 다국어 대응이나 레이어 책임 분리를 고려하면, 프레젠테이션 계층의 리소스/매퍼(예: extension in presentation)로 이동하거나 UI에서 string resource를 통해 매핑하는 편이 깔끔합니다.
원하시면 presentation 모듈에
RecommendLevel.toLocalizedLabel(context)형태의 확장 함수를 생성하는 패치를 제안드릴게요.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/atom/RecommendCategoryChip.kt (1)
29-38: 클릭 영역 축소 이슈: clickable는 padding 이후에 적용하세요현재 modifier 체인에서 clickable가 padding보다 먼저 적용되어, 내부 패딩 영역이 클릭되지 않을 수 있습니다. UX 저하를 유발할 수 있으니 clickable을 가장 마지막(혹은 최소한 padding 이후)으로 이동을 권장합니다. 또한 터치 타겟 접근성 최소 기준(48dp)을 고려하면 height 36dp는 다소 작습니다.
권장 수정안:
- .background( + .background( color = if (!isSelected) BitnagilTheme.colors.white else BitnagilTheme.colors.coolGray10, shape = RoundedCornerShape(20.dp), ) - .height(36.dp) - .clickableWithoutRipple(onClick = onCategorySelected) - .padding( - vertical = 6.dp, - horizontal = 14.dp, - ), + .height(36.dp) + .padding( + vertical = 6.dp, + horizontal = 14.dp, + ) + .clickableWithoutRipple(onClick = onCategorySelected),가능하다면 높이를 48.dp로 상향하거나, 외곽에 추가 padding을 두고 clickable을 바깥에 적용해 실제 터치 타겟을 확장하는 것도 고려해주세요.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
54-54: 문자열 합치기 기반의 포맷 의존성 축소 제안
"난이도 ${recommendLevel.toKoreanLevel()} | ${recommendLevel.displayName}"처럼 합성 문자열에 split 로직을 의존하는 대신,LevelOption에 두 개의 인자(예:levelLabel: String,displayLabel: String)를 직접 전달하거나, 도메인 객체(혹은 UI 전용 모델)를 넘겨 내부에서 스타일링하는 구조가 더 견고합니다. 표시 구분자나 레이아웃 변화에 강해집니다.원하시면
LevelOption(levelLabel: String, displayLabel: String, ...)형태로의 리팩터링 패치를 제안드릴게요.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt (1)
9-11: 기본값(0, "") 도입에 따른 식별자/키 사용 주의id 기본값 0은 LazyList key 등에서 충돌을 유발할 수 있습니다. 리스트 키로 사용하는 경우 서버에서 오는 실제 id 또는 stable한 고유 키를 사용하도록 점검 부탁드립니다. name의 빈 문자열 기본값도 UI에서 자리 표시나 접근성(컨텐츠 설명) 측면에서 오해를 줄 수 있으니 필요 시 placeholder 처리 고려 바랍니다.
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt (1)
21-25: 호환성 보완: Serialization 오류 방지 위해 필드 기본값 지정 권장
- 대상 파일
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt
위치: 21–25행- 변경 제안:
- @SerialName("recommendedRoutineType") - val recommendedRoutineType: String, + @SerialName("recommendedRoutineType") + val recommendedRoutineType: String? = null,
- 매핑 로직에서도
recommendedRoutineType이 nullable 및 기본값(null)을 처리하도록 함께 보완해주세요.이렇게 하면 서버에서 해당 필드를 누락해서 보내더라도
kotlinx.serialization역직렬화 실패로 인한 앱 크래시 리스크를 방어할 수 있습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (1)
192-195: TODO 주석 처리가 필요합니다.추천 카테고리를 함께 전달해야 한다는 TODO가 있습니다. PR 설명에 따르면 향후 작업으로 명시되어 있으니, 이슈로 등록하여 추적하는 것이 좋겠습니다.
추천 카테고리 전달 기능을 구현하는 이슈를 생성하시겠습니까?
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (4)
15-20: nullable + 기본값 도입으로 하위 사용처 영향 점검 필요
- level, recommendedRoutineType가 nullable로 변경되었습니다. UI에서 텍스트 변환/표시 시 NPE 또는 불필요한 Elvis 처리 증가 가능성이 있습니다.
- id 기본값 0은 리스트 키/식별자로 사용 시 충돌 여지가 있으니(실 ID 0 가능성 포함) 의도된 sentinel인지 확인 바랍니다.
- recommendSubRoutines의 기본 emptyList는 Compose Preview/초기 렌더링에 유익합니다.
가능하면 도메인/디자인 정책에 맞는 안전한 기본값(예: UNKNOWN 레벨/카테고리)을 사용하거나, UI 전용 파생 프로퍼티(safe getter)를 두어 null 분기를 내부로 캡슐화하는 방식을 권장합니다.
4-9: @DrawableRes 어노테이션으로 리소스 타입 안정성 보강 제안아이콘 리소스 ID에 @DrawableRes를 부여하면 호출/대입 시 타입 안전성이 올라가고 Lint가 잘 잡아줍니다.
아래처럼 import 및 어노테이션을 추가하는 것을 제안합니다.
+import androidx.annotation.DrawableRes- val icon: Int + @get:DrawableRes + val icon: Int get() = recommendedRoutineType?.getIcon() ?: R.drawable.ic_shine-private fun RecommendCategory.getIcon(): Int = +@DrawableRes +private fun RecommendCategory.getIcon(): Int = when (this) { ... }Also applies to: 21-22, 37-38
49-60: 색상 매핑 중복 분기 축약 및 스펙 확인
- 동일 색상을 사용하는 분기를 묶어 가독성을 개선할 수 있습니다.
- OUTING_REPORT/UNKNOWN을 yellow10으로 처리한 것이 디자인 스펙과 일치하는지 확인 바랍니다.
축약 예시:
@Composable private fun RecommendCategory.getColor(): Color = when (this) { RecommendCategory.OUTING -> BitnagilTheme.colors.skyBlue10 RecommendCategory.WAKE_UP -> BitnagilTheme.colors.orange25 RecommendCategory.CONNECT -> BitnagilTheme.colors.purple10 RecommendCategory.REST -> BitnagilTheme.colors.green10 RecommendCategory.GROW -> BitnagilTheme.colors.pink10 - RecommendCategory.PERSONALIZED -> BitnagilTheme.colors.yellow10 - RecommendCategory.OUTING_REPORT -> BitnagilTheme.colors.yellow10 - RecommendCategory.UNKNOWN -> BitnagilTheme.colors.yellow10 + RecommendCategory.PERSONALIZED, + RecommendCategory.OUTING_REPORT, + RecommendCategory.UNKNOWN -> BitnagilTheme.colors.yellow10 }
21-26: Fallback 동작에 대한 간단한 유닛 테스트 권장
- recommendedRoutineType=null일 때 icon/color가 기본값(shine/yellow10)으로 나오는지
- 각 카테고리별 아이콘/색상 매핑이 의도와 일치하는지
작은 단위 테스트로 회귀를 방지할 수 있습니다. 필요하시면 테스트 스켈레톤을 만들어 드리겠습니다.
Also applies to: 37-60
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (10)
core/designsystem/src/main/res/drawable-hdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-hdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-mdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-mdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xhdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xhdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxhdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxhdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxxhdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxxhdpi/default_marble.pngis excluded by!**/*.png
📒 Files selected for processing (12)
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt(3 hunks)domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutine.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt(7 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/atom/RecommendCategoryChip.kt(2 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt(2 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt(3 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (1)
EmotionRecommendRoutineButton(24-76)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt (1)
EmptyRecommendRoutineView(14-45)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (1)
RecommendRoutineItem(28-104)
🔇 Additional comments (14)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (1)
56-57: LGTM: Spacer(weight=1f)로 우측 CTA 정렬리스트 내 아이템 배치 간결하고 의도에 부합합니다.
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt (1)
34-36: fromString 폴백 로직 이미 구현됨 — 추가 변경 불필요
RecommendCategory.fromString은 알 수 없는 categoryName에 대해 UNKNOWN으로 폴백 처리되며, DTO의recommendedRoutineType: String필드는 non-nullable이라 null이 들어올 수 없습니다. 제안하신?: ""코드는 컴파일되지 않을 뿐더러 불필요하니 그대로 두셔도 안전합니다.
- domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendCategory.kt:
fromString에서?: UNKNOWN폴백 구현- data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt:
recommendedRoutineType은 non-nullable StringLikely an incorrect or invalid review comment.
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutine.kt (1)
9-9: LGTM! 추천 루틴 타입 필드 추가가 적절합니다.리디자인 요구사항에 따라
recommendedRoutineType필드가 추가되었습니다. 이 변경은 UI에서 루틴별 아이콘과 색상을 표시하는 데 필요한 정보를 제공합니다.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt (1)
14-45: Empty 상태 뷰 구현이 잘 되었습니다.난이도별 추천 루틴이 없을 때 표시되는 Empty 뷰가 적절하게 구현되었습니다. 디자인 가이드라인에 맞는 스타일과 메시지를 제공합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineState.kt (1)
18-19: 감정 버튼 표시 로직이 명확하게 구현되었습니다.
shouldShowEmotionButton프로퍼티가 적절한 조건으로 구현되었습니다. PERSONALIZED 카테고리이면서 감정이 선택되지 않은 경우에만 버튼을 표시하는 로직이 명확합니다.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (3)
29-33: 데이터 모델 기반 API로 변경이 적절합니다.개별 문자열 파라미터 대신
RecommendRoutineUiModel객체를 받도록 변경한 것이 좋습니다. 데이터의 일관성과 확장성이 향상됩니다.
76-102: 세부 루틴 표시 구현이 잘 되었습니다.세부 루틴이 있을 때만 조건부로 렌더링하는 로직이 적절합니다. 구분선과 함께 세부 루틴 목록을 깔끔하게 표시합니다.
49-58:routine.color의 null 처리 불필요
현재RecommendRoutineUiModel에서color는 non-nullableColor타입이며, 내부 구현이recommendedRoutineType이 null일 때도
BitnagilTheme.colors.yellow10을 기본값으로 반환하도록 되어 있어 추가 처리가 필요 없습니다:val color: Color @Composable get() = recommendedRoutineType?.getColor() ?: BitnagilTheme.colors.yellow10해당 코멘트는 무시하셔도 좋습니다.
Likely an incorrect or invalid review comment.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (3)
85-91: 스크롤 위치 초기화 로직이 적절합니다.카테고리 변경 시 리스트를 맨 위로 스크롤하는 기능이 잘 구현되었습니다. 사용자 경험을 개선하는 좋은 추가사항입니다.
121-128: 감정 버튼 조건부 렌더링이 잘 구현되었습니다.
shouldShowEmotionButton상태에 따라 감정 추천 버튼을 표시하는 로직이 적절합니다.
173-177: Empty 상태 처리가 적절합니다.난이도가 선택되었지만 해당하는 루틴이 없을 때
EmptyRecommendRoutineView를 표시하는 로직이 잘 구현되었습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (3)
4-9: Compose/Theme 및 R 의존성 추가 방향 문제 없습니다카테고리별 색상/아이콘 계산 로직 도입에 따른 import 정리가 일관적입니다.
28-35: toUiModel 매핑 LGTM도메인 모델에서 recommendedRoutineType을 전달하고, 서브루틴 매핑도 일관됩니다. 제거된 필드(description/executionTime)와 충돌하는 부분도 없습니다.
37-47: 아이콘 매핑 중복 분기 병합 제안 및 드로어블 리소스 재확인
- RecommendCategory.PERSONALIZED, OUTING_REPORT, UNKNOWN이 같은 R.drawable.ic_shine 을 가리키므로 한 줄로 묶어 가독성과 유지보수성 향상 권장
- 아래 리소스가 실제로 프로젝트 내에 존재하는지 확인 필요
- ic_outside
- ic_wakeup
- ic_connect
- ic_rest
- ic_grow
- ic_shine
경로 예시:- presentation/src/main/res/drawable/
- presentation/src/main/res/drawable-/
중복 분기 축약 예시:
private fun RecommendCategory.getIcon(): Int = when (this) { RecommendCategory.OUTING -> R.drawable.ic_outside RecommendCategory.WAKE_UP -> R.drawable.ic_wakeup RecommendCategory.CONNECT -> R.drawable.ic_connect RecommendCategory.REST -> R.drawable.ic_rest RecommendCategory.GROW -> R.drawable.ic_grow - RecommendCategory.PERSONALIZED -> R.drawable.ic_shine - RecommendCategory.OUTING_REPORT -> R.drawable.ic_shine - RecommendCategory.UNKNOWN -> R.drawable.ic_shine + RecommendCategory.PERSONALIZED, + RecommendCategory.OUTING_REPORT, + RecommendCategory.UNKNOWN -> R.drawable.ic_shine }
...egap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt
Show resolved
Hide resolved
...eegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt
Outdated
Show resolved
Hide resolved
...eegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (20)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/atom/RecommendCategoryChip.kt (2)
30-37: 선택/비선택 배경색 반전은 의도 이해. 다만 터치 타깃(36dp)·경계 가시성·접근성(선택 상태 표시) 보완을 권장
- 36dp 고정 높이는 Material 가이드(최소 48dp) 미만이라 터치 미스가 늘 수 있습니다.
- 비선택 배경이 white로 바뀌면서 컨테이너도 white인 경우 칩 경계가 흐려질 수 있습니다(보더 필요 가능).
- 현재 clickableWithoutRipple을 쓰고 있어 접근성(선택 상태 전달) 관점에서 semantics/selectable 도입이 안전합니다.
권장 수정:
- 최소 터치 타깃 확보: minimumInteractiveComponentSize() 또는 heightIn(min = 48.dp) 사용.
- 경계 가시성: 비선택 상태에 한해 1dp 보더(coolGray20 등) 고려.
- 접근성: selectable(role = Role.Tab, selected = isSelected, indication = null)로 semantics 부여.
예시 diff:
- shape = RoundedCornerShape(20.dp), + shape = RoundedCornerShape(18.dp), @@ - .height(36.dp) + .minimumInteractiveComponentSize()minimumInteractiveComponentSize 사용 시 import가 필요합니다:
import androidx.compose.material3.minimumInteractiveComponentSize보더는 조건부 적용이므로 체이닝 내 if/then으로 처리하거나 확장함수(conditional Modifier) 사용을 권장합니다.
필요 시 selectable로 대체하는 예시(참고용):
modifier = modifier .selectable( selected = isSelected, onClick = onCategorySelected, role = Role.Tab, indication = null, interactionSource = remember { MutableInteractionSource() }, )원하시면 conditional Modifier 확장과 함께 보더/semantics 반영한 패치를 만들어드리겠습니다.
42-43: 선택 상태 텍스트 색상 대비 확인 필요(white on coolGray10 가능성) — 대비 부족 시 텍스트 색상 조정 권장배경이 coolGray10일 때 텍스트를 white로 유지하면 대비가 부족할 수 있습니다. 디자인 의도대로 배경을 밝게 가져가려면 선택 상태 텍스트를 진한 톤(예: navy500 또는 coolGray90)으로 변경하는 편이 안전합니다.
제안 diff:
- color = if (!isSelected) BitnagilTheme.colors.coolGray60 else BitnagilTheme.colors.white, + color = if (!isSelected) BitnagilTheme.colors.coolGray60 else BitnagilTheme.colors.navy500,
- 타이포 변화(caption1Medium/ caption1SemiBold)는 의도대로 보입니다. 다만 대비 이슈가 남으면 굵기보다 색상을 먼저 조정하는 것이 효과적입니다.
디자인 토큰(coolGray10/white/navy500)의 실제 HEX를 공유해주시면 명도 대비(AA/AAA) 기준 충족 여부를 계산해 드릴 수 있습니다.
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt (1)
12-16: 도메인 계층에 하드코딩된 현지화 문자열이 섞여 있습니다 — 프레젠테이션 레이어로 이동 권장toKoreanLevel()이 도메인 모델에서 직접 "하/중/상" 한글 문자열을 반환하고 있어 i18n/로캘 변경에 취약합니다. 도메인은 UI 언어에 독립적인 값을 유지하고, 표시 문자열은 Presentation에서 stringResource로 매핑하는 편이 유지보수에 유리합니다.
제안 1) Presentation 모듈에 확장함수로 옮기기:
// presentation 모듈 어딘가 fun RecommendLevel.labelRes(): Int = when (this) { RecommendLevel.LEVEL1 -> R.string.recommend_level_low // "하" RecommendLevel.LEVEL2 -> R.string.recommend_level_mid // "중" RecommendLevel.LEVEL3 -> R.string.recommend_level_high // "상" }사용 예:
Text(text = stringResource(id = selectedRecommendLevel.labelRes()))제안 2) 혹은 "LOW/MID/HIGH" 같은 중립 레이블(또는 별도 enum/값 객체)을 도메인이 반환하고, UI에서만 현지화 문자열로 변환하세요.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (3)
54-54: " | " 구분자 기반 문자열 분할은 취약합니다 — LevelOption에 구조화된 파라미터로 변경 제안optionText를 "난이도 X | 설명" 형태의 단일 문자열로 만들고 split(" | ")로 파싱하는 방식은 i18n, 카피 변경, 구분자 포함 가능성에 취약합니다. left/right 텍스트를 별도 파라미터로 받아 AnnotatedString을 구성하도록 변경하면 견고합니다.
아래 diff 적용을 제안합니다:
@@ - LevelOption( - optionText = "난이도 ${recommendLevel.toKoreanLevel()} | ${recommendLevel.displayName}", + LevelOption( + leftText = "난이도 ${recommendLevel.toKoreanLevel()}", + rightText = recommendLevel.displayName, isSelected = selectedRecommendLevel == recommendLevel, onClick = { @@ -private fun LevelOption( - optionText: String, +private fun LevelOption( + leftText: String, + rightText: String, isSelected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ - val parts = optionText.split(" | ") - val annotatedString = buildAnnotatedString { - if (parts.size >= 2) { + val annotatedString = buildAnnotatedString { withStyle( style = BitnagilTheme.typography.body1SemiBold.toSpanStyle(), ) { - append(parts[0]) + append(leftText) } withStyle( style = SpanStyle( color = BitnagilTheme.colors.coolGray10, baselineShift = BaselineShift(0.1f), ), ) { append(" | ") } withStyle( style = BitnagilTheme.typography.body1Regular.toSpanStyle(), ) { - append(parts[1]) + append(rightText) } - } } @@ - optionText = "난이도 상 | 가볍게 할 수 있어요", + leftText = "난이도 상", + rightText = "가볍게 할 수 있어요", isSelected = true, onClick = {},추가로, "난이도 ${...}" 문자열은 stringResource 포맷으로 전환하면 i18n에 안전합니다. (예: stringResource(R.string.level_prefix, recommendLevel.toKoreanLevel()))
Also applies to: 82-86, 93-117, 119-123, 134-141
54-54: 하드코딩된 한글 문자열을 stringResource로 이전 권장"난이도 ..." 문자열을 string 리소스로 분리하면 다국어 대응과 A/B 테스트에 유리합니다. 포맷 리소스(예: "난이도 %s")를 만들어 사용해 주세요.
102-109: 분리자 색상 지정은 중복입니다Text의 color가 이미 coolGray10으로 지정되어 있어, 분리자 " | "에 동일 색상을 SpanStyle로 다시 지정하는 부분은 불필요합니다. 유지해도 무방하지만 제거하면 코드가 간결해집니다.
- withStyle( - style = SpanStyle( - color = BitnagilTheme.colors.coolGray10, - baselineShift = BaselineShift(0.1f), - ), - ) { + withStyle( + style = SpanStyle( + baselineShift = BaselineShift(0.1f), + ), + ) { append(" | ") }presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt (3)
22-33: 중앙 정렬 가독성 개선: textAlign/width 지정 권장Column 정중앙 정렬만으로는 멀티라인 시 시각적 중심이 어긋날 수 있습니다. 각 Text에 textAlign = Center 및 fillMaxWidth를 부여해 중앙 정렬을 보장하는 것을 권장합니다.
Text( text = "해당 난이도 루틴이 없어요", color = BitnagilTheme.colors.coolGray30, style = BitnagilTheme.typography.subtitle1SemiBold.copy( lineHeightStyle = LineHeightStyle( alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.None, ), ), - modifier = Modifier.padding(bottom = 2.dp), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 2.dp), + textAlign = TextAlign.Center, ) @@ Text( text = "다른 난이도를 살펴보거나 루틴을 추가해 보세요.", color = BitnagilTheme.colors.coolGray70, style = BitnagilTheme.typography.body2Regular.copy( lineHeightStyle = LineHeightStyle( alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.None, ), ), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, )참고: 위 변경에는 import 추가가 필요합니다.
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.ui.text.style.TextAlignAlso applies to: 34-43
23-23: 하드코딩된 문구를 stringResource로 이전하세요한글 고정 문자열을 string 리소스로 옮기면 다국어/카피 변경에 유연합니다. 예) stringResource(R.string.empty_recommend_primary), stringResource(R.string.empty_recommend_secondary)
Also applies to: 35-35
19-21: 175.dp 매직 넘버 대신 디자인 토큰/리소스 사용 권장고정 top padding(175.dp)은 기기/폰트 스케일에 따라 레이아웃 균형이 깨질 수 있습니다. Spacer + weight, verticalArrangement, 또는 dimen 리소스/테마 스페이싱 토큰을 사용하는 방식을 고려해 주세요.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (3)
58-67: CTA 터치 타겟이 38dp로 권고 기준(>=48dp)에 미달합니다Material/Android 접근성 가이드에서 최소 48dp 터치 타겟을 권장합니다. 높이를 48dp 이상으로 상향해 주세요.
- .height(38.dp) + .height(48.dp)대안: 고정 height 대신
.heightIn(min = 48.dp)로 유연하게 보장하는 방법도 있습니다.
50-54: 하드코딩된 문구를 stringResource로 이전하세요"내 기분에 맞는 루틴 추천받기", "추천받기"는 string 리소스로 옮기면 i18n/카피 변경에 유연합니다.
Also applies to: 70-73
65-65: 리플 피드백 부재 — 클릭 시 시각적 피드백 고려clickableWithoutRipple은 미려하지만 피드백 부재로 체감 반응성이 떨어질 수 있습니다. 디자인 허용 시 ripple 또는 pressed state 변화를 도입하는 것을 권장합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt (1)
9-10: 기본 id 값 충돌 방지 제안: -1 사용
현재 코드에서RecommendSubRoutineUiModel은
id = 1(예시)id = this.id(toUiModel 변환)
등으로만 호출되고, 기본값0이 실제로 사용되거나id == 0분기 로직도 없습니다.
그럼에도 placeholder 용도의 기본값은 서버에서 유효할 수 없는 값인-1로 두는 편이 안전합니다.적용 예시:
- val id: Int = 0, + val id: Int = -1,data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt (1)
21-25: 신규 필드 recommendedRoutineType 역직렬화 안정성 보강 권장서버/클라이언트 릴리스 타이밍 차이, 캐시된 응답, A/B 실험 등으로 필드가 누락되거나 알 수 없는 값이 올 수 있습니다. 현재는 필수(String) + 직접 변환으로 실패 시 크래시 가능성이 있습니다. 역직렬화 기본값과 매핑 시 안전한 폴백을 두는 것을 권장합니다.
적용 예시:
@Serializable data class RecommendedRoutineDto( @@ - @SerialName("recommendedRoutineType") - val recommendedRoutineType: String, + @SerialName("recommendedRoutineType") + val recommendedRoutineType: String? = null, @@ fun RecommendedRoutineDto.toDomain(): RecommendRoutine = RecommendRoutine( @@ - recommendedRoutineType = RecommendCategory.fromString(recommendedRoutineType), + // 서버에서 누락되거나 미지의 값일 경우 기본 카테고리로 폴백 + recommendedRoutineType = recommendedRoutineType + ?.let { runCatching { RecommendCategory.fromString(it) }.getOrNull() } + ?: RecommendCategory.PERSONALIZED, recommendSubRoutines = recommendedSubRoutineSearchResult.map { it.toDomain() }, )
- Domain의 RecommendCategory에 Unknown/기본값이 존재하는지, fromString이 예외를 던지는지 확인 부탁드립니다. Unknown이 있다면 위 폴백을 Unknown으로 교체하는 편이 더 명확합니다.
Also applies to: 34-36
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (2)
88-100: 하드코딩된 문자열은 string 리소스로 이동 권장 ("세부 루틴", 불릿 라인)국제화/번역/일관된 카피 관리를 위해 하드코딩 문구는 string 리소스로 추출하는 것을 권장합니다. 불릿 접두사("• ")도 포맷 문자열로 관리하면 좋습니다.
예:
- strings.xml
- key: recommend_detail_title → "세부 루틴"
- key: recommend_detail_item → "• %1$s"
- 코드:
- text = stringResource(R.string.recommend_detail_title)
- text = stringResource(R.string.recommend_detail_item, subRoutine.name)
69-73: 플로팅 추가 아이콘 접근성 고려카드 전체가 클릭 가능한 지금 구조라도, 보조기기 사용자에게는 명시적인 역할 노출이 도움됩니다. Plus 아이콘에 contentDescription 또는 Semantics를 부여해 “루틴 추가” 역할을 노출하는 것을 고려해 주세요.
예:
- Modifier.semantics { contentDescription = "루틴 추가" }
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (2)
141-144: 헤더 카피 가독성 및 i18n 개선 제안"추천 루틴리스트"는 띄어쓰기 없이 붙어 가독성이 떨어집니다. "추천 루틴 리스트" 또는 “추천 루틴 목록” 등으로 수정하고 string 리소스로 이동하는 것을 권장합니다.
적용 예시(텍스트만 수정):
- text = "추천 루틴리스트", + text = "추천 루틴 리스트",추가로 strings.xml로 추출하여 stringResource 사용을 추천합니다.
186-196: TODO: 추천 카테고리 함께 전달 미구현 — 파라미터 확장 제안등록 화면으로 네비게이션 시 routine.id만 전달 중이며, PR 설명의 미구현 항목(추천타입 전달)이 남아있습니다. onRegisterRoutineClick 시그니처를 확장하거나, NavArgs에 recommendedRoutineType을 포함하는 방향을 제안합니다.
예시(호출부 기준):
- onRegisterRoutineClick(routine.id.toString()) + onRegisterRoutineClick( + routine.id.toString(), + routine.recommendedRoutineType // NavArgs/모델에 맞게 직렬화 형태 결정 + )필요하시면 네비게이션 인자/라우트 정의까지 변경안 작성해 드리겠습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (2)
13-14: Compose 재구성 효율 향상을 위해 @immutable 추가 제안UI 모델이 불변 데이터로 쓰이는 만큼 @immutable을 부여하면 Compose가 변경 추적을 더 효율적으로 수행할 수 있습니다.
다음 변경을 제안합니다:
-@Parcelize -data class RecommendRoutineUiModel( +@Parcelize +@Immutable +data class RecommendRoutineUiModel(추가 import (선택):
import androidx.compose.runtime.Immutable
15-26: 카테고리 기본값을 UNKNOWN으로 고정해 널 분기/폴백 제거 (+ @DrawableRes 명시)도메인에서
recommendedRoutineType이 비널로 전달된다면 UI 모델도 기본값을UNKNOWN으로 두고, 아이콘/컬러에서 널 안전 연산자와 폴백을 제거하는 편이 명확합니다. 또한icon에는@DrawableRes를 붙여 타입 안정성을 높이는 것을 권장합니다.- val recommendedRoutineType: RecommendCategory? = null, + val recommendedRoutineType: RecommendCategory = RecommendCategory.UNKNOWN, @@ - val icon: Int - get() = recommendedRoutineType?.getIcon() ?: R.drawable.ic_shine + @DrawableRes + val icon: Int + get() = recommendedRoutineType.getIcon() @@ - val color: Color - @Composable get() = recommendedRoutineType?.getColor() ?: BitnagilTheme.colors.yellow10 + val color: Color + @Composable get() = recommendedRoutineType.getColor()아이콘 어노테이션용 import:
import androidx.annotation.DrawableRes참고:
level도 도메인에서 항상 존재한다면 비널로 유지할 필요가 없을 수 있습니다. 도메인 보장 여부에 따라 비널 → 널 제거도 고려해 주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (10)
core/designsystem/src/main/res/drawable-hdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-hdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-mdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-mdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xhdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xhdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxhdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxhdpi/default_marble.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxxhdpi/default_ball.pngis excluded by!**/*.pngcore/designsystem/src/main/res/drawable-xxxhdpi/default_marble.pngis excluded by!**/*.png
📒 Files selected for processing (12)
data/src/main/java/com/threegap/bitnagil/data/recommendroutine/model/response/RecommendedRoutineDto.kt(3 hunks)domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt(1 hunks)domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutine.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt(7 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/atom/RecommendCategoryChip.kt(2 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt(2 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt(3 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendSubRoutineUiModel.kt(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (1)
EmotionRecommendRoutineButton(24-76)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt (1)
EmptyRecommendRoutineView(14-45)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (1)
RecommendRoutineItem(28-104)
🔇 Additional comments (17)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/atom/RecommendCategoryChip.kt (1)
7-7: height import 추가 적절고정 높이 적용을 위한 import 추가는 변경 의도와 일치합니다.
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt (1)
12-16: 복사체계/UX 카피 확인 필요: "하/중/상" 표기가 최신 디자인/카피 가이드와 일치하는지 확인해 주세요레벨 표기 방식(예: "하/중/상" vs "낮음/보통/높음")은 디자인/카피 팀 기준에 따라 변경 가능성이 큽니다. 도메인에 고정 문자열이 있으면 변경 반영이 어려우니, 현행 표기가 맞는지 확인 부탁드립니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
127-129: 선택 표시 아이콘 교체 LGTMic_check_md로의 교체와 테마 색상 사용이 일관되고 명확합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (2)
29-41: Row 전체 클릭 → CTA만 클릭으로 변경된 상호작용 의도 확인 필요컴포넌트 좌측(이미지/문구) 터치는 반응하지 않고, 우측 Pill만 클릭 가능합니다. 기존 대비 상호작용 범위 축소가 의도된 리디자인인지 확인 부탁드립니다.
Also applies to: 58-67
42-48: 이미지 접근성 확인: contentDescription = null 적절성순수 장식용이라면 null이 맞습니다. 만약 의미가 있다면 적절한 contentDescription을 제공해야 합니다.
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendRoutine.kt (2)
9-11: 필드 추가 자체는 LGTMrecommendedRoutineType 도입으로 카테고리 표현이 명확해졌습니다.
9-11: 생성자 변경 영향 점검 및 매핑 로직 검증 결과
RecommendedRoutineDto.toDomain()및RecommendRoutinesDto.toDomain()에서
· 신규 필드(recommendedRoutineType,recommendSubRoutines) 모두 반영 완료RecommendCategory.fromString구현부(entries.find { … } ?: UNKNOWN)로 UNKNOWN fallback 처리 확인app/src/.../navigation/home/HomeRoute.kt의RecommendRoutine(...)호출은 패키지 임포트(…)로 보아 도메인 모델이 아닌 네비게이션 전용 로컬 클래스일 가능성이 큽니다.앱 전체에서 도메인
RecommendRoutine생성자가 호출되는 곳이 DTO 매핑부 외에 없는지, 그리고 HomeRoute 측 호출이 실제 도메인 클래스가 아닌지 최종 확인 부탁드립니다.presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineState.kt (2)
18-20: Emotion 버튼 노출 조건 로직 적절합니다.PERSONALIZED 카테고리이면서 감정 선택 전(emotionMarbleType == null)일 때만 노출하는 의도가 코드로 명확하게 반영되었습니다.
9-17: 불필요한 Parcelable 선언 요청 무시
MviState 인터페이스가 이미 Parcelable을 상속하고 있어, RecommendRoutineState는: MviState만으로도 Parcelable을 구현합니다.@Parcelize어노테이션만으로 정상 동작하므로 별도Parcelable상속 선언은 필요하지 않습니다.Likely an incorrect or invalid review comment.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (2)
28-43: 아이템 컴포넌트 API 단일화 및 카드 인터랙션 정리 좋습니다routine 단일 모델로 Props를 수렴하고 카드 전체 클릭으로 액션을 일원화한 점이 명확하고 유지보수에 유리합니다. 배경/라운딩/패딩도 디자인 가이드를 잘 따르고 있습니다.
49-58: 아이콘 틴트 null 처리 + 배경색 배지 구현 적절BitnagilIcon의 기본 틴트를 무력화(null)하고 배경색을 별도 레이어로 주어 아이콘 본연의 색상을 유지하는 접근이 맞습니다. 재사용성도 좋습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (3)
85-91: 카테고리 변경 시 리스트 최상단 스크롤 처리 적절rememberLazyListState + LaunchedEffect로 포지션 리셋을 부드럽게 처리한 구현이 깔끔합니다. 불필요한 애니메이션 방지를 위한 가드도 좋습니다.
121-128: Emotion 추천 버튼의 스크린 상단 배치 전환 적합uiState.shouldShowEmotionButton으로 상단에서 조건부 노출하는 변경이 사용자 시선 흐름에 더 자연스럽습니다. spacing도 적절합니다.
173-177: 난이도 선택 후 빈 상태(Empty) 노출 로직 합리적선택된 난이도가 있고 결과가 비었을 때만 Empty 뷰를 노출하는 조건이 UX에 부합합니다. 빈 상태 컴포넌트도 톤/타이포가 일관적입니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/model/RecommendRoutineUiModel.kt (3)
4-8: Compose/디자인시스템 의존 추가 적절프레젠테이션(UI) 모델에서 Color/Theme 및 디자인 리소스(R) 의존을 갖는 방향은 이번 리디자인 목적과 일치하며, 책임 범위도 적절해 보입니다.
28-35: toUiModel 매핑 적절도메인 모델의
recommendedRoutineType과 서브 루틴 매핑이 일관되게 반영되어 있습니다.
37-60: 아이콘 리소스 존재 확인 완료
모든 기대 아이콘(ic_outside,ic_wakeup,ic_connect,ic_rest,ic_grow,ic_shine)이 정상적으로 존재함을 확인했습니다.컬러 스펙 확인 요청
OUTING_REPORT,PERSONALIZED,UNKNOWN이 모두BitnagilTheme.colors.yellow10으로 매핑되어 있습니다. 디자인 스펙 상 해당 세 카테고리에 동일한 색상을 사용하는 것이 맞는지 검토 부탁드립니다.
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt
Outdated
Show resolved
Hide resolved
...eegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (6)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (3)
141-144: 문구 니트픽 — ‘추천 루틴리스트’ 띄어쓰기 권장가독성을 위해 ‘추천 루틴 리스트’ 또는 ‘추천 루틴 목록’으로의 변경을 제안드립니다. 디자인 카피 가이드에 맞춰 선택해 주세요.
- Text( - text = "추천 루틴리스트", + Text( + text = "추천 루틴 목록", color = BitnagilTheme.colors.coolGray60, style = BitnagilTheme.typography.body2SemiBold,
155-157: 하드코딩 문자열 리소스화(i18n) 권장"난이도", "선택" 등 문자열은 string 리소스로 분리하면 다국어 대응과 카피 변경에 유리합니다. 아래 영역은
stringResource(...)로의 치환을 고려해 주세요.Also applies to: 163-163
191-195: TODO: 추천 카테고리 함께 전달 — 이벤트 시그니처 확장 제안주석대로 등록 시 추천 카테고리(또는 타입)를 함께 넘기면 이후 화면에서의 컨텍스트 유지가 쉬워집니다. 예:
(id: String, category: RecommendCategory)또는 DTO/parcelable 모델로 묶어 전달.원하시면 이벤트/네비게이션 시그니처 확장에 맞춘 전반 수정안까지 생성해 드릴게요.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (3)
32-33: BottomSheet 부분 확장 상태 방지 옵션 고려절반(expanded/partiallyExpanded) 상태를 사용하지 않을 계획이라면 부분 확장을 막아 의도치 않은 중간 상태를 방지할 수 있습니다.
- val sheetState = rememberModalBottomSheetState() + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true, + )
92-101: 문자열 하드코딩 분리 및 접근성 개선 제안
- "난이도", 구분자
" | ",optionText는 string 리소스로 분리 권장(i18n/카피 변경 용이).- 스크린 리더(TalkBack) 접근성: 시각 구분자
" | "대신 두 텍스트를 의미 단위로 읽히도록semantics { }로 문장을 합치거나, 단일Text에 포맷팅된 문자열을 전달하는 쪽이 더 자연스럽습니다.필수는 아니나 장기 유지보수/접근성 품질을 위해 고려 부탁드립니다.
104-109: 체크 아이콘 터치 타깃/접근성 니트픽선택 아이콘이 작다면
.size(24.dp)정도로 명시해 터치 타깃을 보장하고, 필요 시semantics { contentDescription = ... }로 보조 기술 사용자에게 상태를 전달하는 것도 고려해 주세요. (장식 아이콘이라면 현 상태도 무방)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt(7 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/EmotionRecommendRoutineButton.kt (1)
EmotionRecommendRoutineButton(24-76)core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (1)
BitnagilIcon(21-33)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/EmptyRecommendRoutineView.kt (1)
EmptyRecommendRoutineView(14-45)presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/block/RecommendRoutineItem.kt (1)
RecommendRoutineItem(28-104)
⏰ 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: build
🔇 Additional comments (5)
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/RecommendRoutineScreen.kt (3)
85-91: 카테고리 변경 시 자동 스크롤 처리: UX 관점에서 적절합니다선택 카테고리 변경 시 리스트를 최상단으로 스무스하게 이동시키는 처리 깔끔합니다. 불필요한 재컴포지션도 없고, 사용성 측면에서 긍정적이에요.
121-129: Emotion 추천 버튼 위치/노출 제어 리팩터링 좋습니다상단 게이트(
uiState.shouldShowEmotionButton)로 통일된 위치에서 노출을 관리하는 방향이 구조적이며, 이후 리스트 내부 렌더링과의 결합도 줄여 유지보수성이 좋아졌습니다.
189-189: items key 안정성 확보: LGTMid 기반 key로 안정성 확보되어 스크롤/애니메이션 측면에서 안전합니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/recommendroutine/component/template/RecommendLevelBottomSheet.kt (1)
54-63: 선택 토글 + hide 후 onDismiss 호출 패턴: 적절합니다같은 항목 재클릭 시 해제 처리 및
hide()완료 후onDismiss()호출 플로우가 안정적입니다. 사용자 인지/상태 일관성 측면에서 👍domain/src/main/java/com/threegap/bitnagil/domain/recommendroutine/model/RecommendLevel.kt (1)
5-10: koreanLevel 도입으로 UI 표기 일관성 확보: 좋습니다UI에서 직접 사용 가능한
koreanLevel추가와 각 상수 값 보강이 명확합니다. 프리젠테이션 레이어와의 결합도가 줄고 매핑도 간결해집니다.
[ PR Content ]
추천 루틴 화면 리디자인 변경사항을 반영했습니다.
Related issue
Screenshot 📸
Screen_recording_20250819_154139.mp4
Work Description
To Reviewers 📢
미구현 사항(미구현 사항들은 화면이 연결되면서 추가적인 작업을 통해 구현해보겠습니다!)
추천루틴 화면은 기능적인 부분이 변경된 곳은 없고, 디자인 변경으로 인한 자잘하게 수정된 사항들이 대부분입니다요!
추가적인 궁금증, 틀린부분 등등 리뷰로 남겨주세욤
Summary by CodeRabbit
신기능
스타일
기타