Skip to content

Navigation3 기반 앱 네비게이션 시스템 구축 및 Scaffold 기본 구조 구현#51

Open
HamBeomJoon wants to merge 26 commits intodevelopfrom
feat/#48-prezel-app-state
Open

Navigation3 기반 앱 네비게이션 시스템 구축 및 Scaffold 기본 구조 구현#51
HamBeomJoon wants to merge 26 commits intodevelopfrom
feat/#48-prezel-app-state

Conversation

@HamBeomJoon
Copy link
Contributor

@HamBeomJoon HamBeomJoon commented Feb 6, 2026

📌 작업 내용

📍 1. Navigation3 기반 핵심 네비게이션 시스템 추가

  • core:navigation 모듈 생성
  • Navigation3 의존성 설정(navigation3.runtime, navigation3.ui, savedstate-compose)
  • Navigator 클래스 추가
    • 상위 레벨 전환, 서브 스택 관리, 뒤로가기 로직 처리
  • NavigationState + rememberNavigationState 구현
    • Compose 환경에서 네비게이션 상태 유지
  • toEntries(...) 확장 함수 추가
    • NavigationState → NavEntry 리스트 변환

📍 2. Feature 모듈 분리 (API/Impl) 구조 도입
Home, History, Profile 피처를 각각 api/impl 모듈로 분리
모듈별 NavKey 및 기본 구현 추가
feature:home, feature:history, feature:profile 모듈들이 독립적으로 구성됨

📍 3. PrezelBottomNavigationBar 컴포넌트 추가

  • PrezelNavigationBar, PrezelNavigationBarItem 컴포넌트 추가
    • 디자인 테마(PrezelTheme)에 맞춘 스타일 적용
  • PrezelNavigationScaffold 및 PrezelNavigationScope DSL 추가

📍 4. App 구조 변경 (Compose + Hilt)

  • PrezelAppState 및 rememberPrezelAppState 추가
    • 네비게이션 상태 + 네트워크 모니터링 저장
  • PrezelApp 최상위 Composable 추가
    • NavDisplay 기반 화면 전환 구조 설정
  • MainActivity에서 Hilt 적용 후 PrezelApp 실행 로직 구성

📍 5. 네트워크 상태 모니터링 기능 추가

  • NetworkMonitor 인터페이스 및 구현체 (ConnectivityManagerNetworkMonitor)
  • DI 설정 추가 (DataModule, 권한 ACCESS_NETWORK_STATE)

📍 6. 빌드/디자인 시스템 변경

  • PrezelDispatchers + Hilt Coroutine DI 설정 추가
  • 디자인 시스템 업데이트:
    • 네비게이션 바 배경, 아이콘 색상
    • 상단 구분선(border) 추가

📍 7. 네비게이션 구조 리팩토링

  • EntryBuilder + Hilt Multi-bindings 패턴 도입
    • 각 feature가 스스로 네비게이션 엔트리를 등록
  • 네비게이션 전환 애니메이션 제거 설정
  • 여러 유틸리티 함수 및 state 접근성, 확장 함수 개선

🧩 관련 이슈


📸 스크린샷

  • BottomNavigationBar
Screenshot_1770362512 Screenshot_1770362515 Screenshot_1770362517

📢 논의하고 싶은 내용

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 홈, 히스토리, 프로필 섹션을 포함한 하단 네비게이션 바 추가
    • 네트워크 연결 상태 감지 기능 추가
    • 앱 네비게이션 상태 관리 시스템 구현
  • 리팩터

    • 의존성 주입 기반 모듈식 앱 아키텍처 도입

Navigation3 라이브러리를 활용하여 다중 백스택(Multiple Back Stacks)을 지원하는 네비게이션 구조를 설계하고 구현했습니다.

*   `Navigator`: 상위 레벨 전환, 서브 스택 관리 및 뒤로 가기 로직을 처리하는 클래스 추가
*   `NavigationState`: `NavBackStack`을 활용한 상태 관리 및 `rememberNavigationState` 구현
*   `toEntries`: `NavigationState`를 `NavHost`에서 사용 가능한 `NavEntry` 리스트로 변환하는 확장 함수 추가
*   `core:navigation` 모듈 설정 및 관련 의존성(`navigation3`, `savedstate-compose`) 추가
home feature의 인터페이스와 네비게이션 정의를 위한 `feature:home:api` 모듈을 생성했습니다.

*   `feature:home:api` 모듈 및 관련 설정 파일 추가
*   `HomeNavKey` 정의 및 `navigation3` 의존성 추가
*   `libs.versions.toml`에 appcompat, material, navigation3 버전 정보 추가
*   `settings.gradle.kts`에 신규 모듈 등록
home feature의 구현(impl) 모듈을 생성하고 기본 구조를 설정했습니다.

*   `home:impl` 모듈 생성 및 `settings.gradle.kts` 등록
*   `HomeScreen`, `HomeViewModel`, `HomeUiState` 기본 코드 추가
*   `navigation3`를 이용한 `HomeEntryProvider` 네비게이션 설정 추가
*   `AndroidFeatureImplConventionPlugin` 내 불필요한 의존성 주석 처리
*   `TopLevelNavItem` 데이터 클래스 및 홈, 히스토리, 프로필 아이템 정의
*   `app` 모듈에 내비게이션 및 홈 피처 관련 의존성 추가
*   `feature:home:api`에 타이틀 문자열 리소스 추가
의존성 주입을 위한 Coroutine Dispatcher 및 ApplicationScope 설정을 추가합니다.

*   `PrezelDispatchers` enum 및 `@Dispatcher` 한정자(Qualifier) 정의
*   `DispatchersModule`을 통해 IO, Default 디스패처 제공
*   `CoroutineScopesModule`을 통해 `SupervisorJob` 기반의 `ApplicationScope` 제공
네트워크 연결 상태를 실시간으로 감지하기 위한 `NetworkMonitor` 인터페이스 및 구현체를 추가했습니다.

* `NetworkMonitor` 인터페이스 및 `ConnectivityManagerNetworkMonitor` 구현체 추가
* `DataModule`을 통한 `NetworkMonitor` 의존성 주입 설정
* `RepositoryModule` 위치 변경 (`com.team.prezel.core.data` -> `com.team.prezel.core.data.di`)
* `AndroidManifest.xml`에 `ACCESS_NETWORK_STATE` 권한 추가
Navigation3 라이브러리를 사용하여 앱의 네비게이션 구조를 설정하고, `HomeNavKey`를 통한 홈 화면 진입을 구현했습니다.

*   `PrezelAppState` 및 `rememberPrezelAppState`를 추가하여 네비게이션 상태와 네트워크 모니터링 관리
*   `PrezelApp` 컴포저블을 추가하여 `NavDisplay` 기반의 화면 전환 구조 정의
*   `MainActivity`에 Hilt를 적용하고 `PrezelApp`을 시작 화면으로 설정
*   `TopLevelNavItem`에 `TOP_LEVEL_KEYS` 및 `START_KEY` 정의
*   `HomeScreen`에 기본적인 UI 구성 요소 추가
*   `navigation3-ui` 의존성 및 `kotlinx-serialization` 플러그인 추가
디자인 시스템에 `PrezelNavigationBar` 및 `PrezelNavigationBarItem` 공통 컴포넌트를 추가했습니다.

*   `NavigationBar`를 기반으로 한 `PrezelNavigationBar` 구현
*   아이콘, 라벨, 선택 상태에 따른 스타일이 적용된 `PrezelNavigationBarItem` 구현
*   디자인 시스템 테마(PrezelTheme) 및 색상, 타이포그래피 적용
하단 네비게이션 바를 포함한 화면 구조를 쉽게 구성할 수 있도록 `PrezelNavigationScaffold`와 이를 위한 DSL 형태의 `PrezelNavigationScope`를 추가했습니다.

*   `PrezelNavigationScaffold` 컴포저블 추가
*   `PrezelNavigationScope`를 통한 네비게이션 아이템 선언 방식 도입
*   관련 프리뷰 코드 업데이트 및 Scaffold 적용 방식으로 수정
- `PrezelNavigationScaffold`를 도입하여 네비게이션 바 레이아웃을 적용했습니다.
- `NavDisplay`에 `entryProvider`를 사용하여 화면 전환 로직을 리팩토링했습니다.
- `NavigationState`에 현재 최상위 키를 반환하는 `currentTopLevelKeyOrStart` 확장 함수를 추가했습니다.
- `NavigationState`의 `currentSubStack` 및 `currentKey` 프로퍼티의 접근 제한을 완화했습니다.
Android Feature 및 Navigation 관련 모듈의 의존성 구조를 정리하고, 중복된 설정을 컨벤션 플러그인으로 통합했습니다.

*   `AndroidFeatureImplConventionPlugin`: `library.compose` 플러그인 적용 및 공통 네비게이션/디자인 시스템 의존성 추가
*   `AndroidFeatureApiConventionPlugin`: Navigation3 런타임 및 Serialization 플러그인 추가
*   `core:navigation`: Compose 라이브러리 플러그인 적용 및 불필요한 의존성 제거
*   `AndroidCompose`: Compose 관련 테스트 의존성(`runtimeTesting`)을 컨벤션 내부로 이동
*   기타 각 모듈별로 산재해 있던 의존성(Navigation3, Immutable collections 등) 정리 및 추가
네비게이션 관련 로직을 정리하고 `PrezelAppState`의 역할을 강화했습니다.

*   **네비게이션 구조 개선**:
    *   `TOP_LEVEL_NAV_ITEMS`를 `persistentMapOf`로 변경하고 `ImmutableSet`을 적용하여 안정성을 높였습니다.
    *   `currentTopLevelKeyOrStart` 확장 함수를 제거하고 `PrezelAppState` 내 프로퍼티로 로직을 이동했습니다.
    *   `Navigator` 생성을 `PrezelApp` 수준으로 이동하여 `PrezelAppState`를 단순화했습니다.
*   **AppState 강화**: `shouldShowNavigationBar`, `currentTopLevelKey` 등 UI 상태 결정 로직을 `PrezelAppState` 내부로 캡슐화했습니다.
*   **디자인 시스템**: `PrezelCheckbox`의 활성화 상태 아이콘 색상을 `interactiveRegular`에서 `feedbackGoodRegular`로 변경했습니다.
*   **기타**: `NavigationState`에서 불필요한 리스트 정렬 로직을 제거했습니다.
history 피처를 api와 impl 모듈로 분리하여 새롭게 추가했습니다.

*   `settings.gradle.kts`에 `:feature:history:api`, `:feature:history:impl` 모듈 등록
*   각 모듈의 `build.gradle.kts`, `AndroidManifest.xml`, ProGuard 설정 등 기본 구조 생성
*   `feature:history:impl`에서 `feature:history:api` 의존성 추가
*   `feature:profile:api` 모듈의 `AndroidManifest.xml` 추가 (빈 파일)
profile 기능을 위한 api 및 impl 모듈을 생성하고 프로젝트에 등록했습니다.

* profile api 및 impl 모듈 구조 생성
* 각 모듈별 build.gradle.kts 및 기본 설정 파일 추가
* settings.gradle.kts에 신규 모듈 등록
*   History 및 Profile 피처의 API/Impl 모듈 기본 구조(Screen, ViewModel, NavKey)를 추가했습니다.
*   `HomeEntryProvider`를 제거하고, Hilt Multi-bindings(`IntoSet`)를 활용한 `EntryBuilder` 방식을 도입하여 각 피처가 스스로 네비게이션 엔트리를 등록하도록 개선했습니다.
*   앱의 하단 탭 메뉴(`TOP_LEVEL_NAV_ITEMS`)에 History와 Profile을 활성화했습니다.
*   `MainActivity` 및 `PrezelApp`에서 주입된 `entryBuilders`를 사용하도록 수정했습니다.
- `TopLevelNavItem`의 히스토리 및 프로필 아이콘, 타이틀 리소스를 각 피처에 맞게 수정했습니다.
- 네비게이션 전환 시 발생하는 애니메이션(`transitionSpec`)을 비활성화했습니다.
- `PrezelIcons`에 `Storage` 아이콘을 추가했습니다.
- 피처별(home, history, profile) API 문자열 리소스를 추가 및 정리했습니다.
- `Navigator` 및 `NavigationState` 내 주요 메서드와 클래스 주석을 한글로 번역하여 가독성을 높였습니다.
- `PrezelNavigationBar`의 배경색을 `PrezelTheme.colors.bgRegular`로 명시적으로 설정했습니다.
- 프리뷰 및 내부 코드에서 사용되던 `PrezelIcons.Blank`를 `PrezelIcons.Storage`로 교체했습니다.
- `PrezelNavigationBar` 상단에 1dp 두께의 구분선(borderRegular)을 추가했습니다.
- 선택되지 않은 네비게이션 아이템의 아이콘 색상을 `iconDisabled`에서 `iconRegular`로 변경했습니다.
@HamBeomJoon HamBeomJoon self-assigned this Feb 6, 2026
@HamBeomJoon HamBeomJoon added the ✨ feat 새로운 기능 추가 또는 기존 기능 확장 label Feb 6, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

Walkthrough

이 PR은 Navigation3을 기반으로 한 앱 전역 네비게이션 시스템을 구축합니다. 홈, 히스토리, 프로필의 세 개 상단 레벨 화면을 정의하고, 이를 관리하기 위해 NavigationState 및 Navigator 클래스를 구현합니다. MainActivity를 의존성 주입으로 업데이트하고, PrezelApp 컴포저블과 PrezelAppState를 추가하여 네비게이션 상태를 관리합니다. 또한 네트워크 연결 상태를 추적하는 NetworkMonitor 인터페이스 및 구현체를 추가하고, PrezelNavigationBar와 PrezelNavigationScaffold 같은 UI 컴포넌트를 설계합니다.

Possibly related PRs

  • PR #5: MainActivity에 @AndroidEntryPoint 어노테이션 추가 및 네비게이션 와이어링으로 이전 PR에서 생성된 MainActivity를 직접 수정합니다.
  • PR #12: 이 PR에서 사용하는 NetworkMonitor 인터페이스와 DataModule을 core:data 모듈에 제공합니다.
  • PR #10: PrezelNavigationBar와 기타 UI 컴포넌트들이 의존하는 디자인 시스템 색상 및 테마를 정의합니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항(Navigation3 기반 앱 네비게이션 시스템 구축 및 Scaffold 기본 구조 구현)을 명확하게 설명하며, changeset의 핵심 목표와 완전히 일치합니다.
Description check ✅ Passed PR 설명이 저장소의 필수 템플릿 섹션(📌 작업 내용, 🧩 관련 이슈, 📸 스크린샷)을 모두 포함하고 있으며, 각 섹션이 상세하게 작성되었습니다.
Linked Issues check ✅ Passed 코드 변경사항이 연결된 이슈 #48의 모든 주요 목표(Navigation3 도입, 네비게이션 시스템 구축, Scaffold 기반 레이아웃, Navigation↔Scaffold 책임 분리)를 충족합니다.
Out of Scope Changes check ✅ Passed 모든 코드 변경사항이 이슈 #48 범위 내(Navigation3 의존성, 네비게이션 엔트리 포인트, Scaffold 레이아웃) 또는 이를 직접 지원하는 필수 변경(DI 설정, 디자인 시스템)으로 일관성 있게 범위 내입니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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
Contributor

@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: 5

🤖 Fix all issues with AI agents
In `@Prezel/app/build.gradle.kts`:
- Around line 30-33: The navigation3 dependencies
(implementation(libs.androidx.navigation3.runtime) and
implementation(libs.androidx.navigation3.ui)) should use the stable 1.0.0
release; open your version catalog (where libs.androidx.navigation3.runtime and
libs.androidx.navigation3.ui are defined), confirm their current versions, and
change them to "1.0.0" unless you explicitly need an alpha (e.g.,
1.1.0-alpha03). After updating the catalog entries, sync the Gradle project and
run a quick build to ensure no breaking changes; if any code depends on
alpha-only APIs, either keep the alpha with a comment explaining why or refactor
to work with 1.0.0.

In `@Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt`:
- Line 24: The Navigator instance is being remembered without a key so it won't
update when appState.navigationState changes; update the remember call that
creates Navigator to use appState.navigationState as its key (i.e., call
remember with appState.navigationState so Navigator(appState.navigationState) is
recreated on changes); target the remember usage that constructs Navigator and
ensure it observes appState.navigationState (related to rememberPrezelAppState)
so the Navigator no longer holds a stale navigationState reference.

In
`@Prezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureApiConventionPlugin.kt`:
- Around line 15-17: The dependency on androidx.navigation3.runtime in
AndroidFeatureApiConventionPlugin.kt is declared with "implementation", but the
feature API module exposes NavKey subclasses (HistoryNavKey, HomeNavKey,
ProfileNavKey) that inherit androidx.navigation3.runtime.NavKey, so the
dependency must be public; change the dependency declaration that uses
libs.findLibrary("androidx.navigation3.runtime").get() from "implementation" to
"api" in the dependencies block so consumers can resolve the NavKey type.

In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt`:
- Around line 69-91: The Icon's explicit tint in the Icon composable should be
removed so icon coloring is delegated to NavigationBarItemDefaults.colors; open
the Icon call (painterResource(iconRes), contentDescription = null, tint = ...)
in PrezelNavigationBar and delete the tint argument, and ensure
NavigationBarItemDefaults.colors(...) keeps the desired selectedIconColor and
unselectedIconColor values (adjust unselectedIconColor to
PrezelTheme.colors.iconRegular if you want the previous unselected look) so all
icon color decisions come from NavigationBarItemDefaults.colors rather than an
overriding tint.

In
`@Prezel/core/network/src/main/java/com/team/prezel/core/network/PrezelDisptachers.kt`:
- Line 1: 파일명이 잘못되어 있습니다: rename PrezelDisptachers.kt → PrezelDispatchers.kt; 실제
수정 작업은 파일을 git mv로 이동하여 히스토리를 유지하고 파일명 변경에 따라 프로젝트 전체의 모든 참조(imports,
Gradle/Kotlin build scripts, IDE run configurations, 및 다른 소스 파일에서 사용되는
클래스/objects)에서 "PrezelDisptachers"를 "PrezelDispatchers"로 업데이트하세요; 또한 파일 내
package 선언(com.team.prezel.core.network)은 그대로 두고 클래스/객체/파일레벨 심볼명이 파일명에 의존한다면 해당
심볼 이름도 일치시켜 주세요 (예: 파일에 있는 클래스/또는 object 이름이 PrezelDisptachers이면 이를
PrezelDispatchers로 변경).
🧹 Nitpick comments (12)
Prezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/internal/AndroidCompose.kt (1)

21-21: 모든 Compose 모듈에 lifecycle.runtimeTesting 테스트 의존성이 추가됩니다.

이 컨벤션 플러그인은 모든 Compose 모듈에 적용되므로, androidx.lifecycle.runtimeTesting이 실제로 필요하지 않은 모듈에도 androidTest 의존성으로 포함됩니다. 특정 모듈(예: app 또는 feature impl)에서만 필요한 경우, 해당 모듈의 build.gradle.kts에 직접 선언하는 것이 더 적절할 수 있습니다.

Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryUiState.kt (1)

3-7: Success 상태가 누락되어 있습니다.

현재 LoadingLoadFailed만 정의되어 있으며, 데이터를 성공적으로 로드한 경우를 나타내는 Success 상태가 없습니다. 초기 scaffold 구현으로 의도된 것이라면 괜찮지만, 실제 데이터를 표시할 때 추가가 필요합니다. HomeUiState, ProfileUiState에도 동일하게 적용됩니다.

💡 향후 추가 예시
 sealed interface HistoryUiState {
     data object Loading : HistoryUiState
 
     data object LoadFailed : HistoryUiState
+
+    data class Success(val data: HistoryData) : HistoryUiState
 }
Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/HomeUiState.kt (1)

3-7: Success 상태가 누락되어 있습니다.

현재 LoadingLoadFailed만 정의되어 있어 정상 데이터 로드 완료 시 사용할 상태가 없습니다. 초기 스캐폴딩 단계라면 괜찮지만, 추후 Success (또는 데이터를 담는) 상태 추가가 필요합니다.

Prezel/feature/profile/impl/src/main/java/com/team/prezel/feature/profile/impl/ProfileUiState.kt (1)

3-7: HomeUiState와 동일한 구조 — Success 상태 추가 고려

HomeUiState와 완전히 동일한 구조입니다. 마찬가지로 Success 상태가 누락되어 있으므로 추후 추가가 필요합니다. 여러 feature에서 Loading/LoadFailed 패턴이 반복된다면, core 모듈에 공통 base interface를 두는 것도 고려해 볼 수 있습니다.

Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/navigation/HistoryEntryBuilder.kt (1)

13-17: featureHistoryEntryBuilder()의 가시성을 internal로 제한하는 것을 고려해 주세요.

이 확장 함수는 동일 모듈 내 FeatureHistoryModule에서만 호출되므로, internal 키워드를 추가하면 모듈 외부로의 불필요한 노출을 방지할 수 있습니다. HomeEntryBuilder, ProfileEntryBuilder에도 동일하게 적용됩니다.

♻️ 제안
-fun EntryProviderScope<NavKey>.featureHistoryEntryBuilder() {
+internal fun EntryProviderScope<NavKey>.featureHistoryEntryBuilder() {
Prezel/app/src/main/java/com/team/prezel/navigation/TopLevelNavItem.kt (1)

21-34: HOME, HISTORY, PROFILE — top-level 변수 스코핑 개선을 고려해 주세요.

HOME, HISTORY, PROFILE은 매우 범용적인 이름의 top-level val입니다. 다른 모듈에서 import 시 이름 충돌이나 혼동이 발생할 수 있으므로, private으로 선언하거나 TopLevelNavItem의 companion object 내에 배치하는 것을 권장합니다. 외부에서는 TOP_LEVEL_NAV_ITEMS 맵을 통해 접근하면 충분합니다.

♻️ private으로 제한하는 예시
-val HOME = TopLevelNavItem(
+private val HOME = TopLevelNavItem(
     iconRes = PrezelIcons.Home,
     titleTextId = homeR.string.feature_home_api_title,
 )

-val HISTORY = TopLevelNavItem(
+private val HISTORY = TopLevelNavItem(
     iconRes = PrezelIcons.Storage,
     titleTextId = historyR.string.feature_history_api_title,
 )

-val PROFILE = TopLevelNavItem(
+private val PROFILE = TopLevelNavItem(
     iconRes = PrezelIcons.Profile,
     titleTextId = profileR.string.feature_profile_api_title,
 )
Prezel/core/data/src/main/java/com/team/prezel/core/data/di/DataModule.kt (1)

13-14: @Singleton 스코프 누락 고려

@Binds 메서드에 스코프 어노테이션이 없으므로, NetworkMonitor가 주입될 때마다 새로운 ConnectivityManagerNetworkMonitor 인스턴스가 생성됩니다. 각 인스턴스마다 별도의 NetworkCallback이 등록되므로, @Singleton을 추가하여 인스턴스를 재사용하는 것이 좋습니다.

♻️ 제안된 수정
+import javax.inject.Singleton
+
 `@Module`
 `@InstallIn`(SingletonComponent::class)
 abstract class DataModule {
+    `@Singleton`
     `@Binds`
     internal abstract fun bindsNetworkMonitor(networkMonitor: ConnectivityManagerNetworkMonitor): NetworkMonitor
 }
Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/NavigationState.kt (3)

67-84: toEntries()가 리컴포지션마다 새로운 SnapshotStateList를 생성합니다

toMutableStateList()는 매번 새 리스트 인스턴스를 반환합니다. NavHost가 참조 동등성(reference equality) 대신 키 기반으로 비교한다면 문제가 없겠지만, 불필요한 할당과 잠재적 리컴포지션을 유발할 수 있습니다. remember + derivedStateOf 등을 활용하여 리스트를 캐싱하는 것을 고려해 보세요.


69-80: mapValues 내에서 @Composable 함수 호출의 안정성 확인

rememberSaveableStateHolderNavEntryDecoratorrememberViewModelStoreNavEntryDecoratorsubStacks.mapValues 루프 안에서 호출됩니다. subStacks가 불변이므로 반복 횟수가 일정하여 현재는 안전하지만, 향후 동적 탭 추가/제거가 발생하면 Compose의 호출 순서 제약(positional memoization)을 위반할 수 있습니다.

이 점을 염두에 두고, 동적 변경이 필요해지면 key(navKey) 블록으로 감싸는 리팩토링을 고려해 주세요.


45-62: topLevelStack 또는 currentSubStack이 비어 있을 경우 크래시 발생 가능

currentTopLevelKey(Line 51)와 currentKey(Line 61) 모두 .last()를 사용하여 빈 리스트에서 NoSuchElementException을 던질 수 있습니다. rememberNavBackStack이 초기 키를 포함하므로 정상 흐름에서는 비어 있지 않겠지만, 방어적으로 lastOrNull() 사용 혹은 비어 있을 때의 폴백 처리를 고려해 보세요.

Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt (1)

183-185: "Nia" 접두사는 "Now in Android" 샘플 앱에서 온 것으로 보입니다. PrezelNavigationBarPreview로 이름을 변경하고 private으로 설정해 주세요.

♻️ 수정 제안
 `@ThemePreview`
 `@Composable`
-fun NiaNavigationBarPreview() {
+private fun PrezelNavigationBarPreview() {
Prezel/core/network/src/main/java/com/team/prezel/core/network/di/CoroutineScopeModule.kt (1)

15-17: ApplicationScope 한정자를 별도 파일로 분리하는 것을 고려해 주세요.

현재 DI 모듈 파일 내에 한정자 어노테이션이 정의되어 있어, 다른 모듈에서 이 한정자를 참조할 때 발견하기 어려울 수 있습니다. PrezelDispatchers.kt처럼 별도 파일로 분리하면 일관성이 향상됩니다.

프로젝트 내 여러 모듈의 `.gitignore` 및 `proguard-rules.pro` 파일의 개행 및 형식을 정리했습니다.
*   `libs.versions.toml`: `androidxActivity` 버전 변수를 통합하고 `navigation3` 관련 변수를 단일화했습니다.
*   `AndroidFeatureApiConventionPlugin`: `androidx.navigation3.runtime` 의존성을 `implementation`에서 `api`로 변경했습니다.
*   `PrezelNavigationBar`: `Icon`의 개별 `tint` 설정을 제거하고 `NavigationBarItemDefaults.colors`를 통해 색상을 관리하도록 수정했습니다.
*   `core:network`: 오타가 있던 파일명을 `PrezelDisptachers.kt`에서 `PrezelDispatchers.kt`로 변경했습니다.
Copy link
Contributor

@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: 2

🤖 Fix all issues with AI agents
In `@Prezel/gradle/libs.versions.toml`:
- Around line 77-80: Remove the unused dependency entries keyed as
"androidx-appcompat" and "material" from libs.versions.toml (these correspond to
androidx.appcompat:appcompat and com.google.android.material:material); delete
their lines/entries so they are no longer referenced by the build, but leave the
"androidx-navigation3-runtime" and "androidx-navigation3-ui" entries intact
since those are used by NavigationState.kt, PrezelApp.kt, and MainActivity.kt.
- Line 102: Remove the unnecessary android-library plugin declaration from
libs.versions.toml (the line defining android-library = { id =
"com.android.library", version.ref = "agp" }) because it’s not referenced
directly and all library modules use the convention plugin
prezel-android-library which applies com.android.library internally; delete that
entry and verify there are no remaining references to the symbol android-library
(and that Prezel/build.gradle.kts can drop any corresponding apply false) so the
version catalog contains only actually-used plugin entries.
🧹 Nitpick comments (1)
Prezel/gradle/libs.versions.toml (1)

79-80: Navigation3 라이브러리의 implementation vs api 사용에 주의하세요.

PR 커밋 메시지에서 일부 navigation3 의존성을 implementation에서 api로 변경했다고 언급되어 있습니다. navigation3-runtimenavigation3-uiapi로 노출할 경우, 이를 의존하는 모든 모듈에서 Navigation3 타입이 전이적으로 노출됩니다. 의도적으로 NavKey 등의 타입을 다른 모듈에서 참조해야 하는 경우에만 api를 사용하고, 그렇지 않으면 implementation으로 제한하는 것이 좋습니다.

* `PrezelApp`의 내비게이션 전환 효과에 100ms fade-in/out 애니메이션을 적용했습니다.
* `libs.versions.toml`에서 사용하지 않는 `appcompat` 및 `material` 의존성을 제거했습니다.
Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@Prezel/gradle/libs.versions.toml`:
- Around line 11-13: The libs.versions.toml adds androidxSavedStateCompose =
"1.4.0" and an androidx-savedstate-compose library entry but the codebase
doesn't reference it; either remove the androidxSavedStateCompose version and
the corresponding androidx-savedstate-compose library entry from
libs.versions.toml (and any unused alias in build files) or update code to
actually use the savedstate-compose artifact where intended (e.g., replace
usages that need saved-state support with the androidx-savedstate-compose
alias); ensure you edit the symbols androidxSavedStateCompose and the
androidx-savedstate-compose alias consistently across the toml and
build.gradle(.kts) files.
🧹 Nitpick comments (1)
Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt (1)

26-28: entryProvider가 매 리컴포지션마다 재생성됩니다.

entryProvider { ... }@Composable이 아닌 함수를 반환하는데, remember 없이 호출되면 매 리컴포지션마다 새 인스턴스가 생성됩니다. Hilt를 통해 주입되는 안정적인 entryBuilders Set을 기준으로 메모이제이션하면 불필요한 재생성을 방지할 수 있습니다. 이는 Android Navigation3 공식 가이드의 권장 패턴입니다.

♻️ 수정 제안
-    val entryProvider = entryProvider {
-        entryBuilders.forEach { builder -> this.builder() }
-    }
+    val entryProvider = remember(entryBuilders) {
+        entryProvider {
+            entryBuilders.forEach { builder -> this.builder() }
+        }
+    }

* `PrezelApp`의 내비게이션 전환 효과에 100ms fade-in/out 애니메이션을 적용했습니다.
* `libs.versions.toml`에서 사용하지 않는 `appcompat` 및 `material` 의존성을 제거했습니다.
Copy link
Contributor

@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

🤖 Fix all issues with AI agents
In `@Prezel/gradle/libs.versions.toml`:
- Line 24: The [versions] key "material" is declared but no library in the
catalog references it; remove the unused version key material = "1.13.0" from
the libs.versions.toml [versions] section and, before committing, search the
project for any remaining references to the material version key (if you find
references either restore the corresponding library entry or update those
references to the correct version key).
🧹 Nitpick comments (2)
Prezel/app/src/main/java/com/team/prezel/navigation/TopLevelNavItem.kt (2)

14-17: @param:DrawableRes 대신 @get:DrawableRes 사용 고려

@param:DrawableRes는 생성자 파라미터에만 적용되므로, 프로퍼티 접근 시(item.iconRes) Android Lint 검사가 동작하지 않습니다. @get:DrawableRes를 사용하면 getter에도 어노테이션이 적용되어 Lint가 올바르게 동작합니다.

제안
 data class TopLevelNavItem(
-    `@param`:DrawableRes val iconRes: Int,
-    `@param`:StringRes val titleTextId: Int,
+    `@DrawableRes` val iconRes: Int,
+    `@StringRes` val titleTextId: Int,
 )

19-32: HOME, HISTORY, PROFILE 상수의 가시성을 private으로 제한 권장

이 상수들은 TOP_LEVEL_NAV_ITEMS 맵 구성에만 사용되며, 매우 일반적인 이름이라 다른 파일에서 import 시 네이밍 충돌 가능성이 있습니다. private으로 선언하면 불필요한 외부 노출을 방지할 수 있습니다.

제안
-val HOME = TopLevelNavItem(
+private val HOME = TopLevelNavItem(
     iconRes = PrezelIcons.Home,
     titleTextId = R.string.bottom_nav_home,
 )

-val HISTORY = TopLevelNavItem(
+private val HISTORY = TopLevelNavItem(
     iconRes = PrezelIcons.Storage,
     titleTextId = R.string.bottom_nav_history,
 )

-val PROFILE = TopLevelNavItem(
+private val PROFILE = TopLevelNavItem(
     iconRes = PrezelIcons.Profile,
     titleTextId = R.string.bottom_nav_profile,
 )

- `AndroidFeatureApiConventionPlugin`에서 `androidx.navigation3.runtime` 의존성을 `api`에서 `implementation`으로 변경했습니다.
- `app/build.gradle.kts`에서 중복된 `androidx.navigation3.runtime` 의존성을 제거하고 코드를 정리했습니다.
* `PrezelAppState`의 불필요한 KDoc 주석 삭제
* `libs.versions.toml`에서 미사용 `material` 의존성 제거
`libs.versions.toml`과 `build.gradle.kts`에서 더 이상 사용되지 않는 `com.android.library` 플러그인 종속성을 제거합니다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 새로운 기능 추가 또는 기존 기능 확장

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Navigation3 기반 앱 네비게이션 시스템 구축 및 Scaffold 기본 구조 구현

2 participants