Skip to content

[Init] #3 Nav3 셋업#7

Merged
ikseong00 merged 38 commits into
developfrom
init/#3-navigation3-setup
Dec 31, 2025
Merged

[Init] #3 Nav3 셋업#7
ikseong00 merged 38 commits into
developfrom
init/#3-navigation3-setup

Conversation

@ikseong00
Copy link
Copy Markdown
Contributor

@ikseong00 ikseong00 commented Dec 29, 2025

🔗 관련 이슈

📙 작업 설명

  • 컨벤션 플러그인

    • feature 모듈을 api, impl 로 분리함에 맞춰서 컨벤션 플러그인 또한 AndroidFeatureApiConventionPlugin, AndroidFeatureImplConventionPlugin 로 분리했습니다.
  • 의존성

    • nav3 의존성 추가
    • hilt lifecycle viewmodel compose 의존성 추가
    • hilt 버전 업데이트
    • SDK 버전 36으로 업데이트
  • NavigationState

    • 현재 NavKey
    • 현재 NavKeySubstack
    • TopLevelKey 등을 가지고 있는 클래스입니다.
  • Navigator

    • 각 feature 모듈에서 EntryProviderHilt 로 주입하고 있고, Navigatornavigate 함수와 goBack 함수를 사용해야 하기 때문에 해당 EntryProvider 에 주입하기 위해서 인터페이스와 구현체로 나누었고, app 모듈에서 의존성을 주입합니다.
  • 바텀 탭 모듈 생성 (Pose, Archive, Map)

  • 바텀 네비게이션 바 임시 구현

    • 추후 폰트와 컬러 디자인 시스템이 설정되면 수정이 필요합니다.
image

💬 추가 설명 or 리뷰 포인트 (선택)

  • 인자를 Screen 에서 전달받고 뷰모델로 넘기는 것 대신에, entry Scope 에서 바로 ViewModel 로 전달하는 것을 사용하려면 Sample 모듈을 확인하면 될 것 같습니다!
  • 뷰모델을 공유하는 방법에 대해서는 viewModelStoreOwnernav3 에서도 적용이 가능한 지 확인해봐야할 것 같습니다.
  • 추가적으로 스플래시, 온보딩 과정이 있을 때의 구조도 논의가 필요할 것 같습니다.
    • Activity 추가 or NavigationState 에 온보딩 관련 SubStack 정의

Summary by CodeRabbit

  • 새로운 기능

    • 앱 최상단 내비게이션 추가: 포즈 추천, 아카이빙, 네컷지도 탭 및 각 탭 이동 지원
    • 각 탭용 아이콘과 한국어 라벨 추가
    • 샘플 화면(샘플 상세) 및 해당 내비게이션 엔트리 추가
  • UI/UX 개선

    • 하단바의 출현/사라짐 애니메이션 및 선택 상태 시각적 피드백 적용

✏️ Tip: You can customize this high-level summary in your review settings.

@ikseong00 ikseong00 requested a review from Ojongseok December 29, 2025 19:50
@ikseong00 ikseong00 self-assigned this Dec 29, 2025
@ikseong00 ikseong00 added the feat label Dec 29, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 29, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Navigation3 기반의 다층 네비게이션(core/navigation: NavigationState, Navigator/Impl), 피처별 API/impl 모듈(pose/archive/map)과 EntryProvider 등록, 앱 MainActivity에 Hilt 주입된 Navigator 연결 및 Compose 바텀 네비게이션, 빌드 로직·버전 카탈로그 확장 추가.

Changes

코호트 / 파일(들) 변경 요약
빌드 설정 & 버전 카탈로그
gradle/libs.versions.toml, build.gradle.kts, app/build.gradle.kts, build-logic/build.gradle.kts
Hilt 버전 상향, Navigation3·lifecycle·material 등 항목 추가, 플러그인 alias 분리(api/impl) 및 android-library alias 추가
빌드 로직 플러그인 구현
build-logic/src/main/java/.../AndroidFeatureApiConventionPlugin.kt, .../AndroidFeatureImplConventionPlugin.kt, build-logic/src/main/java/.../const/BuildConst.kt
feature api/impl convention 플러그인 추가/리네이밍, 플러그인 의존성 변경, COMPILE_SDK/TARGET_SDK 35→36
프로젝트 설정
settings.gradle.kts
core/navigation 및 pose/archive/map 각 api/impl 모듈 포함 추가
코어 네비게이션 모듈
core/navigation/... (NavigationState.kt, Navigator.kt, NavigatorImpl.kt)
NavigationState(탑레벨/서브 스택, toEntries), Navigator 인터페이스·typealias, NavigatorImpl 구현 추가 — 다층 스택 및 탐색 로직 도입
앱 레이어 및 DI
app/src/main/java/com/neki/android/app/MainActivity.kt, app/src/main/AndroidManifest.xml, app/src/main/java/com/neki/android/app/navigation/di/*, .../keys/Keys.kt, .../TopLevelNavItem.kt
Hilt 주입 MainActivity 추가(Compose + NavDisplay + BottomNav 연동), Navigator 바인딩 모듈 및 NavigationState 제공, 탑레벨 네비 아이템/키 정의, 매니페스트 activity 이름 상대경로로 변경
앱 UI 컴포넌트 & 리소스
app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt, app/src/main/res/drawable/ic_nav_*.xml, app/src/main/res/values/strings.xml
Compose 기반 BottomNavigationBar/Item 추가, 탑레벨 아이콘 벡터 6종 및 문자열 3개 추가
피처 모듈 — API 정의
feature/pose/api/..., feature/archive/api/..., feature/map/api/..., feature/sample/api/...
각 피처용 NavKey(sealed, @Serializable) 및 Navigator 확장(navigateToX) 추가; sample API에서 기존 불필요 클래스 제거 후 SampleNavKey 도입
피처 모듈 — Impl 등록
feature/pose/impl/..., feature/archive/impl/..., feature/map/impl/..., feature/sample/impl/...
각 impl에 EntryProviderModule(@Module/@Installin, @IntoSet) 추가하여 NavEntry 등록; sample impl에 SampleScreen·SampleViewModel(AssistedInject) 도입, 기존 sample MainActivity/MainViewModel 제거
모듈별 Gradle 스크립트
core/navigation/build.gradle.kts, feature/*/*/build.gradle.kts
각 모듈 plugin alias 및 namespace 설정, navigation3·lifecycle 의존성 선언 등 추가

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant MainActivity
    participant NavigatorImpl
    participant NavigationState
    participant EntryProviderScope
    participant FeatureModule as "Feature\n(Pose/Archive/Map)"

    User->>MainActivity: 앱 실행 / 탭 선택
    MainActivity->>NavigatorImpl: navigate(key)
    alt key가 탑레벨일 때
        NavigatorImpl->>NavigationState: goToTopLevel(key)
    else 서브 키일 때
        NavigatorImpl->>NavigationState: goToKey(key)
    end
    NavigationState-->>MainActivity: 상태(현재 키 및 스택) 업데이트
    MainActivity->>EntryProviderScope: entryProvider 구성 (Set<EntryProviderInstaller>)
    EntryProviderScope->>FeatureModule: 등록된 entry 빌드 요청
    FeatureModule-->>MainActivity: NavEntry 반환
    MainActivity->>User: NavDisplay로 화면 렌더링
Loading
sequenceDiagram
    autonumber
    participant User
    participant BottomNavBar
    participant NavigatorImpl
    participant NavigationState

    User->>BottomNavBar: 탭 클릭
    BottomNavBar->>NavigatorImpl: onTabSelected(navKey)
    NavigatorImpl->>NavigationState: goToTopLevel(navKey)
    NavigationState-->>BottomNavBar: UI 상태(선택/비선택) 업데이트
    User->>System: Back 버튼
    System->>NavigatorImpl: goBack()
    alt 현재가 startKey
        NavigatorImpl->>NavigatorImpl: 에러 처리 / 종료 시그널
    else top-level 스택에서 pop
        NavigatorImpl->>NavigationState: pop topLevelStack
    else sub-stack에서 pop
        NavigatorImpl->>NavigationState: pop currentSubStack
    end
    NavigationState-->>User: 이전 화면으로 복귀
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • [Init] #3 Nav3 셋업 #7 — 동일한 코드 수준 변경: core/navigation 추가, Navigator/NavigationState, pose/archive/map 피처와 EntryProvider, app MainActivity/BottomNavigationBar 및 빌드 로직 변경과 매칭됨.

Suggested reviewers

  • Ojongseok

Poem

폴짝폴짝 네비 다니는 토끼예요,
탭을 톡 누르면 층층 스택 위로 폴짝,
Hilt 실로 길을 잇고 아이콘 반짝,
포즈·아카이브·지도가 함께 춤추네,
토끼가 껑충 외쳐요: 잘 했어요! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 Navigation3 설정을 명확히 설명하며 PR의 주요 목표와 정확히 일치합니다.
Linked Issues check ✅ Passed 모든 코딩 요구사항 충족: TopLevelRoute (TopLevelNavItem), 네비게이션바 (BottomNavigationBar), NavDisplay, Entry Provider (ArchiveEntryProviderModule 등), HiltViewModel Factory (SampleViewModel).
Out of Scope Changes check ✅ Passed 모든 변경사항이 Navigation3 설정 범위 내에 있습니다. SDK 버전 업데이트, feature 모듈 분리, 의존성 추가 모두 Navigation3 구현을 위한 필수 변경입니다.

📜 Recent review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de190d3 and 7538094.

📒 Files selected for processing (1)
  • app/src/main/java/com/neki/android/app/navigation/di/NavigationModule.kt

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Nitpick comments (10)
build-logic/src/main/java/com/neki/android/buildlogic/plugins/AndroidFeatureApiConventionPlugin.kt (1)

1-5: 패키지 선언이 누락되었습니다.

파일 경로가 com/neki/android/buildlogic/plugins인데 패키지 선언이 없습니다. 다른 컨벤션 플러그인과의 일관성을 위해 패키지 선언을 추가하는 것이 좋습니다.

🔎 제안하는 수정
+package com.neki.android.buildlogic.plugins
+
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.kotlin.dsl.apply
 import org.gradle.kotlin.dsl.dependencies
feature/pose/impl/build.gradle.kts (1)

9-14: 의존성 구성 확인

impl 모듈의 의존성 구성이 적절합니다. pose.api 모듈에 대한 의존성이 올바르게 선언되어 있습니다.

참고: androidx.appcompat 의존성이 Compose 기반 구현에 실제로 필요한지 확인해보세요. Compose만 사용한다면 불필요할 수 있습니다.

feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleScreen.kt (1)

9-14: modifier 파라미터가 적용되지 않았습니다.

modifier 파라미터가 선언되었지만 Text composable에 전달되지 않아 호출자가 레이아웃을 제어할 수 없습니다.

🔎 수정 제안
 @Composable
 fun SampleScreen(
     modifier: Modifier = Modifier,
     viewModel: SampleViewModel = hiltViewModel(),
 ) {
-    Text("Sample")
+    Text("Sample", modifier = modifier)
 }
app/src/main/res/drawable/ic_nav_map_unselected.xml (1)

8-8: 하드코딩된 색상 값 사용

#B7B9C3 색상이 직접 하드코딩되어 있습니다. 디자인 시스템 통합 시 테마 색상 리소스(@color/... 또는 ?attr/...)로 변경하면 유지보수성이 향상됩니다. PR 설명에 임시 구현이라고 언급되어 있으므로 현재는 허용 가능합니다.

Also applies to: 14-14

feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapEntryProvider.kt (2)

25-27: Entry 구현 내용을 추가해야 합니다.

entry<MapNavKey.Map> 블록이 비어 있습니다. Map 화면의 Composable 콘텐츠를 추가해야 합니다.

Map 화면 구현 코드를 생성하거나 이를 추적할 이슈를 생성하시겠습니까?


29-29: 파일 끝의 불필요한 빈 줄을 제거하세요.

🔎 제안된 수정사항
 private fun EntryProviderScope<NavKey>.mapEntry(navigator: Navigator) {
     entry<MapNavKey.Map> {}
 }
-
-
feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/navigation/PoseEntryProvider.kt (1)

25-27: 빈 entry 본문 및 미사용 파라미터 확인 필요.

entry<PoseNavKey.Pose> { }가 비어 있어 이 경로로 이동 시 아무 UI도 렌더링되지 않습니다. 또한 navigator 파라미터가 사용되지 않고 있습니다. 의도된 플레이스홀더라면 TODO 주석을 추가하거나, 실제 UI 구현이 필요하다면 PoseScreen 컴포저블을 추가해 주세요.

🔎 사용하지 않는 파라미터 제거 제안
-private fun EntryProviderScope<NavKey>.poseEntry(navigator: Navigator) {
-    entry<PoseNavKey.Pose> { }
+private fun EntryProviderScope<NavKey>.poseEntry() {
+    entry<PoseNavKey.Pose> { 
+        // TODO: PoseScreen 구현 필요
+    }
 }

그리고 호출부도 수정:

 fun providePoseEntryBuilder(navigator: Navigator): EntryProviderInstaller = {
-    poseEntry(navigator)
+    poseEntry()
 }
app/src/main/java/com/neki/android/app/MainActivity.kt (1)

27-28: 구체적 구현체 대신 인터페이스 주입을 권장합니다.

NavigatorImpl을 직접 주입하는 것보다 Navigator 인터페이스를 주입하는 것이 DI 원칙에 부합합니다. 이렇게 하면 테스트 시 mock 교체가 용이하고 구현 변경에 유연하게 대응할 수 있습니다.

🔎 인터페이스 주입으로 변경
 @Inject
-lateinit var navigator: NavigatorImpl
+lateinit var navigator: Navigator

단, Navigator 인터페이스에 state 프로퍼티가 노출되어 있어야 합니다.

app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt (1)

69-99: 하드코딩된 컬러값은 디자인 시스템으로 이전이 필요합니다.

PR 설명에 디자인 시스템 폰트/컬러 적용이 보류 중이라고 명시되어 있습니다. 현재 하드코딩된 컬러값(0xFF3C3E48, 0xFFB7B9C3)을 추후 NekiTheme의 컬러 시스템으로 교체하시기 바랍니다.

추가로, BottomNavigationBarItem이 public으로 선언되어 있는데, 이 컴포넌트 내부에서만 사용된다면 private으로 변경하는 것을 고려해 주세요.

core/navigation/src/main/java/com/neki/android/core/navigation/NavigationState.kt (1)

31-50: toMutableStateList() 호출이 매 리컴포지션마다 새 리스트를 생성합니다.

Line 49의 toMutableStateList()remember로 감싸지 않으면 매 리컴포지션마다 새로운 SnapshotStateList 인스턴스를 생성합니다. 이로 인해 NavDisplay와 같은 소비자에서 참조 안정성이 깨져 불필요한 리컴포지션이 발생할 수 있습니다.

remember로 결과를 캐싱하여 참조 안정성을 확보하세요:

 @Composable
 fun NavigationState.toEntries(
     entryProvider: (NavKey) -> NavEntry<NavKey>,
 ): SnapshotStateList<NavEntry<NavKey>> {
     val decoratedEntries = subStacks.mapValues { (_, stack) ->
         val decorators = listOf(
             rememberSaveableStateHolderNavEntryDecorator<NavKey>(),
             rememberViewModelStoreNavEntryDecorator<NavKey>(),
         )
         rememberDecoratedNavEntries(
             backStack = stack,
             entryDecorators = decorators,
             entryProvider = entryProvider,
         )
     }
 
-    return topLevelStack
-        .flatMap { decoratedEntries[it] ?: emptyList() }
-        .toMutableStateList()
+    return remember(topLevelStack.toList(), decoratedEntries) {
+        topLevelStack
+            .flatMap { decoratedEntries[it] ?: emptyList() }
+            .toMutableStateList()
+    }
 }
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8dda0bd and 6318891.

📒 Files selected for processing (54)
  • app/build.gradle.kts
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/com/neki/android/app/MainActivity.kt
  • app/src/main/java/com/neki/android/app/navigation/TopLevelNavItem.kt
  • app/src/main/java/com/neki/android/app/navigation/di/AppModule.kt
  • app/src/main/java/com/neki/android/app/navigation/di/NavigationModule.kt
  • app/src/main/java/com/neki/android/app/navigation/keys/Keys.kt
  • app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt
  • app/src/main/res/drawable/ic_nav_archive_selected.xml
  • app/src/main/res/drawable/ic_nav_archive_unselected.xml
  • app/src/main/res/drawable/ic_nav_map_selected.xml
  • app/src/main/res/drawable/ic_nav_map_unselected.xml
  • app/src/main/res/drawable/ic_nav_pose_selected.xml
  • app/src/main/res/drawable/ic_nav_pose_unselected.xml
  • app/src/main/res/values/strings.xml
  • build-logic/build.gradle.kts
  • build-logic/src/main/java/com/neki/android/buildlogic/const/BuildConst.kt
  • build-logic/src/main/java/com/neki/android/buildlogic/plugins/AndroidFeatureApiConventionPlugin.kt
  • build-logic/src/main/java/com/neki/android/buildlogic/plugins/AndroidFeatureImplConventionPlugin.kt
  • build.gradle.kts
  • core/navigation/.gitignore
  • core/navigation/build.gradle.kts
  • core/navigation/src/main/java/com/neki/android/core/navigation/NavigationState.kt
  • core/navigation/src/main/java/com/neki/android/core/navigation/Navigator.kt
  • core/navigation/src/main/java/com/neki/android/core/navigation/NavigatorImpl.kt
  • feature/archive/api/.gitignore
  • feature/archive/api/build.gradle.kts
  • feature/archive/api/src/main/kotlin/com/neki/android/feature/archive/api/ArchiveNavKey.kt
  • feature/archive/impl/.gitignore
  • feature/archive/impl/build.gradle.kts
  • feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt
  • feature/map/api/.gitignore
  • feature/map/api/build.gradle.kts
  • feature/map/api/src/main/java/com/neki/android/feature/map/api/MapNavKey.kt
  • feature/map/impl/.gitignore
  • feature/map/impl/build.gradle.kts
  • feature/map/impl/src/main/java/com/neki/android/feature/map/impl/MapEntryProvider.kt
  • feature/pose/api/.gitignore
  • feature/pose/api/build.gradle.kts
  • feature/pose/api/src/main/java/com/neki/android/feature/pose/api/PoseNavKey.kt
  • feature/pose/impl/.gitignore
  • feature/pose/impl/build.gradle.kts
  • feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/navigation/PoseEntryProvider.kt
  • feature/sample/api/build.gradle.kts
  • feature/sample/api/src/main/java/com/neki/android/feature/sample/api/MyClass.kt
  • feature/sample/api/src/main/java/com/neki/android/feature/sample/api/SampleNavKey.kt
  • feature/sample/impl/build.gradle.kts
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/MainActivity.kt
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/MainViewModel.kt
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleEntryProvider.kt
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleScreen.kt
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleViewModel.kt
  • gradle/libs.versions.toml
  • settings.gradle.kts
💤 Files with no reviewable changes (3)
  • feature/sample/api/src/main/java/com/neki/android/feature/sample/api/MyClass.kt
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/MainViewModel.kt
  • feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/MainActivity.kt
🧰 Additional context used
🧬 Code graph analysis (3)
app/src/main/java/com/neki/android/app/MainActivity.kt (2)
core/designsystem/src/main/java/com/neki/android/core/designsystem/ui/theme/Theme.kt (1)
  • NekiTheme (35-57)
app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt (1)
  • BottomNavigationBar (32-67)
app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt (1)
core/designsystem/src/main/java/com/neki/android/core/designsystem/ui/theme/Theme.kt (1)
  • NekiTheme (35-57)
feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleEntryProvider.kt (1)
feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleScreen.kt (1)
  • SampleScreen (8-14)
⏰ 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: ci-build
🔇 Additional comments (43)
feature/pose/impl/.gitignore (1)

1-1: 표준 빌드 무시 규칙이 올바르게 추가되었습니다.

모듈별 빌드 아티팩트를 버전 관리에서 제외하는 표준 관행입니다.

feature/pose/api/.gitignore (1)

1-1: LGTM!

표준 빌드 디렉토리 무시 규칙입니다.

feature/map/impl/.gitignore (1)

1-1: LGTM!

올바른 빌드 아티팩트 무시 규칙입니다.

feature/map/api/.gitignore (1)

1-1: LGTM!

표준 .gitignore 규칙이 올바르게 적용되었습니다.

core/navigation/.gitignore (1)

1-1: LGTM!

core/navigation 모듈의 빌드 디렉토리가 올바르게 무시 처리되었습니다.

feature/archive/impl/.gitignore (1)

1-1: LGTM!

표준 빌드 무시 규칙입니다.

feature/archive/api/.gitignore (1)

1-1: LGTM!

올바른 빌드 아티팩트 무시 규칙입니다.

build-logic/src/main/java/com/neki/android/buildlogic/const/BuildConst.kt (1)

12-13: Android SDK 36은 2025년 3월에 공식 출시된 안정적인 플랫폼입니다. 프로젝트의 현재 의존성 버전들이 모두 SDK 36과 호환됩니다:

  • AGP 8.13.1: API 36 공식 지원 (최소 요구사항: AGP 8.9.1)
  • Kotlin 2.1.0: AGP 8.13.1과 호환
  • Hilt 2.54: AGP 8.1 이상 지원, KSP 기반 컴파일러 사용
  • Navigation3 1.0.0: 현재 Kotlin/AGP 버전과 호환
  • KSP 2.1.0-1.0.29: Kotlin 2.1.0과 일치

TARGET_SDK와 COMPILE_SDK를 36으로 업데이트하는 것은 안전하고 호환성 문제가 없습니다.

feature/sample/api/build.gradle.kts (1)

2-2: 새로운 feature.api 플러그인으로 마이그레이션이 잘 되었습니다.

기존 library 플러그인에서 feature.api 플러그인으로의 전환이 적절합니다. 이를 통해 feature API 모듈에 필요한 의존성과 설정이 자동으로 적용됩니다.

build.gradle.kts (1)

3-3: android.library 플러그인 추가 확인

라이브러리 모듈 지원을 위한 표준적인 플러그인 추가입니다.

feature/archive/api/build.gradle.kts (1)

1-7: Archive API 모듈 설정이 올바릅니다.

feature API 컨벤션 플러그인을 사용한 표준적인 설정입니다. 네임스페이스도 적절하게 지정되었습니다.

feature/map/api/build.gradle.kts (1)

1-7: Map API 모듈 설정이 올바릅니다.

Archive API 모듈과 동일한 패턴을 따르고 있으며, feature API 컨벤션 플러그인이 적절하게 적용되었습니다.

build-logic/src/main/java/com/neki/android/buildlogic/plugins/AndroidFeatureApiConventionPlugin.kt (1)

6-19: 플러그인 구현이 올바르게 설정되어 있습니다.

"neki.android.library" 플러그인이 build-logic에 제대로 등록되어 있으며, AndroidLibraryConventionPlugin도 존재합니다. API 모듈에 적합한 구성입니다. api() 의존성으로 navigation을 노출하는 것이 올바른 접근이며, 직렬화 플러그인도 네비게이션 데이터 처리에 적합합니다.

feature/map/impl/build.gradle.kts (1)

1-14: LGTM!

표준 feature 구현 모듈 구성이 올바르게 적용되었습니다. 플러그인, 네임스페이스, 의존성 선언이 일관성 있게 구성되어 있습니다.

app/src/main/res/drawable/ic_nav_map_selected.xml (1)

1-16: LGTM!

하단 네비게이션 아이콘 리소스가 올바르게 정의되었습니다.

app/src/main/res/drawable/ic_nav_archive_unselected.xml (1)

1-9: LGTM!

미선택 상태의 아카이브 아이콘이 올바르게 정의되었습니다.

feature/pose/api/build.gradle.kts (1)

1-7: LGTM!

feature api 모듈의 표준 구성이 올바르게 적용되었습니다.

app/src/main/res/drawable/ic_nav_pose_selected.xml (1)

1-9: LGTM!

선택 상태의 포즈 아이콘이 올바르게 정의되었습니다.

app/src/main/res/values/strings.xml (1)

4-6: LGTM!

하단 네비게이션 라벨에 필요한 문자열 리소스가 올바르게 추가되었습니다.

app/src/main/java/com/neki/android/app/navigation/keys/Keys.kt (1)

6-7: LGTM!

네비게이션 키 상수 정의가 올바르게 구성되었습니다. (import 오류 수정 필요)

feature/archive/impl/build.gradle.kts (1)

1-14: LGTM!

표준 feature 구현 모듈 구성이 일관성 있게 적용되었습니다.

feature/sample/impl/build.gradle.kts (1)

2-3: LGTM!

플러그인 별칭이 neki.android.feature.impl로 변경되고 feature.sample.api 의존성이 추가되어 feature 모듈 분리 패턴(api/impl)과 일관성 있게 구성되었습니다.

Also applies to: 13-13

app/src/main/AndroidManifest.xml (1)

17-24: LGTM!

MainActivity가 app 모듈로 이동됨에 따라 manifest에서 상대 경로(.MainActivity)를 올바르게 사용하고 있습니다. 기존 .NekiApplication 참조와 일관성 있는 패턴입니다.

app/build.gradle.kts (1)

14-34: LGTM!

Navigation 3 설정을 위한 의존성이 잘 구성되었습니다:

  • Core navigation 모듈 추가
  • Feature 모듈들(pose, archive, map)이 api/impl 패턴으로 일관되게 추가됨
  • 필요한 Compose 및 Navigation UI 라이브러리 포함
feature/map/api/src/main/java/com/neki/android/feature/map/api/MapNavKey.kt (1)

7-15: LGTM!

Navigation key 패턴이 잘 구현되었습니다:

  • sealed interface로 타입 안전성 확보
  • @Serializable로 상태 직렬화 지원
  • Navigator 확장 함수로 편리한 네비게이션 API 제공

다른 feature 모듈(pose, archive)과 일관된 패턴입니다.

feature/pose/api/src/main/java/com/neki/android/feature/pose/api/PoseNavKey.kt (1)

7-15: LGTM!

MapNavKey와 동일한 패턴으로 일관성 있게 구현되었습니다. Sealed interface와 @Serializable data object를 사용한 Navigation key 정의가 적절합니다.

feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleScreen.kt (1)

6-6: import 경로는 정확합니다

현재 코드에서 사용하는 androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel은 Hilt Compose 통합의 현재 표준입니다. 이전에 사용되던 androidx.hilt.navigation.compose.hiltViewModel은 이미 deprecated되었으며, API가 lifecycle.viewmodel.compose로 이동되었습니다.

Likely an incorrect or invalid review comment.

feature/sample/api/src/main/java/com/neki/android/feature/sample/api/SampleNavKey.kt (1)

1-15: LGTM! 잘 구현된 네비게이션 키입니다.

파라미터를 받는 Sample 데이터 클래스와 네비게이션 확장 함수가 올바르게 구현되었습니다. 다른 feature 모듈들의 NavKey 패턴과 일관성 있게 구성되어 있습니다.

app/src/main/java/com/neki/android/app/navigation/di/AppModule.kt (1)

10-18: LGTM! Navigator DI 바인딩이 올바릅니다.

ActivityRetainedComponent 스코프에서 Navigator 인터페이스를 NavigatorImpl에 바인딩하는 것이 적절합니다.

app/src/main/java/com/neki/android/app/navigation/di/NavigationModule.kt (1)

11-22: LGTM! NavigationState 제공 로직이 올바릅니다.

START_NAV_KEYTOP_LEVEL_NAV_KEYS를 사용한 NavigationState 초기화가 적절하게 구현되었습니다.

core/navigation/src/main/java/com/neki/android/core/navigation/Navigator.kt (1)

1-11: LGTM! 깔끔한 네비게이션 인터페이스입니다.

Navigator 인터페이스와 EntryProviderInstaller typealias가 명확하고 간결하게 정의되었습니다.

settings.gradle.kts (1)

35-41: LGTM! 새 모듈이 올바르게 추가되었습니다.

core:navigation 모듈과 feature 모듈들(pose, archive, map의 api/impl)이 프로젝트 구조에 맞게 적절히 추가되었습니다.

core/navigation/build.gradle.kts (1)

11-14: Navigation3 라이브러리 버전이 유효하게 정의되어 있습니다.

gradle/libs.versions.toml에서 버전이 명시적으로 관리되고 있으며, 사용 중인 버전들이 유효합니다:

  • androidxNavigation3 = "1.0.0"
  • androidxLifecycleViewModelNavigation3 = "2.10.0"

이 버전들은 안정 버전이며 알려진 보안 취약점이 없습니다.

feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleEntryProvider.kt (1)

1-35: LGTM!

Assisted injection을 사용한 hiltViewModel 패턴이 올바르게 구현되었습니다. 이 패턴은 런타임에 navKey를 ViewModel에 전달하는 좋은 예시입니다.

feature/sample/impl/src/main/java/com/neki/android/feature/sample/impl/SampleViewModel.kt (1)

1-21: LGTM!

@HiltViewModel@AssistedFactory를 함께 사용한 패턴이 올바르게 구현되었습니다. Nav3에서 네비게이션 인자를 ViewModel에 전달하는 좋은 참조 구현입니다.

build-logic/build.gradle.kts (1)

34-41: LGTM!

feature 모듈의 api/impl 분리에 맞춰 플러그인을 두 개로 분리한 것이 적절합니다. 이 구조는 feature 모듈 간 의존성 관리를 개선합니다.

core/navigation/src/main/java/com/neki/android/core/navigation/NavigatorImpl.kt (2)

44-48: subList().clear() 사용이 올바릅니다.

subList(1, size).clear()는 원본 리스트를 직접 수정하여 첫 번째 요소만 남기는 올바른 방법입니다.


18-27: startKey가 topLevelKeys에 포함되어 있는지 확인하세요.

goBack()에서 topLevelStack이 완전히 비워지는 상황은 방지됩니다. 다만, startKeytopLevelKeys에 없으면 더 심각한 문제가 발생합니다. topLevelStack[startKey]로 축소되었을 때 currentSubStack에 접근하면, subStacksstartKey의 항목이 없어 (NavigationState 라인 21) 예외가 발생합니다. topLevelStack[startKey] 상태에서 currentSubStack이나 currentKey에 접근하면 "Sub stack for startKey does not exist" 에러가 나올 수 있습니다.

app/src/main/java/com/neki/android/app/navigation/TopLevelNavItem.kt (1)

11-38: Enum 구조가 잘 설계되었습니다.

@DrawableRes, @StringRes 어노테이션을 활용하여 리소스 타입 안전성을 확보했고, 각 항목에 navKey를 연결하여 네비게이션과 UI의 결합이 명확합니다.

app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt (2)

32-67: currentKeycurrentTab 파라미터의 역할이 혼동될 수 있습니다.

currentKey는 현재 네비게이션 위치(서브스택 포함)를, currentTab은 현재 선택된 탭을 나타내는 것으로 보입니다. 그러나:

  • Line 59: selected 판단에는 currentTab 사용
  • Line 61: 클릭 방지에는 currentKey 사용

이 로직이 의도된 것인지 확인이 필요합니다. 만약 같은 탭 내에서 다른 화면에 있을 때 탭을 다시 클릭하면 어떤 동작을 원하시나요? 현재 로직상 같은 탭이라도 currentKey가 다르면 onTabSelected가 호출됩니다.


101-113: Preview 구현이 적절합니다.

mutableStateOf를 사용하여 탭 전환을 인터랙티브하게 테스트할 수 있도록 구현되어 있습니다.

gradle/libs.versions.toml (2)

43-50: 라이브러리 추가 및 버전 관리가 잘 구성되어 있습니다.

Navigation3 관련 의존성들이 버전 카탈로그에 체계적으로 추가되었습니다. 그러나 hilt-lifecycle-viewmodel-compose가 알파 버전(1.3.0-alpha02)인 점은 주의가 필요합니다. 안정 버전이 출시되면 업그레이드를 고려해 주세요.


76-78: Feature 플러그인 분리가 적절합니다.

neki-android-featureneki-android-feature-implneki-android-feature-api로 분리하여 feature 모듈의 api/impl 구조에 맞게 구성한 것은 좋은 접근입니다.

Comment thread app/src/main/java/com/neki/android/app/MainActivity.kt
Comment thread app/src/main/java/com/neki/android/app/navigation/keys/Keys.kt Outdated
Comment thread app/src/main/java/com/neki/android/app/navigation/TopLevelNavItem.kt Outdated
Comment thread app/src/main/res/drawable/ic_nav_archive_selected.xml
Comment thread app/src/main/res/drawable/ic_nav_pose_unselected.xml
Comment thread gradle/libs.versions.toml
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a 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

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6318891 and a4f0e7d.

📒 Files selected for processing (4)
  • app/src/main/java/com/neki/android/app/navigation/TopLevelNavItem.kt
  • app/src/main/java/com/neki/android/app/navigation/keys/Keys.kt
  • feature/archive/api/src/main/kotlin/com/neki/android/feature/archive/api/ArchiveNavKey.kt
  • feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/neki/android/app/navigation/keys/Keys.kt
  • app/src/main/java/com/neki/android/app/navigation/TopLevelNavItem.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). (1)
  • GitHub Check: ci-build
🔇 Additional comments (3)
feature/archive/api/src/main/kotlin/com/neki/android/feature/archive/api/ArchiveNavKey.kt (2)

1-11: 이전 이슈가 해결되었으며 구현이 올바릅니다.

패키지 선언의 중복 문제가 수정되었습니다. ArchiveNavKey sealed interface와 Archive data object의 구조가 Navigation3 패턴을 올바르게 따르고 있으며, @Serializable 어노테이션도 적절히 적용되었습니다.


13-15: 편리한 확장 함수입니다.

Navigator에 대한 확장 함수로 Archive 화면으로의 네비게이션을 간편하게 제공합니다. 타입 안전성을 유지하면서 호출 코드를 간결하게 만듭니다.

feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/navigation/ArchiveEntryProvider.kt (1)

14-23: 이전 이슈가 모두 해결되었으며 Hilt 모듈 구성이 올바릅니다.

패키지 선언, import 경로, 모듈 이름이 모두 올바르게 수정되었습니다. ActivityRetainedComponent에 설치하는 것이 네비게이션 엔트리 제공자로서 적절하며, @IntoSet@Provides를 통한 멀티바인딩 패턴이 올바르게 구현되었습니다.

Comment thread feature/archive/impl/build.gradle.kts Outdated
Comment thread app/src/main/java/com/neki/android/app/ui/BottomNavigationBar.kt
Comment thread app/src/main/java/com/neki/android/app/navigation/di/AppModule.kt Outdated
Comment thread core/navigation/src/main/java/com/neki/android/core/navigation/NavigatorImpl.kt Outdated
Comment thread feature/sample/api/build.gradle.kts
Comment thread app/src/main/java/com/neki/android/app/navigation/di/AppModule.kt
Comment thread app/src/main/java/com/neki/android/app/navigation/di/NavigationModule.kt Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
app/src/main/java/com/neki/android/app/navigation/di/NavigationModule.kt (2)

15-16: provider 함수를 internal로 변경하는 것을 고려하세요.

모듈 객체는 이미 internal로 선언되어 있지만, providesNavigationState() 함수는 public입니다. 이전 리뷰 코멘트에서 언급된 것처럼, DI provider 함수를 internal로 선언하면 모듈 외부에서의 직접 접근을 방지하여 캡슐화를 강화할 수 있습니다.

🔎 제안하는 수정
     @Provides
-    fun providesNavigationState(): NavigationState {
+    internal fun providesNavigationState(): NavigationState {
         return NavigationState(
             startKey = START_NAV_KEY,
             topLevelKeys = TOP_LEVEL_NAV_KEYS.toSet(),
         )
     }

19-19: toSet() 변환이 매번 수행됩니다.

TOP_LEVEL_NAV_KEYS.toSet()이 provider 함수 내부에서 호출되므로, 스코프가 추가되더라도 초기화 시 변환이 발생합니다. 만약 TOP_LEVEL_NAV_KEYS가 이미 List라면, Keys.kt에서 미리 Set으로 정의하면 약간의 성능 개선이 가능합니다.

core/navigation/src/main/java/com/neki/android/core/navigation/NavigatorImpl.kt (1)

7-10: 생성자 스타일을 통일하는 것을 고려하세요.

이전 리뷰 코멘트에서 언급된 것처럼, 생성자 주입 시 파라미터를 개행하여 가독성을 높이는 것이 권장됩니다.

🔎 제안하는 코드 스타일
 @ActivityRetainedScoped
-class NavigatorImpl @Inject constructor(
-    val state: NavigationState,
-) : Navigator {
+class NavigatorImpl @Inject constructor(
+    val state: NavigationState
+): Navigator {

또는:

 @ActivityRetainedScoped
 class NavigatorImpl @Inject constructor(
-    val state: NavigationState,
+    val state: NavigationState
 ) : Navigator {
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a4f0e7d and de190d3.

📒 Files selected for processing (6)
  • app/src/main/java/com/neki/android/app/navigation/di/AppModule.kt
  • app/src/main/java/com/neki/android/app/navigation/di/NavigationModule.kt
  • core/navigation/src/main/java/com/neki/android/core/navigation/NavigatorImpl.kt
  • feature/archive/impl/build.gradle.kts
  • feature/map/impl/build.gradle.kts
  • feature/pose/impl/build.gradle.kts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/neki/android/app/navigation/di/AppModule.kt
  • feature/archive/impl/build.gradle.kts
⏰ 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: ci-build
🔇 Additional comments (9)
feature/map/impl/build.gradle.kts (2)

1-7: 플러그인과 네임스페이스 설정이 올바릅니다.

neki.android.feature.impl 플러그인 사용과 네임스페이스 명명이 feature 모듈의 api/impl 분리 컨벤션에 정확히 부합합니다. 버전 카탈로그 alias 사용도 좋습니다.


9-11: 의존성 설정이 올바릅니다.

api 모듈에 대한 의존성이 올바르게 선언되었고, type-safe project accessor(projects.feature.map.api) 사용은 모범 사례입니다. 컨벤션 플러그인이 Hilt, Compose 등 공통 의존성을 제공할 것으로 예상됩니다.

feature/pose/impl/build.gradle.kts (3)

5-7: 네임스페이스가 명명 규칙을 올바르게 따르고 있습니다.

com.neki.android.feature.pose.impl 네임스페이스는 api/impl 분리 구조의 명명 규칙을 정확히 따르고 있습니다.


9-11: 의존성 선언이 올바릅니다.

pose.api 모듈에 대한 의존성이 정확하게 선언되어 있으며, type-safe project accessor를 사용하여 컴파일 타임 안전성을 확보하고 있습니다. 컨벤션 플러그인이 Compose, Hilt 등의 공통 의존성을 제공하므로 이 선언만으로 충분합니다.


1-3: 컨벤션 플러그인 적용이 올바릅니다.

새로 생성된 AndroidFeatureImplConventionPlugin을 올바르게 사용하고 있습니다. 컨벤션 플러그인은 neki.android.library.composeneki.hilt를 적용하고, Compose, Hilt, Navigation3 등 필요한 의존성을 제공합니다.

core/navigation/src/main/java/com/neki/android/core/navigation/NavigatorImpl.kt (4)

12-18: navigate() 로직을 확인했습니다.

현재 key에 따라 clearSubStack, goToTopLevel, goToKey를 호출하는 분기 로직이 명확합니다. 각 케이스가 적절히 처리되고 있습니다.


31-36: goToKey() 로직을 확인했습니다.

키를 제거한 후 다시 추가하여 스택의 끝으로 이동시키는 로직이 명확합니다. 중복 방지 및 순서 관리가 적절히 구현되어 있습니다.


38-44: goToTopLevel() 로직을 확인했습니다.

startKey인 경우 스택을 초기화하고, 그렇지 않으면 기존 키를 제거한 후 추가하는 로직이 명확합니다. Top-level 네비게이션의 의도가 잘 반영되어 있습니다.


46-50: clearSubStack() 로직을 확인했습니다.

subList(1, size).clear()를 사용하여 첫 번째 요소만 남기고 나머지를 제거하는 구현이 효율적이고 명확합니다. 현재 top-level key로 이동 시 sub-stack을 초기화하는 의도가 잘 구현되어 있습니다.

@ikseong00 ikseong00 merged commit ca1c39f into develop Dec 31, 2025
1 of 2 checks passed
@ikseong00 ikseong00 deleted the init/#3-navigation3-setup branch January 1, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[init] Navigation3 환경 설정

2 participants