-
Notifications
You must be signed in to change notification settings - Fork 1
Navigation3 기반 앱 네비게이션 시스템 구축 및 Scaffold 기본 구조 구현 #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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`로 변경했습니다.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughNavigation3를 기반으로 앱 전역 네비게이션 시스템을 구축합니다. core/navigation 모듈을 신규 추가하여 NavigationState와 Navigator 클래스를 구현하고, 세 개의 기능 모듈(home, history, profile)을 API/구현 계층으로 분리합니다. MainActivity에 Hilt 의존성 주입을 적용하고 PrezelApp 및 PrezelAppState를 통해 앱 상태와 네비게이션을 중앙화합니다. NetworkMonitor 인터페이스와 ConnectivityManagerNetworkMonitor 구현체를 추가하여 네트워크 연결 상태를 추적합니다. 새로운 PrezelNavigationBar 및 PrezelNavigationScaffold 컴포넌트로 공통 UI 레이아웃을 구성하며, Dispatcher 및 ApplicationScope를 DI로 제공하여 비동기 작업을 지원합니다. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this 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상태가 누락되어 있습니다.현재
Loading과LoadFailed만 정의되어 있으며, 데이터를 성공적으로 로드한 경우를 나타내는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상태가 누락되어 있습니다.현재
Loading과LoadFailed만 정의되어 있어 정상 데이터 로드 완료 시 사용할 상태가 없습니다. 초기 스캐폴딩 단계라면 괜찮지만, 추후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-levelval입니다. 다른 모듈에서 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함수 호출의 안정성 확인
rememberSaveableStateHolderNavEntryDecorator와rememberViewModelStoreNavEntryDecorator가subStacks.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처럼 별도 파일로 분리하면 일관성이 향상됩니다.
| dependencies { | ||
| // "api"(project(":core:navigation")) | ||
| "implementation"(libs.findLibrary("androidx.navigation3.runtime").get()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find NavKey related files in the codebase
echo "=== Searching for NavKey definitions ==="
fd -i navkey -e kt -e java --type f
echo ""
echo "=== Searching for NavKey class/interface definitions ==="
rg -n "class NavKey|interface NavKey" --type=kt --type=java
echo ""
echo "=== Check feature API modules structure ==="
fd -type d "feature" | head -20Repository: Team-Prezel/Prezel-Android
Length of output: 701
🏁 Script executed:
echo "=== HistoryNavKey.kt content ==="
cat Prezel/feature/history/api/src/main/java/com/team/prezel/feature/history/api/HistoryNavKey.kt
echo ""
echo "=== HomeNavKey.kt content ==="
cat Prezel/feature/home/api/src/main/java/com/team/prezel/feature/home/api/HomeNavKey.kt
echo ""
echo "=== ProfileNavKey.kt content ==="
cat Prezel/feature/profile/api/src/main/java/com/team/prezel/feature/profile/api/ProfileNavKey.kt
echo ""
echo "=== Check all imports from androidx.navigation in feature API modules ==="
rg "import androidx.navigation" Prezel/feature/*/api/src --type-list | head -20Repository: Team-Prezel/Prezel-Android
Length of output: 1183
🏁 Script executed:
echo "=== Check what modules depend on feature API modules ==="
rg "implementation.*feature.*api|api.*feature.*api" Prezel --type gradle -A 2 -B 2 | head -30
echo ""
echo "=== Check gradle files in feature modules ==="
fd "build.gradle" Prezel/feature -A 5 | head -40Repository: Team-Prezel/Prezel-Android
Length of output: 421
Feature API 모듈에서 implementation 대신 api를 사용해야 합니다.
Feature API 모듈의 NavKey 클래스들(HistoryNavKey, HomeNavKey, ProfileNavKey)이 androidx.navigation3.runtime의 NavKey를 직접 상속하고 있습니다. 이는 NavKey가 public API 표면의 일부가 되므로, 이를 의존하는 모듈에서도 접근 가능해야 합니다. implementation 범위는 이 의존성을 비공개로 만들기 때문에, 다른 모듈에서 이 API를 사용할 때 NavKey 타입을 resolve할 수 없어 컴파일 에러가 발생합니다.
🔧 api로 변경 제안
dependencies {
- "implementation"(libs.findLibrary("androidx.navigation3.runtime").get())
+ "api"(libs.findLibrary("androidx.navigation3.runtime").get())
}📝 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.
| dependencies { | |
| // "api"(project(":core:navigation")) | |
| "implementation"(libs.findLibrary("androidx.navigation3.runtime").get()) | |
| } | |
| dependencies { | |
| "api"(libs.findLibrary("androidx.navigation3.runtime").get()) | |
| } |
🤖 Prompt for AI Agents
In
`@Prezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureApiConventionPlugin.kt`
around lines 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.
...esignsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt
Show resolved
Hide resolved
Prezel/core/network/src/main/java/com/team/prezel/core/network/PrezelDispatchers.kt
Show resolved
Hide resolved
프로젝트 내 여러 모듈의 `.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`로 변경했습니다.
There was a problem hiding this 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 라이브러리의implementationvsapi사용에 주의하세요.PR 커밋 메시지에서 일부 navigation3 의존성을
implementation에서api로 변경했다고 언급되어 있습니다.navigation3-runtime과navigation3-ui를api로 노출할 경우, 이를 의존하는 모든 모듈에서 Navigation3 타입이 전이적으로 노출됩니다. 의도적으로NavKey등의 타입을 다른 모듈에서 참조해야 하는 경우에만api를 사용하고, 그렇지 않으면implementation으로 제한하는 것이 좋습니다.
* `PrezelApp`의 내비게이션 전환 효과에 100ms fade-in/out 애니메이션을 적용했습니다. * `libs.versions.toml`에서 사용하지 않는 `appcompat` 및 `material` 의존성을 제거했습니다.
There was a problem hiding this 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를 통해 주입되는 안정적인entryBuildersSet을 기준으로 메모이제이션하면 불필요한 재생성을 방지할 수 있습니다. 이는 Android Navigation3 공식 가이드의 권장 패턴입니다.♻️ 수정 제안
- val entryProvider = entryProvider { - entryBuilders.forEach { builder -> this.builder() } - } + val entryProvider = remember(entryBuilders) { + entryProvider { + entryBuilders.forEach { builder -> this.builder() } + } + }
.../main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureApiConventionPlugin.kt
Outdated
Show resolved
Hide resolved
Prezel/feature/history/api/src/main/res/values/feature_history_api_strings.xml
Outdated
Show resolved
Hide resolved
* `PrezelApp`의 내비게이션 전환 효과에 100ms fade-in/out 애니메이션을 적용했습니다. * `libs.versions.toml`에서 사용하지 않는 `appcompat` 및 `material` 의존성을 제거했습니다.
There was a problem hiding this 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` 플러그인 종속성을 제거합니다.
- `LocalNavigator`, `LocalSnackbarHostState` staticCompositionLocal 추가 - `PrezelApp`에서 `CompositionLocalProvider`를 사용하여 하위 컴포넌트에 navigator와 snackbarHostState 주입 - `PrezelNavigationScaffold`에 `SnackbarHost` 추가 및 `snackbarHostState` 파라미터 요구하도록 변경
There was a problem hiding this 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/app/src/main/java/com/team/prezel/ui/PrezelApp.kt`:
- Around line 32-75: PrezelApp is too long; extract the heavy lambdas into
private composables: move the TOP_LEVEL_NAV_ITEMS iteration into a new
`@Composable` function (e.g., PrezelNavigationItems(navigator: Navigator,
currentTopLevelKey: NavKey, onNavigate: (NavKey)->Unit)) and move the NavDisplay
invocation and its transitionSpec/popTransitionSpec into another private
`@Composable` (e.g., PrezelNavDisplay(entries, onBack, modifier)); then replace
the navigationItems lambda passed to PrezelNavigationScaffold with a call to
PrezelNavigationItems and call PrezelNavDisplay inside the scaffold content
lambda, keeping the same parameters (snackbarHostState, navigator,
appState.navigationState.toEntries(entryProvider),
transitionSpec/popTransitionSpec) and reusing existing symbols PrezelApp,
PrezelNavigationScaffold, TOP_LEVEL_NAV_ITEMS, NavDisplay and Navigator.
In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt`:
- Around line 72-76: 네비게이션 아이콘의 contentDescription이 null로 설정되어 있어 접근성 문제가 있으므로,
PrezelNavigationBar의 icon lambda에서 contentDescription을 null 대신 해당 탭의 레이블을 제공하도록
변경하세요; 구체적으로 Icon(painter = painterResource(iconRes), contentDescription =
stringResource(labelTextId))처럼 labelTextId를 stringResource로 사용하여 스크린리더가 읽을 수 있게
만드세요.
🧹 Nitpick comments (1)
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt (1)
97-103: 필수 파라미터snackbarHostState가 선택적 파라미터 뒤에 위치합니다.Compose API 컨벤션에 따르면 기본값이 없는 필수 파라미터가 기본값이 있는 선택적 파라미터(
showNavigationBar) 앞에 와야 합니다. 호출부에서 named argument 없이 사용할 때 혼란을 줄 수 있습니다.♻️ 파라미터 순서 조정 제안
`@Composable` fun PrezelNavigationScaffold( navigationItems: `@Composable` PrezelNavigationScope.() -> Unit, + snackbarHostState: SnackbarHostState, modifier: Modifier = Modifier, showNavigationBar: Boolean = true, - snackbarHostState: SnackbarHostState, content: `@Composable` (PaddingValues) -> Unit, )
...esignsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt
Show resolved
Hide resolved
- `PrezelApp`의 UI 렌더링 부분을 `PrezelAppRoot` 컴포포저블로 분리했습니다. - `Navigator` 생성 시 `appState.navigationState`를 key로 사용하여 불필요한 재생성을 방지하도록 수정했습니다. - `entryProvider`를 별도 변수로 추출하여 가독성을 개선했습니다.
`PrezelApp`의 내부 구조를 정리하고 컴포저블 분리를 통해 가독성을 개선했습니다. * `PrezelAppRoot`를 `PrezelAppContent`로 이름을 변경하고 내부 로직을 이동했습니다. * `entryProvider` 생성 시 `remember`를 사용하여 불필요한 재계산을 방지하도록 수정했습니다. * `CompositionLocalProvider` 내에서 `LocalNavigator`와 `LocalSnackbarHostState`를 주입하고 하위 컴포저블에서 사용하도록 구조를 변경했습니다. * 불필요한 import문을 제거했습니다.
비어있던 `contentDescription`에 `labelTextId`를 활용한 `stringResource`를 할당하여 접근성을 개선했습니다.
| val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> { | ||
| error("SnackbarHostState is not provided") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이게 여기 있으면 feature들에서는 사용할 수 없을 것 같아요.
navigation 모듈로 이동 시켜야 할 것 같습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Navigator도 같이 이동시켰습니다
...ic/convention/src/main/java/com/team/prezel/buildlogic/convention/internal/AndroidCompose.kt
Show resolved
Hide resolved
...esignsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt
Outdated
Show resolved
Hide resolved
Prezel/gradle/libs.versions.toml
Outdated
| junitVersion = "1.3.0" | ||
| espressoCore = "3.7.0" | ||
| activityCompose = "1.12.2" | ||
| androidxActivity = "1.12.3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이름이 변경된 이유가 궁금해요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
이렇게 기존 androidx-lifecycle-xxx가 version.ref = "androidxLifecycle"로 되어있길래 똑같이 맞추고자 변경했습니다.
androidx-activity-compose여서 androidxActivity로 네이밍했습니다.
* `androidxActivity` 버전을 1.12.3에서 1.12.4로 업데이트했습니다. * 사용하지 않는 `androidx-activity-ktx` 의존성 설정을 삭제했습니다.
* 사용하지 않는 각 feature 모듈(history, home, profile)의 api 및 impl 내 AndroidManifest.xml 파일을 삭제했습니다. * core/navigation 모듈의 AndroidManifest.xml 파일을 한 줄로 정리했습니다.
`PrezelApp.kt`에서 정의되어 있던 `LocalNavigator`와 `LocalSnackbarHostState`를 `core:navigation` 모듈로 이동했습니다. * `PrezelApp.kt` 내 관련 정의 삭제 * `core:navigation` 모듈에 `LocalNavigator.kt`, `LocalSnackbarHostState.kt` 파일 생성 및 정의 이동
PrezelNavigationBarItem의 파라미터 타입을 리소스 ID(`Int`)에서 `String` 및 `IconSource`로 변경하여 유연성을 높였습니다. * `PrezelNavigationBarItem` 및 `PrezelNavigationBarScope.item`의 파라미터를 `@StringRes labelTextId: Int`에서 `label: String`으로, `@DrawableRes iconRes: Int`에서 `icon: IconSource`로 변경 * `IconSource` 인터페이스를 활용하여 아이콘 렌더링 및 콘텐츠 설명 로직 추상화 * 기존 리소스 ID 기반 호출부를 `DrawableIcon` 사용으로 수정 * 불필요한 `@StringRes`, `@DrawableRes` 어노테이션 및 import 제거
There was a problem hiding this 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/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt`:
- Around line 150-198: The PrezelNavigationScaffoldPreview function exceeds the
detekt LongMethod threshold; to fix, add a `@Suppress`("LongMethod") annotation
directly above the PrezelNavigationScaffoldPreview composable (or alternatively
extract the navigationItems block into a small helper function) so the preview
no longer triggers the rule; reference the PrezelNavigationScaffoldPreview
function and the navigationItems anonymous block when making the change.
🧹 Nitpick comments (3)
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt (2)
58-94:unselectedIconColor가iconDisabled로 설정되어 있습니다 — 의도 확인 필요.비선택 상태의 아이콘에
iconDisabled를 사용하면 실제disabled상태와 시각적으로 구분이 안 될 수 있습니다. 비선택(inactive) 상태라면iconRegular등 별도 색상이 더 적합할 수 있습니다. 의도된 디자인이라면 무시해 주세요.
3-3:android.Rimport는 Preview 전용이지만, 앱의R클래스와 혼동될 수 있습니다.
android.R은 Preview에서 placeholder 문자열용으로만 사용됩니다. 현재 문제는 아니지만, 향후 이 파일에 앱 리소스(com.team.prezel...R)를 추가할 경우 충돌/혼동이 발생할 수 있으므로 인지해 두시면 좋겠습니다.Prezel/gradle/libs.versions.toml (1)
24-24: Navigation31.0.1패치 버전으로 업데이트 권장.
navigation3 = "1.0.1"은1.0.0의 최신 패치 버전이며, NavigationEvent 1.0.2 의존성 업데이트를 통해 Android Studio Previews에서 NavDisplay 사용 시 발생하던 IllegalStateException을 수정합니다.♻️ 제안
-navigation3 = "1.0.0" +navigation3 = "1.0.1"
...esignsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelNavigationBar.kt
Outdated
Show resolved
Hide resolved
`PrezelNavigationScaffold` preview에서 사용하는 `navigationItems` 코드를 `PreviewNavigationItems` 함수로 분리하여 가독성을 개선했습니다.
Prezel/core/ui/src/main/java/com/team/prezel/core/ui/LocalSnackbarHostState.kt
Show resolved
Hide resolved
`IconSource`를 인터페이스에서 클래스로 변경하고, 기존 `DrawableIcon` 기능을 `IconSource` 생성자로 통합하였습니다. * `DrawableIcon` 제거 및 `IconSource` 생성자로 대체 * `IconSource`에 `Painter` 및 `DrawableRes`를 지원하는 생성자 추가 * 디자인 시스템 내 모든 컴포넌트(Button, Chip, Snackbar, NavigationBar 등)의 아이콘 호출 방식을 `IconSource`로 통일 * `PrezelNavigationBarItem` 등 일부 컴포넌트의 파라미터 순서 및 프리뷰 코드 최적화
빌드 로직을 컨벤션 플러그인으로 이동하여 모듈별 중복 설정을 제거하고 의존성 관리를 최적화했습니다. * `AndroidFeatureImplConventionPlugin`에 `isIncludeAndroidResources` 설정 추가 및 개별 모듈(`history`, `profile`, `home`) 내 중복 설정 제거 * 각 feature 모듈(`api`, `impl`) 및 `app` 모듈에서 불필요한 플러그인(`compose`, `serialization`) 선언 제거 * `core:navigation` 모듈의 불필요한 `AndroidManifest.xml` 삭제 * `libs.versions.toml` 내 변수명 정리 (`androidxActivity` -> `activityCompose`) 및 루트 `build.gradle.kts`에 `serialization` 플러그인 추가
* core-ui 모듈을 신규 생성하고 관련 설정을 추가했습니다. * `LocalSnackbarHostState`를 core-navigation 모듈에서 core-ui 모듈로 이동했습니다. * AndroidFeatureImplConventionPlugin 및 앱 의존성에 core-ui를 추가했습니다. * `libs.versions.toml`에 appcompat, material 등 필요한 라이브러리 및 플러그인 정의를 추가했습니다.
`libs.versions.toml` 및 `build.gradle.kts`에서 사용하지 않는 라이브러리와 플러그인 설정을 제거했습니다. * `appcompat`, `material` 라이브러리 의존성 제거 * `android-library` 플러그인 설정 제거
detekt의 `performance` 카테고리 중 `SpreadOperator` 룰을 비활성화합니다.
📌 작업 내용
📍 1. Navigation3 기반 핵심 네비게이션 시스템 추가
📍 2. Feature 모듈 분리 (API/Impl) 구조 도입
Home, History, Profile 피처를 각각 api/impl 모듈로 분리
모듈별 NavKey 및 기본 구현 추가
feature:home, feature:history, feature:profile 모듈들이 독립적으로 구성됨
📍 3. PrezelBottomNavigationBar 컴포넌트 추가
📍 4. App 구조 변경 (Compose + Hilt)
📍 5. 네트워크 상태 모니터링 기능 추가
📍 6. 빌드/디자인 시스템 변경
📍 7. 네비게이션 구조 리팩토링
🧩 관련 이슈
📸 스크린샷
📢 논의하고 싶은 내용
Summary by CodeRabbit
Release Notes
New Features
Refactor