Conversation
- navOptions 클래스 추가 - navigationEvent 인터페이스 정의
- NavigationModule 의존성 모듈 추가
Walkthrough앱의 네비게이션 시스템을 구현하는 변경입니다. 새로운 core:navigation 모듈을 생성하여 Navigator 인터페이스, AppRoute, NavigationEvent를 정의하고, NavigatorImpl로 구현합니다. app 모듈에서는 MainActivity를 app/main 패키지로 이동하고 NavHost 기반 네비게이션을 도입합니다. feature/onboarding의 Splash, SignIn, SignUpProfile, SignUpPhoneAuth 화면들을 네비게이션에 연결합니다. Changes
Sequence DiagramsequenceDiagram
participant User
participant MainActivity as MainActivity<br/>(NavHost)
participant SplashScreen
participant SplashViewModel
participant Navigator as Navigator<br/>(NavigatorImpl)
participant SignInScreen
User->>MainActivity: Launch App
MainActivity->>SplashScreen: Display Splash
SplashScreen->>SplashViewModel: Load ViewModel
activate SplashViewModel
SplashViewModel->>SplashViewModel: Wait 2 seconds
SplashViewModel->>Navigator: navigate(AppRoute.SignIn,<br/>popUpTo=Splash,<br/>inclusive=true)
deactivate SplashViewModel
Navigator->>Navigator: Send NavigationEvent.Navigate
MainActivity->>MainActivity: Observe NavigationEvent
MainActivity->>SignInScreen: Navigate to SignIn
User->>SignInScreen: User interaction
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 여러 파일에 걸쳐 새로운 네비게이션 시스템을 추가하며, 모듈 구조 변경과 화면 연결이 포함됩니다. 대부분의 변경이 일관된 패턴(ViewModel 주입, 네비게이션 콜백)을 따르지만, 네비게이션 흐름의 정확성과 앱 진입점 변경을 검토해야 합니다. 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.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/auth/SignUpPhoneAuthScreen.kt (4)
63-96: 플레이스홀더 코드를 제거하거나 실제 로직으로 교체해야 합니다.전화번호 입력 필드에 다음과 같은 하드코딩된 값들이 있습니다:
isError = true로 고정errorMessage = "어쩌구"플레이스홀더 메시지- 인증 버튼의
onClick = {}빈 핸들러이는 개발 중인 임시 코드로 보이며, 실제 유효성 검증 로직과 인증 요청 기능이 구현되어야 합니다.
100-125: 인증번호 필드의 플레이스홀더 코드를 제거해야 합니다.인증번호 입력 필드도 마찬가지로:
isError = true로 고정errorMessage = "어쩌구"플레이스홀더 메시지실제 인증번호 검증 로직으로 교체가 필요합니다.
45-48: 네비게이션 핸들러가 구현되지 않았습니다.
MaTopAppBar의onBackClick이 빈 람다로 설정되어 있습니다. 뒤로가기 동작을 위해Navigator.navigateBack()또는 유사한 네비게이션 로직을 연결해야 합니다.ViewModel을 연결하고 네비게이션을 완성하는 것이 좋습니다.
ViewModel과 네비게이션을 연결하는 코드 작성을 도와드릴까요?
129-140: "다음" 버튼의 동작이 구현되지 않았습니다.버튼의
onClick핸들러가 비어있어 사용자가 다음 단계로 진행할 수 없습니다. ViewModel을 통해 다음 화면으로 네비게이션하는 로직을 구현해야 합니다.
🧹 Nitpick comments (3)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashViewModel.kt (1)
22-33: 스플래시 네비게이션 로직이 정확합니다.
popUpTo와inclusive = true옵션을 사용하여 백스택에서 Splash를 올바르게 제거합니다.선택적으로, 2초 지연 시간을 상수로 추출하는 것을 고려해보세요:
+ companion object { + private const val SPLASH_DELAY_MILLIS = 2000L + } + private fun navigateToSignIn() { viewModelScope.launch { - delay(2000L) + delay(SPLASH_DELAY_MILLIS) navigator.navigate(app/src/main/kotlin/com/moa/app/navigation/ObserveNavigationEvents.kt (1)
20-44: 네비게이션 이벤트 처리가 작동하지만, 더 관용적인 패턴을 고려하세요.현재 구현은
DisposableEffect와lifecycleScope를 사용하여 작동하지만, Compose에서는LaunchedEffect가 더 표준적입니다.다음과 같이 리팩토링을 고려해보세요:
- val lifecycleOwner = LocalLifecycleOwner.current - - DisposableEffect(lifecycleOwner, navController) { - val job = lifecycleOwner.lifecycleScope.launch { - lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.navigationEvents.collect { event -> - when (event) { - is NavigationEvent.NavigateBack -> navController.popBackStack() - is NavigationEvent.Navigate -> { - navController.navigate(event.route) { - event.options.popUpTo?.let { popUpToRoute -> - popUpTo(popUpToRoute) { - inclusive = event.options.inclusive - saveState = event.options.saveState - } - } - launchSingleTop = event.options.launchSingleTop - restoreState = event.options.restoreState - } - } - } + val lifecycleOwner = LocalLifecycleOwner.current + + LaunchedEffect(viewModel, navController) { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.navigationEvents.collect { event -> + when (event) { + is NavigationEvent.NavigateBack -> navController.popBackStack() + is NavigationEvent.Navigate -> { + navController.navigate(event.route) { + event.options.popUpTo?.let { popUpToRoute -> + popUpTo(popUpToRoute) { + inclusive = event.options.inclusive + saveState = event.options.saveState + } + } + launchSingleTop = event.options.launchSingleTop + restoreState = event.options.restoreState + } + } } } } - - onDispose { job.cancel() } }
LaunchedEffect는 자동으로 취소를 관리하며 Compose에서 권장되는 방식입니다.feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (1)
66-85: "이미 계정이 있어요" 버튼의 onClick 핸들러를 구현해주세요.두 번째 버튼의 onClick이 비어있어 아무 동작도 수행하지 않습니다. 기존 계정 로그인 플로우로 이동하는 네비게이션 로직이 필요합니다.
이 기능을 구현하는 코드를 생성하거나 추적을 위한 이슈를 생성하시겠습니까?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
app/build.gradle.kts(1 hunks)app/src/main/AndroidManifest.xml(1 hunks)app/src/main/kotlin/com/moa/app/MainActivity.kt(0 hunks)app/src/main/kotlin/com/moa/app/MoaApplication.kt(1 hunks)app/src/main/kotlin/com/moa/app/di/NavigationModule.kt(1 hunks)app/src/main/kotlin/com/moa/app/main/MainActivity.kt(1 hunks)app/src/main/kotlin/com/moa/app/main/MainViewModel.kt(1 hunks)app/src/main/kotlin/com/moa/app/navigation/NavigatorImpl.kt(1 hunks)app/src/main/kotlin/com/moa/app/navigation/ObserveNavigationEvents.kt(1 hunks)core/designsystem/src/main/res/drawable/img_moa_logo.xml(1 hunks)core/navigation/.gitignore(1 hunks)core/navigation/build.gradle.kts(1 hunks)core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt(1 hunks)core/navigation/src/main/java/com/moa/app/navigation/NavigationEvent.kt(1 hunks)core/navigation/src/main/java/com/moa/app/navigation/NavigationOptions.kt(1 hunks)core/navigation/src/main/java/com/moa/app/navigation/Navigator.kt(1 hunks)feature/onboarding/build.gradle.kts(1 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt(1 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInViewModel.kt(1 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/auth/SignUpPhoneAuthScreen.kt(1 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileScreen.kt(5 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileViewModel.kt(1 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt(1 hunks)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashViewModel.kt(1 hunks)gradle/libs.versions.toml(2 hunks)settings.gradle.kts(1 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/kotlin/com/moa/app/MainActivity.kt
🧰 Additional context used
🧬 Code graph analysis (12)
app/src/main/AndroidManifest.xml (1)
app/src/main/kotlin/com/moa/app/MainActivity.kt (1)
onCreate(17-33)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/auth/SignUpPhoneAuthScreen.kt (1)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (7)
{}(49-49)SignUpProfileScreen(29-32)MaTopAppBar(46-201)SignUpProfileScreenContent(34-202)Text(135-141)Text(57-200)Preview(204-208)
app/src/main/kotlin/com/moa/app/navigation/ObserveNavigationEvents.kt (1)
core/navigation/src/main/java/com/moa/app/navigation/NavigationOptions.kt (1)
popUpTo(12-22)
app/src/main/kotlin/com/moa/app/main/MainViewModel.kt (1)
app/src/main/kotlin/com/moa/app/MainActivity.kt (4)
onCreate(17-33)Scaffold(23-30)onCreate(19-32)MoATheme(22-31)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt (1)
app/src/main/kotlin/com/moa/app/MainActivity.kt (4)
onCreate(17-33)MoATheme(22-31)Scaffold(23-30)onCreate(19-32)
app/src/main/kotlin/com/moa/app/di/NavigationModule.kt (1)
build-logic/convention/src/main/kotlin/com/moa/app/plugin/AndroidHiltPlugin.kt (2)
apply(6-16)apply(7-15)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (1)
core/designsystem/src/main/kotlin/com/moa/app/designsystem/component/core/button/MaButton.kt (1)
MaButton(30-68)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInViewModel.kt (1)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (9)
{ }(188-188)SignUpProfileScreen(29-32){}(49-49)MaTopAppBar(46-201)SignUpProfileScreenContent(34-202)Preview(204-208)Text(135-141)Text(57-200)newValue(75-75)
core/navigation/build.gradle.kts (2)
build.gradle.kts (1)
alias(1-11)build-logic/convention/build.gradle.kts (1)
id(45-48)
feature/onboarding/build.gradle.kts (3)
feature/senior/build.gradle.kts (2)
implementation(35-42)namespace(6-33)feature/guardian/build.gradle.kts (3)
implementation(35-42)namespace(6-33)minSdk(10-15)data/build.gradle.kts (1)
implementation(9-11)
app/src/main/kotlin/com/moa/app/main/MainActivity.kt (7)
app/src/main/kotlin/com/moa/app/navigation/ObserveNavigationEvents.kt (1)
ObserveNavigationEvents(13-45)core/designsystem/src/main/kotlin/com/moa/app/designsystem/theme/MoaTheme.kt (1)
MoATheme(24-34)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt (1)
SplashScreen(18-23)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (1)
SignInScreen(25-32)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileScreen.kt (1)
SignUpProfileScreen(30-38)feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/auth/SignUpPhoneAuthScreen.kt (1)
SignUpPhoneAuthScreen(30-33)app/src/main/kotlin/com/moa/app/MainActivity.kt (5)
onCreate(17-33)MoATheme(22-31)Scaffold(23-30)onCreate(19-32)Greeting(46-48)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileViewModel.kt (1)
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/SignUpProfileScreen.kt (4)
{}(49-49)SignUpProfileScreen(29-32)MaTopAppBar(46-201)SignUpProfileScreenContent(34-202)
⏰ 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 (22)
core/designsystem/src/main/res/drawable/img_moa_logo.xml (1)
1-48: 벡터 드로어블 리소스가 올바르게 정의됨 — 스트로크 너비 검증 권장MOA 로고 벡터 드로어블이 적절하게 구조화되었으며, 색상 값이 일관성 있게 정의되었습니다. 그러나 스트로크 너비(
13.8394)가 뷰포트 크기(200×84)에 비해 상당히 두꺼워 보입니다 (약 7%).스플래시 스크린에서 렌더링되는 실제 로고의 시각적 모양을 확인하여 스트로크 너비가 디자인 의도와 일치하는지 검증하시기 바랍니다. 필요시 다양한 화면 밀도(mdpi, xhdpi 등)에서도 테스트하여 일관된 시각적 품질을 확보하시기 바랍니다.
core/navigation/.gitignore (1)
1-1: LGTM!표준 Gradle 빌드 디렉토리 제외 규칙이 올바르게 설정되었습니다.
app/src/main/AndroidManifest.xml (1)
17-17: LGTM!MainActivity 경로가 새로운 패키지 구조
com.moa.app.main에 맞게 올바르게 업데이트되었습니다.settings.gradle.kts (1)
36-36: LGTM!새로운
core:navigation모듈이 프로젝트에 올바르게 추가되었습니다.feature/onboarding/build.gradle.kts (1)
13-13: LGTM!온보딩 기능 모듈에 네비게이션 의존성이 올바르게 추가되었습니다.
app/build.gradle.kts (1)
39-39: LGTM!앱 모듈에 네비게이션 의존성이 올바르게 추가되었습니다.
app/src/main/kotlin/com/moa/app/main/MainViewModel.kt (1)
8-14: LGTM!
Navigator를 주입받아 네비게이션 이벤트를 노출하는 깔끔한 구현입니다. PR 설명에 명시된 대로 Channel 기반 단일 소비와repeatOnLifecycle을 통한 안전한 수집이 UI 레이어에서 이루어질 것으로 예상됩니다.feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/auth/SignUpPhoneAuthScreen.kt (1)
1-1: LGTM!패키지 구조가
signup.auth로 더 명확하게 분리되었습니다.core/navigation/build.gradle.kts (1)
1-8: 이제 더 직접적인 검증을 위해 저장소 구조를 확인하겠습니다.최초 검증 스크립트의 빈 출력 결과와 웹 기반 조사 결과를 종합하면, 다음과 같은 결론에 도달할 수 있습니다:
웹 검색 결과 분석:
JVM 플러그인만 적용하면 개발자가 의도한 바는 JVM 라이브러리이며, android.content.Context를 임포트하기 어렵게 만들어(좋은 방식) 아키텍처 명확성을 확보할 수 있습니다. 이는 네비게이션 모듈이 순수 Kotlin/JVM 코드로 유지되어야 하는 경우의 의도적인 설계입니다.현재 상황:
- 초기 검증 스크립트에서 Android 임포트가 발견되지 않음 (빈 출력)
- 모듈의 실제 소스 코드 내용을 샌드박스에서 확인할 수 없음
- 리뷰 댓글은 잠재적 위험성 기반으로 작성되었지만 현재 코드에서는 확인되지 않음
결론:
현재moa.kotlin.jvm플러그인 사용은 네비게이션 추상화를 플랫폼 독립적으로 유지하려는 의도적인 아키텍처 선택으로 보이며, 검증 결과 Android API 사용이 감지되지 않았습니다.
navigation 모듈의 현재 build.gradle.kts 구성은 플랫폼 독립적인 네비게이션 추상화 계층으로 설계된 것으로 보입니다. JVM 플러그인 사용은 실제 Android API가 없을 때는 올바른 선택이며, 모듈의 실제 구현을 확인해야 최종 판단이 가능합니다.
core/navigation/src/main/java/com/moa/app/navigation/Navigator.kt (1)
5-14: 네비게이션 인터페이스 설계가 우수합니다.타입 안전한 라우팅과 Flow 기반 이벤트 스트림을 사용한 깔끔한 추상화입니다. NavigationOptions의 기본값 제공도 편리합니다.
core/navigation/src/main/java/com/moa/app/navigation/AppRoute.kt (1)
5-18: 타입 안전한 라우팅 구조가 잘 설계되었습니다.
@Serializablesealed interface를 사용하여 Compose Navigation의 타입 안전 네비게이션을 지원합니다. 모든 라우트가 명확하게 정의되어 있습니다.core/navigation/src/main/java/com/moa/app/navigation/NavigationEvent.kt (1)
3-10: 네비게이션 이벤트 구조가 명확합니다.sealed interface를 사용한 타입 안전한 이벤트 모델이며,
NavigationOptions.Default를 기본값으로 제공하여 편리합니다.feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/splash/SplashScreen.kt (1)
18-45: 스플래시 화면 구현이 깔끔합니다.ViewModel을 통한 네비게이션 로직과 UI를 잘 분리했습니다. Preview 제공도 좋습니다.
core/navigation/src/main/java/com/moa/app/navigation/NavigationOptions.kt (1)
12-22: 네비게이션 옵션이 잘 설계되었습니다.포괄적인 옵션 세트와 명확한 한글 문서화, 그리고 편리한
Default인스턴스를 제공합니다. 모든 속성의 기본값이 안전하게 설정되어 있습니다.app/src/main/kotlin/com/moa/app/di/NavigationModule.kt (1)
11-18: Hilt 바인딩 설정이 올바르게 구성되었습니다.검증 결과,
NavigatorImpl구현체가 존재하고@Inject생성자를 가지고 있으며Navigator인터페이스를 올바르게 구현하고 있습니다. 싱글톤 스코프의 바인딩 설정도 적절합니다.gradle/libs.versions.toml (1)
29-29: 의존성 추가가 적절합니다.코루틴 코어 라이브러리가 올바르게 추가되었으며, 버전 카탈로그 패턴을 정확히 따르고 있습니다. core:navigation 모듈에서 필요한 의존성입니다.
Also applies to: 82-82
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInScreen.kt (1)
25-64: 회원가입 네비게이션 연결이 올바릅니다.SignInViewModel과의 통합이 적절하며, 회원가입 버튼의 네비게이션 로직이 정확하게 구현되어 있습니다.
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signin/SignInViewModel.kt (1)
9-17: 네비게이션 로직이 명확하고 간결합니다.Hilt를 통한 Navigator 주입과 회원가입 화면으로의 네비게이션 구현이 적절합니다. 코드가 간결하며 단일 책임 원칙을 잘 따르고 있습니다.
app/src/main/kotlin/com/moa/app/navigation/NavigatorImpl.kt (1)
10-31: 네비게이션 이벤트 처리가 적절하게 구현되었습니다.Channel.CONFLATED 사용은 중복 네비게이션을 방지하려는 의도와 일치하며, trySend와 onFailure를 통한 에러 핸들링이 적절합니다.
다만, CONFLATED 용량은 빠르게 연속으로 발생하는 네비게이션 이벤트 중 가장 최근 것만 유지하므로, 중간 이벤트가 손실될 수 있습니다. 여러 화면을 순차적으로 거쳐야 하는 시나리오가 있다면 이 동작이 의도한 것인지 확인해주세요. 현재 온보딩 플로우에서는 단일 목적지 네비게이션만 사용하므로 문제없어 보입니다.
app/src/main/kotlin/com/moa/app/main/MainActivity.kt (1)
23-61: 네비게이션 설정이 올바르게 구현되었습니다.MainActivity가 NavHost 기반 네비게이션을 적절히 설정하고 있으며, ObserveNavigationEvents를 통한 ViewModel과의 연결이 정확합니다. type-safe navigation 패턴이 잘 적용되었고, 모든 온보딩 화면이 올바르게 등록되어 있습니다.
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileScreen.kt (1)
31-44: ViewModel 통합이 일관되고 정확합니다.SignUpProfileViewModel과의 연결이 적절하며, 네비게이션 콜백이 UI 컴포넌트에 올바르게 전달되고 있습니다. Preview도 새로운 시그니처에 맞게 업데이트되어 있습니다.
Also applies to: 58-58, 197-197, 216-220
feature/onboarding/src/main/kotlin/com/moa/app/feature/onboarding/signup/profile/SignUpProfileViewModel.kt (1)
9-22: 네비게이션 로직이 명확합니다.앞으로 진행과 뒤로 가기 네비게이션 모두 적절히 구현되어 있으며, Navigator 패턴을 올바르게 활용하고 있습니다. 다른 ViewModel들과 일관된 구조를 유지하고 있습니다.
Related issue 🛠
Work Description ✏️
core:navigation모듈 생성Key Points 🔑
Uncompleted Tasks 😅
Summary by CodeRabbit
릴리스 노트