Conversation
개요온보딩 화면 UI 구현이 추가되었습니다. Jetpack Compose를 사용하여 2페이지 HorizontalPager 기반 온보딩 플로우, ViewModel 계층, 커스텀 컴포저블(UnderlineUntilWord, MovingCherry, PagerIndicator), 지원 드로어블 리소스 5개, 그리고 ConstraintLayout 라이브러리 의존성이 추가되었습니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant User as 사용자
participant OnboardingRoute as OnboardingRoute
participant OnboardingScreen as OnboardingScreen
participant HorizontalPager as HorizontalPager
participant ViewModel as OnboardingViewModel
User->>OnboardingRoute: 온보딩 화면 진입
OnboardingRoute->>ViewModel: hiltViewModel()로 주입
OnboardingRoute->>OnboardingScreen: ViewModel 전달
OnboardingScreen->>HorizontalPager: 2페이지 설정
Note over HorizontalPager: Page 0: OnboardingSection<br/>Page 1: Onboarding2Section
User->>HorizontalPager: 페이지 스와이프
HorizontalPager->>OnboardingScreen: 현재 페이지 업데이트
OnboardingScreen->>OnboardingScreen: PagerIndicator 업데이트
User->>OnboardingScreen: Next 버튼 클릭 (Page 1에서만 활성화)
OnboardingScreen->>ViewModel: onNextClick() 호출
ViewModel->>ViewModel: 다음 단계 처리
sequenceDiagram
participant Compose as Compose UI
participant MovingCherry as MovingCherry 컴포저블
participant Animation as 프레임 기반 애니메이션
participant DrawableRes as DrawableRes (img_onboarding_lv0-4)
Compose->>MovingCherry: Onboarding2Section에서 렌더링
MovingCherry->>Animation: 체리 이동 애니메이션 시작
Animation->>DrawableRes: 현재 프레임에 해당하는 드로어블 요청
DrawableRes->>Animation: img_onboarding_lv{index} 리소스 반환
Animation->>MovingCherry: 다음 프레임으로 진행
MovingCherry->>Compose: 애니메이션 프레임 표시
Note over Animation: 프레임 루프 반복
코드 리뷰 난이도 평가🎯 3 (Moderate) | ⏱️ ~20 분 관련된 Pull Request
제안 검수자
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/cherrish/android/core/designsystem/theme/Color.kt`:
- Around line 228-237: Replace the incorrect use of CherrishTheme.colors.shadow
for the two preview Text composables so they render the actual gradient endpoint
colors: change the first Text's color argument to CherrishTheme.colors.graStart
and the second to CherrishTheme.colors.graEnd (keep style
CherrishTheme.typography.body1M14 unchanged); also ensure the preview background
is a dark surface if needed so the near-white graStart/graEnd colors are
visible.
In
`@app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt`:
- Around line 60-68: The OnboardingRoute composable currently calls viewModel()
which won't provide Hilt-injected dependencies for OnboardingViewModel; replace
the viewModel() call with hiltViewModel() so the OnboardingViewModel receives
Hilt injections, i.e., obtain the ViewModel inside OnboardingRoute using
hiltViewModel() and pass its onCancelClick/onNextClick handlers to
OnboardingScreen (symbols: OnboardingRoute, OnboardingViewModel,
OnboardingScreen, viewModel(), hiltViewModel()).
- Around line 132-147: The CherrishButton remains clickable even when visually
hidden because graphicsLayer { alpha = 0f } only affects visuals; update the
button usage (CherrishButton) to set its enabled parameter based on the same
condition (pagerState.currentPage == 1) instead of relying solely on the onClick
guard, and remove/keep graphicsLayer for animation only; ensure onNextClick is
only invoked when enabled is true to fix accessibility and interaction
correctness.
- Around line 271-278: The parameter insetPx: Dp is being shadowed by a local
val insetPx, so rename the local density-converted variable (e.g., to insetPxPx
or insetPxPxFloat) to avoid shadowing the parameter used by the composable;
update all references that currently use the local insetPx to the new name
inside the OnboardingScreen composable (and keep the existing conversions for
thicknessPx and yOffsetPx consistent).
In `@gradle/libs.versions.toml`:
- Line 51: Update the constraintlayout entry in libs.versions.toml to the latest
stable Compose-compatible release by changing the constraintlayout version from
"1.0.1" to "1.1.1"; locate the constraintlayout key in the file and replace its
value, then refresh Gradle (e.g., sync/refresh dependencies) to ensure the new
version is applied.
🧹 Nitpick comments (6)
app/src/main/java/com/cherrish/android/core/designsystem/theme/Color.kt (1)
40-41: 네이밍 개선 권장
graStart와graEnd의 "gra" 접두사가 의미가 불분명합니다. 그라데이션(gradient)을 의미한다면gradientStart,gradientEnd또는onboardingGradientStart,onboardingGradientEnd처럼 더 명확한 이름을 사용하는 것이 좋습니다.app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingViewModel.kt (1)
7-15: UiState 및 상태 관리 누락온보딩 화면의 상태 관리를 위한 UiState와 StateFlow가 없습니다. 현재 플레이스홀더 메서드만 존재하며 실제 구현이 필요합니다.
단방향 데이터 흐름 패턴을 위해 다음 요소들을 고려해 주세요:
- 온보딩 현재 페이지/단계 상태
- 네비게이션 이벤트 (취소/다음 버튼 클릭 시)
- UI 상태를 담는 sealed class 또는 data class
UiState 패턴을 적용한 ViewModel 구조를 생성해 드릴까요?
gradle/libs.versions.toml (1)
104-108: 라이브러리 섹션 위치가 부적절합니다.
androidx-constraintlayout은 UI 라이브러리인데# Testing주석 아래에 배치되어 있습니다.# UI & Animation섹션으로 이동하는 것이 좋겠습니다.♻️ 권장 수정
# UI & Animation섹션(line 98-102) 아래로 이동:# UI & Animation coil-bom = { group = "io.coil-kt.coil3", name = "coil-bom", version.ref = "coil" } coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose" } coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp" } lottie = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottieCompose" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constraintlayout" } # Testing junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } -androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constraintlayout" }app/build.gradle.kts (1)
83-85: 의존성 배치 위치를 개선하면 좋겠습니다.
constraintlayout은 UI 라이브러리인데 Hilt 의존성 사이에 배치되어 있습니다. 다른 UI 관련 의존성들(navigation, lottie 등)과 함께 그룹화하면 가독성이 향상됩니다.♻️ 권장 수정
implementation(libs.bundles.hilt) - implementation(libs.androidx.constraintlayout) ksp(libs.hilt.compiler) implementation(platform(libs.coil.bom)) implementation(libs.bundles.coil) implementation(libs.androidx.navigation) + implementation(libs.androidx.constraintlayout) implementation(libs.androidx.datastore.preferences)app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt (2)
296-298:gap계산을drawBehind밖으로 이동하세요.
gap이 매 드로우 호출마다 재계산됩니다.thicknessPx,insetPx처럼drawBehind블록 밖에서 미리 계산하면 불필요한 연산을 줄일 수 있습니다.♻️ 권장 수정
val thicknessPx = with(density) { underlineThickness.toPx() } val insetPx = with(density) { inset.toPx() } val yOffsetPx = with(density) { underlineYOffset.toPx() } + val gapPx = with(density) { 3.dp.toPx() } // ... modifier = modifier.drawBehind { val layout = layoutResult ?: return@drawBehind if (endExclusive <= 0) return@drawBehind val line = layout.getLineForOffset(endExclusive - 1) val end = layout.getHorizontalPosition(endExclusive, usePrimaryDirection = true) val y = layout.getLineBottom(line) + yOffsetPx - val gap = with(density) { 3.dp.toPx() } drawLine( // ... - end = Offset(x = 0f, y = y + gap), + end = Offset(x = 0f, y = y + gapPx), // ... ) }
368-368:weight(62f)값이 임의적입니다.일반적으로
weight(1f)를 사용하여 남은 공간을 채웁니다. 다른 요소들과 비율 조정이 필요한 경우가 아니라면weight(1f)로 충분합니다.♻️ 권장 수정
- Spacer(modifier = Modifier.weight(62f)) + Spacer(modifier = Modifier.weight(1f))
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt`:
- Around line 99-106: OnboardingScreen currently renders the close Icon with
contentDescription = null which hides it from screen readers; update the Icon
(ImageVector.vectorResource id = R.drawable.ic_close) to provide an
accessibility label (e.g., use a string resource like R.string.close or a
localized "닫기") and pass it to contentDescription, keeping the existing modifier
and onCancelClick handler so the button remains functional and tappable for
accessibility services.
| Icon( | ||
| imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), | ||
| contentDescription = null, | ||
| tint = CherrishTheme.colors.gray600, | ||
| modifier = Modifier | ||
| .align(Alignment.End) | ||
| .noRippleClickable(onClick = onCancelClick) | ||
| .padding(end = 24.dp) |
There was a problem hiding this comment.
닫기 아이콘에 접근성 라벨 추가 필요
액션 버튼인데 contentDescription = null이라 스크린리더에서 의미가 사라집니다. 간단한 라벨만 추가해 주세요.
🐛 수정 제안
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close),
- contentDescription = null,
+ contentDescription = "닫기",
tint = CherrishTheme.colors.gray600,
modifier = Modifier
.align(Alignment.End)
.noRippleClickable(onClick = onCancelClick)
.padding(end = 24.dp)
)📝 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.
| Icon( | |
| imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), | |
| contentDescription = null, | |
| tint = CherrishTheme.colors.gray600, | |
| modifier = Modifier | |
| .align(Alignment.End) | |
| .noRippleClickable(onClick = onCancelClick) | |
| .padding(end = 24.dp) | |
| Icon( | |
| imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), | |
| contentDescription = "닫기", | |
| tint = CherrishTheme.colors.gray600, | |
| modifier = Modifier | |
| .align(Alignment.End) | |
| .noRippleClickable(onClick = onCancelClick) | |
| .padding(end = 24.dp) | |
| ) |
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt`
around lines 99 - 106, OnboardingScreen currently renders the close Icon with
contentDescription = null which hides it from screen readers; update the Icon
(ImageVector.vectorResource id = R.drawable.ic_close) to provide an
accessibility label (e.g., use a string resource like R.string.close or a
localized "닫기") and pass it to contentDescription, keeping the existing modifier
and onCancelClick handler so the button remains functional and tappable for
accessibility services.
| id = if (isSelected) { | ||
| R.drawable.ic_indicator | ||
| } else { | ||
| R.drawable.ic_unindicator |
There was a problem hiding this comment.
P1 : 이거 아이콘 크기 같은거 똑같은거 아닝가요 ?????? 하나로 쓰면 안대?!? 색만 바꿔주고 ?!
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt`:
- Around line 95-119: The HorizontalPager inside Column is using
Modifier.fillMaxSize(), causing it to overlap top Spacer/Icon; change the
pager's modifier to use weight(1f) and fillMaxWidth() so it only takes remaining
vertical space (update the Modifier on HorizontalPager that uses pagerState and
hosts OnboardingSection/Onboarding2Section accordingly).
♻️ Duplicate comments (1)
app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt (1)
100-107: 닫기 아이콘에 접근성 라벨이 필요합니다.
액션 아이콘이라 스크린리더 라벨을 제공하는 게 맞습니다.🔧 수정 제안
Icon( imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), - contentDescription = null, + contentDescription = "닫기", tint = CherrishTheme.colors.gray600, modifier = Modifier .align(Alignment.End)
| Column( | ||
| modifier = Modifier.fillMaxSize() | ||
| ) { | ||
| Spacer(modifier = Modifier.height(54.dp)) | ||
|
|
||
| Icon( | ||
| imageVector = ImageVector.vectorResource(id = R.drawable.ic_close), | ||
| contentDescription = null, | ||
| tint = CherrishTheme.colors.gray600, | ||
| modifier = Modifier | ||
| .align(Alignment.End) | ||
| .noRippleClickable(onClick = onCancelClick) | ||
| .padding(end = 24.dp) | ||
| ) | ||
|
|
||
| HorizontalPager( | ||
| state = pagerState, | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| ) { page -> | ||
| when (page) { | ||
| 0 -> OnboardingSection() | ||
| 1 -> Onboarding2Section() | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "OnboardingScreen.kt" -type fRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 158
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt | sed -n '90,125p'Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 1415
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt | sed -n '70,100p'Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 1102
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt | sed -n '115,140p'Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 1035
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt | head -1 && wc -l ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 222
🏁 Script executed:
grep -rn "OnboardingSection\|Onboarding2Section" ./app/src/main/java/com/cherrish/android/presentation/onboarding/ --include="*.kt" | head -20Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 583
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt | sed -n '152,170p'Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 711
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt | sed -n '318,340p'Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 866
HorizontalPager가 상단 요소들과 함께 레이아웃을 초과할 수 있습니다.
Column 내에서 HorizontalPager에 fillMaxSize()를 적용하면 상단 Spacer(54.dp)와 Icon의 높이를 고려하지 않고 전체 Column 크기를 차지하려고 시도합니다. 이로 인해 남은 공간만 차지하도록 weight(1f)과 fillMaxWidth()를 적용해야 합니다.
🔧 수정 제안
HorizontalPager(
state = pagerState,
modifier = Modifier
- .fillMaxSize()
+ .weight(1f)
+ .fillMaxWidth()
) { page ->🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/onboarding/OnboardingScreen.kt`
around lines 95 - 119, The HorizontalPager inside Column is using
Modifier.fillMaxSize(), causing it to overlap top Spacer/Icon; change the
pager's modifier to use weight(1f) and fillMaxWidth() so it only takes remaining
vertical space (update the Modifier on HorizontalPager that uses pagerState and
hosts OnboardingSection/Onboarding2Section accordingly).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/core/designsystem/component/type/CherrishGaugeType.kt`:
- Around line 11-14: ChallengeSection.kt에서 기본 이미지 리소스와 프리뷰들을 새 온보딩 에셋으로 일관되게
전환하세요: Change the function parameter default that currently uses
R.drawable.img_lv1 to either R.drawable.img_onboarding_lv1 (or remove the
default) in the ChallengeSection composable signature, and update all Preview
functions that pass img_lv1/img_lv2 to use
R.drawable.img_onboarding_lv1..img_onboarding_lv4 accordingly; also verify
whether R.drawable.img_lv1/img_lv2 still exist and if they were removed update
any remaining references or delete unused resources.
🧹 Nitpick comments (1)
app/src/main/java/com/cherrish/android/presentation/onboarding/model/OnboardingCherryType.kt (1)
6-15:index프로퍼티가ordinal과 중복될 수 있습니다.현재
index값이 enum의ordinal값과 동일합니다.ordinal을 직접 사용하거나, 향후 값이 달라질 가능성이 없다면index프로퍼티 제거를 고려할 수 있습니다.♻️ 대안 (선택 사항)
index가 항상 순서와 동일하다면:enum class OnboardingCherryType( - `@DrawableRes` val image: Int, - val index: Int + `@DrawableRes` val image: Int ) { - LV0(image = R.drawable.img_onboarding_lv0, index = 0), - LV1(image = R.drawable.img_onboarding_lv1, index = 1), - LV2(image = R.drawable.img_onboarding_lv2, index = 2), - LV3(image = R.drawable.img_onboarding_lv3, index = 3), - LV4(image = R.drawable.img_onboarding_lv4, index = 4) + LV0(image = R.drawable.img_onboarding_lv0), + LV1(image = R.drawable.img_onboarding_lv1), + LV2(image = R.drawable.img_onboarding_lv2), + LV3(image = R.drawable.img_onboarding_lv3), + LV4(image = R.drawable.img_onboarding_lv4) }사용 시:
OnboardingCherryType.LV0.ordinal(결과: 0)단, 명시적인
index프로퍼티가 코드 가독성이나 향후 확장성을 위해 의도된 것이라면 현재 구현을 유지해도 됩니다.
| LEVEL1(step = 1, percent = 25, image = R.drawable.img_onboarding_lv1), | ||
| LEVEL2(step = 2, percent = 50, image = R.drawable.img_onboarding_lv2), | ||
| LEVEL3(step = 3, percent = 75, image = R.drawable.img_onboarding_lv3), | ||
| LEVEL4(step = 4, percent = 100, image = R.drawable.img_onboarding_lv4) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find and examine the CherrishGaugeType.kt file
find . -name "CherrishGaugeType.kt" -type fRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 168
🏁 Script executed:
# Search for old img_lv* references in the codebase
rg "img_lv[1-4]" --type=kotlin --type=xml -g '!**/img_onboarding_lv*'Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 473
🏁 Script executed:
# Also search more broadly for any references to img_lv
rg "img_lv" --type=kotlin --type=xmlRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 473
🏁 Script executed:
cat -n ./app/src/main/java/com/cherrish/android/core/designsystem/component/type/CherrishGaugeType.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 720
🏁 Script executed:
rg -A 5 -B 5 "img_lv1|img_lv2" app/src/main/java/com/cherrish/android/presentation/home/component/ChallengeSection.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 1152
이미지 리소스 참조 마이그레이션 미완료 - ChallengeSection.kt 수정 필요
CherrishGaugeType.kt의 이미지 참조는 새로운 온보딩 에셋(img_onboarding_lv1 ~ img_onboarding_lv4)으로 업데이트되었지만, 동일한 리소스를 사용하는 ChallengeSection.kt에서는 여전히 기존 이미지(img_lv1, img_lv2)를 참조하고 있습니다.
ChallengeSection.kt에서 다음을 수정하세요:
- 함수 매개변수 기본값:
R.drawable.img_lv1→ 새 리소스로 변경 또는 제거 - Preview 함수들:
img_lv1,img_lv2→ 일관된 리소스로 업데이트
기존 img_lv1, img_lv2 리소스가 프로젝트에 존재하는지, 아니면 완전히 제거되어야 하는지 확인하세요.
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/core/designsystem/component/type/CherrishGaugeType.kt`
around lines 11 - 14, ChallengeSection.kt에서 기본 이미지 리소스와 프리뷰들을 새 온보딩 에셋으로 일관되게
전환하세요: Change the function parameter default that currently uses
R.drawable.img_lv1 to either R.drawable.img_onboarding_lv1 (or remove the
default) in the ChallengeSection composable signature, and update all Preview
functions that pass img_lv1/img_lv2 to use
R.drawable.img_onboarding_lv1..img_onboarding_lv4 accordingly; also verify
whether R.drawable.img_lv1/img_lv2 still exist and if they were removed update
any remaining references or delete unused resources.



Related issue 🛠
Work Description ✏️
Screenshot 📸
2026-01-18.6.28.20.mov
Uncompleted Tasks 😅
To Reviewers 📢
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.