Conversation
* `logo_prezel.xml`: 서비스 로고의 벡터 이미지 리소스를 추가하였습니다. (색상: `#0069ff`)
- 앱 실행 시 초기 화면의 레이아웃 미리보기를 방지하고 일관된 배경을 제공하기 위해 스플래시 테마를 추가하고 적용했습니다.
* **feat: SplashScreen 컴포저블 추가**
* 앱 시작 시 로고 애니메이션과 시작 버튼을 포함하는 스플래시 화면을 구현했습니다.
* `LaunchedEffect`와 `Animatable`을 사용하여 로고가 위로 이동하는 애니메이션을 적용했습니다.
* 애니메이션 종료 시점에 `AnimatedVisibility`(fadeIn)를 통해 하단 시작 버튼이 나타나도록 구현했습니다.
* `onSplashEnded` 콜백을 통해 버튼 클릭 시 다음 화면으로 전환되는 로직을 지원합니다.
* **style: 스플래시 관련 문자열 리소스 추가**
* 로고 접근성 설명을 위한 `splash_logo_description`와 버튼 텍스트인 `splash_start_button` 리소스를 추가했습니다.
`PrezelApp`에서 초기 실행 시 `SplashScreen`을 먼저 노출하고, 이후 메인 콘텐츠로 전환되는 로직을 구현했습니다. * `isSplashEnded` 상태를 `rememberSaveable`로 관리하여 스플래시 종료 여부를 추적합니다. * `isSplashEnded`가 `false`일 경우 `SplashScreen`을 렌더링하며, 애니메이션 종료 후 상태를 업데이트하여 `PrezelAppContent`로 진입하도록 개선했습니다.
* `libs.versions.toml`: `androidx.hilt.navigation.compose` 라이브러리 및 버전(`1.3.0`) 정의를 추가했습니다. * `AndroidFeatureImplConventionPlugin`: 모든 피처 모듈의 구현(`impl`) 플러그인에 `hilt-navigation-compose` 의존성을 기본으로 포함하도록 설정했습니다.
앱의 초기 진입점을 `SplashNavKey`로 변경하고, 화면 전환 시 공유 요소 애니메이션을 지원하기 위해 `SharedTransitionLayout`을 도입했습니다.
* **refactor: 앱 초기화 및 네비게이션 로직 수정**
* 앱의 시작 화면(`startKey`)을 `HomeNavKey`에서 `SplashNavKey`로 변경했습니다.
* `topLevelKeys` 구성에 `SplashNavKey`와 `LoginNavKey`를 추가하여 네비게이션 범위를 확장했습니다.
* `PrezelApp`에서 수동으로 관리하던 `isSplashEnded` 상태 로직을 제거하고, 네비게이션 시스템을 통해 스플래시 화면이 제어되도록 구조를 개선했습니다.
* **feat: SharedTransitionLayout 및 관련 Scope 도입**
* 애니메이션 최적화를 위해 `PrezelAppContent`를 `SharedTransitionLayout`으로 감싸고 `ProvideSharedTransitionScope`를 통해 하위 컴포저블에 공유 변환 컨텍스트를 제공합니다.
* `NavDisplay` 및 네비게이션 관련 컴포저블이 공유 요소 전환(Shared Element Transition)을 사용할 수 있는 기반을 마련했습니다.
* **style: 코드 정리 및 성능 최적화**
* `entryProvider` 생성 시 `navigator`에 대한 의존성을 추가하여 리샘플링을 방지했습니다.
* 불필요한 import 문과 사용되지 않는 상태 변수(`rememberSaveable`)를 정리했습니다.
`app` 모듈에 위치하던 로고 이미지 리소스를 `core:designsystem` 모듈로 이동하고, 디자인 시스템 리소스 명명 규칙에 맞춰 변경하였습니다. * `logo_prezel.xml` -> `core_designsystem_logo_prezel.xml` 로 이동 및 변경
`PrezelApp`의 전반적인 구조를 개선하여 공유 요소 전환(Shared Transition)을 위한 기반을 마련하고, 기존의 `SplashScreen` 로직을 정리했습니다.
* **feat: SharedTransitionLayout 및 관련 Scope 추가**
* `PrezelAppContent` 내부를 `SharedTransitionLayout`으로 감싸고 `ProvideSharedTransitionScope`를 통해 하위 컴포넌트에서 공유 요소 전환을 사용할 수 있도록 구조를 변경했습니다.
* `ExperimentalSharedTransitionApi` 옵트인 어노테이션을 추가했습니다.
* **refactor: SplashScreen 로직 제거**
* `PrezelApp` 컴포저블에서 `isSplashEnded` 상태와 `SplashScreen` 렌더링 로직을 제거하고, 즉시 `PrezelAppContent`가 표시되도록 수정했습니다.
* **refactor: entryProvider 생성 로직 최적화**
* `PrezelAppContent` 내에서 `provider` 생성 시 `navigator`를 `remember`의 키값으로 추가하여 의존성을 명확히 했습니다.
* 불필요한 `import` 문을 정리했습니다.
- 기존 `app` 모듈에 위치하던 Splash 로직을 별도 피처 모듈로 분리하고, Login 피처 모듈을 새롭게 추가하여 멀티 모듈 구조를 개선했습니다.
|
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:
Walkthrough앱에 Splash 및 Login 기능 모듈(API/impl)을 추가하고 앱 테마를 스플래시 전용 테마로 변경했습니다. PrezelApp 컴포지션에 SharedTransitionScope 제공자를 도입해 공유 전환 범위를 설정했으며, 네비게이션 코어에 LocalSharedTransitionScope/ProvideSharedTransitionScope, LoggingDecorator, NavigationState의 currentKey/isTopLevel, Navigator.replaceRoot 및 goBack 재구성을 추가했습니다. 또한 entryBuilders 컬렉션을 ImmutableSet으로 전환하고 DoubleBackToExitHandler, 새로운 로고 벡터, Snackbar API/구현 변경, Hilt viewmodel-compose 의존성 및 관련 빌드스크립트·버전 카탈로그와 settings.gradle.kts 모듈 등록 변경이 포함됩니다. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
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 Tip You can customize the tone of the review comments and chat replies.Configure the |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (10)
Prezel/core/designsystem/src/main/res/drawable/core_designsystem_logo_prezel.xml (1)
1-5: 벡터 드로어블 뷰포트 크기가 큽니다.현재 뷰포트 크기가 1017.5 x 437.7로 상당히 큰 편입니다. 공유 요소 전환 애니메이션 시 렌더링 성능에 영향을 줄 수 있습니다. 일반적으로 벡터 드로어블은 작은 뷰포트(예: 100 x 43)를 사용하고 필요에 따라 스케일업하는 것이 효율적입니다.
다만 디자인 도구에서 직접 내보낸 경우 이 값이 유지될 수 있으며, 실제 성능 문제가 없다면 현재 상태를 유지해도 무방합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/core/designsystem/src/main/res/drawable/core_designsystem_logo_prezel.xml` around lines 1 - 5, 벡터 드로어블의 큰 뷰포트(현재 android:viewportWidth="1017.5", android:viewportHeight="437.7")가 공유 요소 전환 성능에 영향을 줄 수 있으니 <vector> 태그의 android:viewportWidth/height 값을 더 작은 비율(예: 100 x 43)로 단축하고 각각의 path 좌표들도 동일한 비율로 스케일 조정해서 적용하거나, 디자인 도구(또는 SVG → VectorAsset 변환기)에서 작은 뷰포트로 다시 내보내도록 변경하세요; 대상 속성은 android:viewportWidth, android:viewportHeight, 그리고 width/height(dp) 속성(예: android:width/android:height)이며, 모든 path 관련 좌표와 strokeWidth를 동일 비율로 보정해야 합니다.Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashUiState.kt (1)
5-6:SplashUiState는 현재 구현체가 없는 빈 상태 계약입니다.검색 결과 이 타입의 구체적인 구현체가 없으며, ViewModel에서 StateFlow나 MutableStateFlow로 노출하지도 않습니다. 선언만 있고 실제 사용처가 없는 상태입니다.
만약 현재 상태 관리가 필요 없다면 이 타입을 제거하고 effect만 유지하는 것이 더 명확합니다. 향후 상태를 확장할 계획이 있다면 최소한 기본 구현체 하나는 함께 정의하여 타입 계약을 완성하는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashUiState.kt` around lines 5 - 6, The sealed interface SplashUiState is declared with no implementations and not exposed from the ViewModel; either remove SplashUiState entirely if you only need effects, or add a minimal concrete implementation (e.g., object Idle : SplashUiState) and update the Splash ViewModel to expose a StateFlow/MutableStateFlow<SplashUiState> (initialize with the default implementation) so the contract is complete and usable; modify references in SplashViewModel (or related classes) to use the new default state.Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/LocalNavigator.kt (1)
13-15:staticCompositionLocalOf사용 고려
LocalNavigator는staticCompositionLocalOf를 사용하고 있습니다.SharedTransitionScope도 한 번 설정되면 변경되지 않으므로, 일관성과 성능 최적화를 위해staticCompositionLocalOf를 사용하는 것이 더 적합할 수 있습니다.♻️ 제안된 변경
-val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope> { +val LocalSharedTransitionScope = staticCompositionLocalOf<SharedTransitionScope> { error("SharedTransitionScope is not provided") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/LocalNavigator.kt` around lines 13 - 15, The CompositionLocal for SharedTransitionScope is created with compositionLocalOf but should use staticCompositionLocalOf since SharedTransitionScope is set once and won't change; update the declaration of LocalSharedTransitionScope to use staticCompositionLocalOf<SharedTransitionScope> with the same error provider to improve consistency and performance (replace compositionLocalOf with staticCompositionLocalOf in the LocalSharedTransitionScope definition).Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.kt (2)
15-19:@Stable어노테이션이 ViewModel에 불필요합니다.
@Stable은 Compose 컴파일러에게 타입의 안정성을 알려주는 용도로, 주로 UI 상태 클래스나 Composable 파라미터에 사용됩니다. ViewModel은 Composable에 직접 전달되지 않고hiltViewModel()을 통해 주입되므로 이 어노테이션이 효과가 없습니다.♻️ 제안하는 수정
-@Stable `@HiltViewModel` class LoginViewModel `@Inject` constructor() : ViewModel() {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.kt` around lines 15 - 19, Remove the unnecessary `@Stable` annotation from the LoginViewModel declaration: the annotation appears directly above the LoginViewModel class (annotated with `@HiltViewModel` and the `@Inject` constructor) and should be deleted so the ViewModel is not incorrectly marked as a Compose stability marker.
26-31: 임시 구현 확인 - 실제 인증 로직 추가 필요.PR 목표에 명시된 대로 현재
login()은 임시 구현입니다. 실제 카카오 로그인 구현 시 다음 사항을 고려해주세요:
LoginUiState.Error또는LoginUiState.Failed상태 추가- 인증 실패 시
LoginUiEffect확장 (에러 메시지 표시 등)Loading상태에서 실제 인증 완료 후 네비게이션 처리카카오 로그인 구현을 추적하기 위해 새 이슈를 생성해 드릴까요?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.kt` around lines 26 - 31, 현재 LoginViewModel의 login()는 임시로 바로 NavigateToHome을 보내는 구현이므로 실제 카카오 인증 흐름을 구현하세요: LoginViewModel.login() 내부에서 카카오 인증 호출을 수행하고 인증 시작 시 _uiState.emit(LoginUiState.Loading)를 유지하며, 성공 시 _uiState.emit(LoginUiState.Success or appropriate) 후 _uiEffect.send(LoginUiEffect.NavigateToHome)를 트리거하고, 실패 시는 _uiState.emit(LoginUiState.Error or LoginUiState.Failed)와 함께 _uiEffect.send(LoginUiEffect.ShowError(message)) 같은 에러용 이펙트를 보내도록 추가하세요; 인증 로직과 에러 메시지 전달은 login(), _uiState, _uiEffect, LoginUiState.*, LoginUiEffect.* 식별자를 수정하면 됩니다.Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/decorator/LoggingDecorator.kt (1)
29-31:metadata로깅 시 민감한 정보 노출 가능성 검토.
metadata에 사용자 식별 정보나 민감한 데이터가 포함될 경우 로그에 노출될 수 있습니다. 현재 NavKey 메타데이터에 민감한 정보가 없다면 괜찮지만, 향후 확장 시 주의가 필요합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/decorator/LoggingDecorator.kt` around lines 29 - 31, enterLog()와 exitLog()가 NavEntry.metadata를 그대로 로그에 찍어 민감정보 유출 위험이 있으니 NavEntry<T>.enterLog()/exitLog()에서 metadata를 직접 출력하지 말고 민감값을 마스킹하거나 허용된 키만 로그에 포함하는 sanitizeMetadata/scrubMetadata 유틸을 호출하도록 변경하세요; 예를 들어 LoggingDecorator의 enterLog/exitLog 구현을 바꿔 contentKey는 그대로 남기되 metadata는 sanitizeMetadata(metadata)로 변환해 반환하고, sanitizeMetadata는 허용된 키(또는 키 화이트리스트)를 사용해 안전한 값만 노출하거나 값 길이/패턴 기반으로 마스킹하도록 구현해 신규 민감 필드가 들어와도 로그에 노출되지 않게 하세요.Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt (4)
66-66: 접근성: 로고 이미지에contentDescription추가 권장로고 이미지에
contentDescription = null이 설정되어 있습니다. 순수 장식용 이미지라면 괜찮지만, 앱 로고는 스크린 리더 사용자에게 의미 있는 정보를 제공하는 것이 좋습니다.♻️ 접근성 개선 제안
- contentDescription = null, + contentDescription = "Prezel 로고",또는
stringResource를 사용하여 지역화된 문자열을 제공할 수 있습니다.Also applies to: 91-91
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt` at line 66, SplashScreen.kt currently sets the Image composable's contentDescription to null (decorative) for the app logo; change this to a meaningful, localizable description by replacing contentDescription = null with contentDescription = stringResource(R.string.splash_logo_description) (or an appropriate string resource) in the Image usages in SplashScreen (look for the Image call around the contentDescription at lines ~66 and ~91) so screen readers receive a useful, localized label for the app logo.
38-45:uiEffect수집 시 ViewModel 참조 변경 대응 고려
LaunchedEffect(Unit)을 사용하여uiEffect를 수집하고 있습니다.viewModel인스턴스가 변경될 경우(일반적인 Hilt 사용 시에는 드물지만) effect가 재시작되지 않습니다. key에viewModel을 추가하면 더 안전합니다.♻️ 개선 제안
- LaunchedEffect(Unit) { + LaunchedEffect(viewModel) { viewModel.uiEffect.collect { effect ->🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt` around lines 38 - 45, The LaunchedEffect currently uses Unit as the key so if the viewModel instance changes the collection of viewModel.uiEffect won't restart; change the key to include the viewModel (e.g., LaunchedEffect(viewModel) { ... }) so the block restarts when the ViewModel reference changes and continue collecting uiEffect (the block that calls navigateToHome() / navigateToLogin() should remain the same).
56-67: Box/Image 구조 중복 코드공개
SplashScreen(lines 51-68)과 private 오버로드(lines 76-93)에서 동일한 Box/Image UI 구조가 반복됩니다. private 함수에서 공개 함수를 호출하거나, 공통 UI를 별도 composable로 추출하면 유지보수성이 향상됩니다.♻️ 공통 UI 추출 예시
`@Composable` private fun SharedTransitionScope.SplashContent( animatedVisibilityScope: AnimatedVisibilityScope, modifier: Modifier = Modifier, ) { Box( modifier = modifier .fillMaxSize() .background(PrezelTheme.colors.bgRegular), ) { Image( modifier = Modifier .align(Alignment.Center) .fillMaxWidth() .padding(horizontal = 95.dp) .sharedElement( sharedContentState = rememberSharedContentState(key = AUTH_LOGO_SHARED_ELEMENT_KEY), animatedVisibilityScope = animatedVisibilityScope, ), painter = painterResource(DSR.drawable.core_designsystem_logo_prezel), contentDescription = null, ) } }그런 다음 두
SplashScreen함수에서SplashContent를 호출합니다.Also applies to: 76-93
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt` around lines 56 - 67, The public SplashScreen and the private SplashScreen overload duplicate the same Box/Image UI; extract that shared UI into a single `@Composable` (for example private fun SharedTransitionScope.SplashContent or SplashContent) that accepts the AnimatedVisibilityScope/animatedVisibilityScope and an optional Modifier, move the Box + Image (including rememberSharedContentState key = AUTH_LOGO_SHARED_ELEMENT_KEY and painterResource(DSR.drawable.core_designsystem_logo_prezel)) into it, then have both SplashScreen functions call this new composable to remove the duplication and keep behavior identical.
60-60: 매직 넘버95.dp상수 추출 고려
95.dp값이 두 곳에서 사용됩니다. 디자인 시스템의 spacing 상수로 추출하거나, 파일 상단에 private val로 정의하면 일관성 유지와 수정이 용이해집니다.Also applies to: 85-85
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt` at line 60, The hard-coded 95.dp horizontal padding used in SplashScreen (seen as .padding(horizontal = 95.dp) and a second identical usage) should be extracted to a named constant to avoid magic numbers; add a private val (e.g., SplashHorizontalPadding = 95.dp) at the top of SplashScreen.kt or use the design system spacing constant, then replace both .padding(horizontal = 95.dp) occurrences with .padding(horizontal = SplashHorizontalPadding) to keep consistency and simplify future changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Prezel/app/src/main/res/values/themes.xml`:
- Around line 5-8: Theme.PrezelSplashScreen의 android:windowBackground가 고정된 흰색으로
설정되어 있어 다크 모드에서 SplashScreen 컴포저블(PrezelTheme.colors.bgRegular, dark: `#232529`)과
불일치해 깜빡임이 발생합니다; Theme.PrezelSplashScreen에서 <item
name="android:windowBackground">를 제거하거나(시스템 기본 사용) 다크/라이트에 따라 동적으로 설정되도록 수정하여
windowBackground가 PrezelTheme.colors.bgRegular와 일치하도록 변경하세요 (수정 대상:
Theme.PrezelSplashScreen의 windowBackground 설정 및 SplashScreen 관련 테마 구성).
In
`@Prezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureImplConventionPlugin.kt`:
- Line 27: The convention plugin is forcing the deprecated artifact; update
libs.versions.toml to add the new artifact coordinate for
androidx.hilt:hilt-lifecycle-viewmodel-compose (Navigation 3.0 migration) and
replace the forced dependency in AndroidFeatureImplConventionPlugin (the line
calling libs.findLibrary("androidx.hilt.navigation.compose").get()) to reference
the new library key, or remove the automatic injection so only modules that need
hilt-lifecycle-viewmodel-compose add it explicitly; ensure the plugin no longer
injects the old "androidx.hilt.navigation.compose" artifact and that modules
like SplashScreen/LoginScreen opt-in to the new artifact.
In
`@Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/Navigator.kt`:
- Around line 30-37: replaceRoot currently only clears state.topLevelStack and
trims the target key's subStack, leaving other tabs' subStacks intact; change
replaceRoot to (1) validate that the given key is a top-level key (use
state.topLevelKeys and reject/ignore otherwise), (2) set state.topLevelStack to
contain only the given key, and (3) iterate all entries in state.subStacks and
for each sub-stack keep only its first/root element (clear subList(1, size) or
replace with a single-element list) so every tab's history is reset to root.
Reference: replaceRoot, state.topLevelStack, state.topLevelKeys,
state.subStacks.
In
`@Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt`:
- Around line 42-72: The file uses ExperimentalSharedTransitionApi symbols
(SharedTransitionScope, SharedTransitionLayout, sharedElement,
rememberSharedContentState) but lacks an opt-in; fix by adding an opt-in for
ExperimentalSharedTransitionApi — e.g., add
`@file`:OptIn(ExperimentalSharedTransitionApi::class) at the top of the file or
annotate the composables that use SharedTransitionScope/LoginScreen with
`@OptIn`(ExperimentalSharedTransitionApi::class) so the compiler accepts usages of
those experimental APIs.
In
`@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/navigation/SplashEntryBuilder.kt`:
- Around line 25-30: The current SplashEntryBuilder uses
navigator.navigate(LoginNavKey) for navigateToLogin which leaves Splash in back
stack; change the handler to call navigator.replaceRoot(LoginNavKey) instead
(i.e., replace the call inside the navigateToLogin lambda) so the Splash is
removed from history; update the lambda named navigateToLogin referencing
LoginNavKey to use replaceRoot rather than navigate.
In
`@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt`:
- Line 20: Import for hiltViewModel in SplashScreen.kt is incorrect; replace the
wrong import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel with the
correct one from the navigation-compose artifact:
androidx.hilt.navigation.compose.hiltViewModel so that references to
hiltViewModel in the SplashScreen composable resolve correctly.
---
Nitpick comments:
In
`@Prezel/core/designsystem/src/main/res/drawable/core_designsystem_logo_prezel.xml`:
- Around line 1-5: 벡터 드로어블의 큰 뷰포트(현재 android:viewportWidth="1017.5",
android:viewportHeight="437.7")가 공유 요소 전환 성능에 영향을 줄 수 있으니 <vector> 태그의
android:viewportWidth/height 값을 더 작은 비율(예: 100 x 43)로 단축하고 각각의 path 좌표들도 동일한 비율로
스케일 조정해서 적용하거나, 디자인 도구(또는 SVG → VectorAsset 변환기)에서 작은 뷰포트로 다시 내보내도록 변경하세요; 대상
속성은 android:viewportWidth, android:viewportHeight, 그리고 width/height(dp) 속성(예:
android:width/android:height)이며, 모든 path 관련 좌표와 strokeWidth를 동일 비율로 보정해야 합니다.
In
`@Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/decorator/LoggingDecorator.kt`:
- Around line 29-31: enterLog()와 exitLog()가 NavEntry.metadata를 그대로 로그에 찍어 민감정보
유출 위험이 있으니 NavEntry<T>.enterLog()/exitLog()에서 metadata를 직접 출력하지 말고 민감값을 마스킹하거나
허용된 키만 로그에 포함하는 sanitizeMetadata/scrubMetadata 유틸을 호출하도록 변경하세요; 예를 들어
LoggingDecorator의 enterLog/exitLog 구현을 바꿔 contentKey는 그대로 남기되 metadata는
sanitizeMetadata(metadata)로 변환해 반환하고, sanitizeMetadata는 허용된 키(또는 키 화이트리스트)를 사용해
안전한 값만 노출하거나 값 길이/패턴 기반으로 마스킹하도록 구현해 신규 민감 필드가 들어와도 로그에 노출되지 않게 하세요.
In
`@Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/LocalNavigator.kt`:
- Around line 13-15: The CompositionLocal for SharedTransitionScope is created
with compositionLocalOf but should use staticCompositionLocalOf since
SharedTransitionScope is set once and won't change; update the declaration of
LocalSharedTransitionScope to use
staticCompositionLocalOf<SharedTransitionScope> with the same error provider to
improve consistency and performance (replace compositionLocalOf with
staticCompositionLocalOf in the LocalSharedTransitionScope definition).
In
`@Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.kt`:
- Around line 15-19: Remove the unnecessary `@Stable` annotation from the
LoginViewModel declaration: the annotation appears directly above the
LoginViewModel class (annotated with `@HiltViewModel` and the `@Inject` constructor)
and should be deleted so the ViewModel is not incorrectly marked as a Compose
stability marker.
- Around line 26-31: 현재 LoginViewModel의 login()는 임시로 바로 NavigateToHome을 보내는
구현이므로 실제 카카오 인증 흐름을 구현하세요: LoginViewModel.login() 내부에서 카카오 인증 호출을 수행하고 인증 시작 시
_uiState.emit(LoginUiState.Loading)를 유지하며, 성공 시
_uiState.emit(LoginUiState.Success or appropriate) 후
_uiEffect.send(LoginUiEffect.NavigateToHome)를 트리거하고, 실패 시는
_uiState.emit(LoginUiState.Error or LoginUiState.Failed)와 함께
_uiEffect.send(LoginUiEffect.ShowError(message)) 같은 에러용 이펙트를 보내도록 추가하세요; 인증 로직과
에러 메시지 전달은 login(), _uiState, _uiEffect, LoginUiState.*, LoginUiEffect.* 식별자를
수정하면 됩니다.
In
`@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt`:
- Line 66: SplashScreen.kt currently sets the Image composable's
contentDescription to null (decorative) for the app logo; change this to a
meaningful, localizable description by replacing contentDescription = null with
contentDescription = stringResource(R.string.splash_logo_description) (or an
appropriate string resource) in the Image usages in SplashScreen (look for the
Image call around the contentDescription at lines ~66 and ~91) so screen readers
receive a useful, localized label for the app logo.
- Around line 38-45: The LaunchedEffect currently uses Unit as the key so if the
viewModel instance changes the collection of viewModel.uiEffect won't restart;
change the key to include the viewModel (e.g., LaunchedEffect(viewModel) { ...
}) so the block restarts when the ViewModel reference changes and continue
collecting uiEffect (the block that calls navigateToHome() / navigateToLogin()
should remain the same).
- Around line 56-67: The public SplashScreen and the private SplashScreen
overload duplicate the same Box/Image UI; extract that shared UI into a single
`@Composable` (for example private fun SharedTransitionScope.SplashContent or
SplashContent) that accepts the AnimatedVisibilityScope/animatedVisibilityScope
and an optional Modifier, move the Box + Image (including
rememberSharedContentState key = AUTH_LOGO_SHARED_ELEMENT_KEY and
painterResource(DSR.drawable.core_designsystem_logo_prezel)) into it, then have
both SplashScreen functions call this new composable to remove the duplication
and keep behavior identical.
- Line 60: The hard-coded 95.dp horizontal padding used in SplashScreen (seen as
.padding(horizontal = 95.dp) and a second identical usage) should be extracted
to a named constant to avoid magic numbers; add a private val (e.g.,
SplashHorizontalPadding = 95.dp) at the top of SplashScreen.kt or use the design
system spacing constant, then replace both .padding(horizontal = 95.dp)
occurrences with .padding(horizontal = SplashHorizontalPadding) to keep
consistency and simplify future changes.
In
`@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashUiState.kt`:
- Around line 5-6: The sealed interface SplashUiState is declared with no
implementations and not exposed from the ViewModel; either remove SplashUiState
entirely if you only need effects, or add a minimal concrete implementation
(e.g., object Idle : SplashUiState) and update the Splash ViewModel to expose a
StateFlow/MutableStateFlow<SplashUiState> (initialize with the default
implementation) so the contract is complete and usable; modify references in
SplashViewModel (or related classes) to use the new default state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ba934a55-8ed4-41a7-a184-79d8cabe5937
📒 Files selected for processing (50)
Prezel/app/build.gradle.ktsPrezel/app/src/main/AndroidManifest.xmlPrezel/app/src/main/java/com/team/prezel/ui/PrezelApp.ktPrezel/app/src/main/java/com/team/prezel/ui/PrezelAppState.ktPrezel/app/src/main/res/values/themes.xmlPrezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureImplConventionPlugin.ktPrezel/core/designsystem/src/main/res/drawable/core_designsystem_logo_prezel.xmlPrezel/core/navigation/build.gradle.ktsPrezel/core/navigation/src/main/java/com/team/prezel/core/navigation/LocalNavigator.ktPrezel/core/navigation/src/main/java/com/team/prezel/core/navigation/NavigationState.ktPrezel/core/navigation/src/main/java/com/team/prezel/core/navigation/Navigator.ktPrezel/core/navigation/src/main/java/com/team/prezel/core/navigation/decorator/LoggingDecorator.ktPrezel/feature/login/api/.gitignorePrezel/feature/login/api/build.gradle.ktsPrezel/feature/login/api/consumer-rules.proPrezel/feature/login/api/proguard-rules.proPrezel/feature/login/api/src/androidTest/java/com/team/prezel/feature/login/api/.gitkeepPrezel/feature/login/api/src/main/java/com/team/prezel/feature/login/api/LoginNavKey.ktPrezel/feature/login/api/src/test/java/com/team/prezel/feature/login/api/.gitkeepPrezel/feature/login/impl/.gitignorePrezel/feature/login/impl/build.gradle.ktsPrezel/feature/login/impl/consumer-rules.proPrezel/feature/login/impl/proguard-rules.proPrezel/feature/login/impl/src/androidTest/java/com/team/prezel/feature/login/impl/.gitkeepPrezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.ktPrezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/navigation/LoginEntryBuilder.ktPrezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginUiEffect.ktPrezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginUiState.ktPrezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.ktPrezel/feature/login/impl/src/test/java/com/team/prezel/feature/login/impl/.gitkeepPrezel/feature/splash/api/.gitignorePrezel/feature/splash/api/build.gradle.ktsPrezel/feature/splash/api/consumer-rules.proPrezel/feature/splash/api/proguard-rules.proPrezel/feature/splash/api/src/androidTest/java/com/team/prezel/feature/splash/api/.gitkeepPrezel/feature/splash/api/src/main/java/com/team/prezel/feature/splash/api/SplashNavKey.ktPrezel/feature/splash/api/src/test/java/com/team/prezel/feature/splash/api/.gitkeepPrezel/feature/splash/impl/.gitignorePrezel/feature/splash/impl/build.gradle.ktsPrezel/feature/splash/impl/consumer-rules.proPrezel/feature/splash/impl/proguard-rules.proPrezel/feature/splash/impl/src/androidTest/java/com/team/prezel/feature/splash/impl/.gitkeepPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/navigation/SplashEntryBuilder.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashUiEffect.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashUiState.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashViewModel.ktPrezel/feature/splash/impl/src/test/java/com/team/prezel/feature/splash/impl/.gitkeepPrezel/gradle/libs.versions.tomlPrezel/settings.gradle.kts
...main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureImplConventionPlugin.kt
Outdated
Show resolved
Hide resolved
Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/Navigator.kt
Show resolved
Hide resolved
Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt
Show resolved
Hide resolved
...lash/impl/src/main/java/com/team/prezel/feature/splash/impl/navigation/SplashEntryBuilder.kt
Show resolved
Hide resolved
Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.kt
Show resolved
Hide resolved
- `LoginScreen`의 최상위 컨테이너를 `Box`에서 `Column`으로 변경하고, 내부 컴포넌트들의 배치 로직을 개선하였습니다.
* refactor: AndroidFeatureImplConventionPlugin 의존성 수정
* `AndroidFeatureImplConventionPlugin`에서 사용되던 `androidx.hilt.navigation.compose` 라이브러리를 `androidx.hilt.lifecycle.viewmodel.compose`로 교체하였습니다.
* chore: libs.versions.toml 라이브러리 선언 변경
* `androidx-hilt-navigation-compose`를 `androidx-hilt-lifecycle-viewmodel-compose`로 변경하여 Hilt ViewModel 관련 의존성을 최신화했습니다.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt (1)
138-142: 버튼 라벨은 문자열 리소스로 분리하는 편이 좋겠습니다.
"시작하기"가 코드에 하드코딩되어 있어 문구 변경이나 다국어 대응 때 이 파일을 다시 수정해야 합니다.stringResource(...)로 빼 두면 feature 모듈 분리 방향과도 더 잘 맞습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt` around lines 138 - 142, The "시작하기" label is hardcoded in the PrezelButton inside LoginScreen (PrezelButton(modifier = Modifier.fillMaxWidth(), text = "시작하기", onClick = onLogin)); change it to use stringResource(...) instead (e.g. text = stringResource(R.string.your_start_label)) and add the corresponding entry in the feature module's strings.xml (e.g. <string name="your_start_label">시작하기</string>) so the label is localized and not hardcoded in code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt`:
- Around line 138-142: The "시작하기" label is hardcoded in the PrezelButton inside
LoginScreen (PrezelButton(modifier = Modifier.fillMaxWidth(), text = "시작하기",
onClick = onLogin)); change it to use stringResource(...) instead (e.g. text =
stringResource(R.string.your_start_label)) and add the corresponding entry in
the feature module's strings.xml (e.g. <string
name="your_start_label">시작하기</string>) so the label is localized and not
hardcoded in code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d45eaad8-3865-4184-851c-284092e553e2
📒 Files selected for processing (1)
Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt
`Navigator`의 `replaceRoot` 함수에서 루트 교체 시의 제약 조건을 추가하고, 모든 서브 스택을 초기화하도록 로직을 개선하였습니다. * `replaceRoot` 호출 시 전달된 키가 `topLevelKeys`에 포함되는지 확인하는 `require` 문을 추가하여 안정성을 높였습니다. * 특정 키의 서브 스택만 정리하던 로직을 모든 서브 스택(`state.subStacks.values`)을 순회하며 루트를 제외한 하위 히스토리를 삭제하도록 변경했습니다.
`PrezelApp.kt`에서 더 이상 사용되지 않는 `ExperimentalSharedTransitionApi` 임포트와 `@OptIn` 어노테이션을 삭제하였습니다.
스플래시 화면의 로그인 상태 확인 시점을 변경하고 네비게이션 동작을 최적화했습니다.
* **refactor: SplashViewModel 로그인 상태 체크 로직 수정**
* `checkLoginStatus` 함수를 `private`으로 변경하고 `init` 블록에서 호출하도록 수정하여 ViewModel 생성 시점에 로직이 실행되도록 변경했습니다.
* 클래스 선언부의 코드 포맷을 정리했습니다.
* **refactor: SplashScreen 생명주기 의존성 제거**
* `LifecycleEventEffect`를 사용해 `ON_RESUME` 시점에 호출하던 `viewModel.checkLoginStatus()` 로직을 제거했습니다. (ViewModel 내부에서 처리되도록 변경)
* 불필요한 import 문을 정리했습니다.
* **refactor: 로그인 화면 이동 시 replaceRoot 적용**
* `navigateToLogin` 호출 시 `navigator.navigate` 대신 `navigator.replaceRoot`를 사용하도록 수정하여 스플래시 화면이 백스택에 남지 않도록 개선했습니다.
`LocalSharedTransitionScope`의 정의를 `compositionLocalOf`에서 `staticCompositionLocalOf`로 수정하였습니다. 해당 값은 빈번하게 변경되지 않으므로, 정적 구성을 통해 불필요한 추적 오버헤드를 줄이고 성능을 최적화했습니다.
`SplashScreen`의 내부 UI 구현 코드를 별도의 `SplashScreen` 컴포저블로 추출하여 책임을 분리하고 가독성을 개선했습니다. * `SplashScreen` 내부의 `Box` 및 `Image` 레이아웃 로직을 분리된 컴포저블로 이동 * `sharedElement` 및 `animatedVisibilityScope`를 통한 로고 애니메이션 구조 유지
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@Prezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashViewModel.kt`:
- Around line 19-25: The splash is navigating immediately because
checkLoginStatus() sends SplashUiEffect.NavigateToLogin right away; modify
SplashViewModel.checkLoginStatus to suspend briefly before sending the effect
(e.g., call kotlinx.coroutines.delay with the desired 400ms) so the splash is
shown for the required time, i.e., change viewModelScope.launch {
_uiEffect.send(SplashUiEffect.NavigateToLogin) } to viewModelScope.launch {
delay(400L); _uiEffect.send(SplashUiEffect.NavigateToLogin) } ensuring the delay
import is added and the coroutine scope remains viewModelScope.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 51bd1e8f-6759-4627-93c5-57c429a5e7ed
📒 Files selected for processing (9)
Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.ktPrezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureImplConventionPlugin.ktPrezel/core/navigation/src/main/java/com/team/prezel/core/navigation/LocalNavigator.ktPrezel/core/navigation/src/main/java/com/team/prezel/core/navigation/Navigator.ktPrezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/SplashScreen.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/navigation/SplashEntryBuilder.ktPrezel/feature/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashViewModel.ktPrezel/gradle/libs.versions.toml
🚧 Files skipped from review as they are similar to previous changes (3)
- Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/Navigator.kt
- Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/viewModel/LoginViewModel.kt
- Prezel/build-logic/convention/src/main/java/com/team/prezel/buildlogic/convention/plugin/AndroidFeatureImplConventionPlugin.kt
...e/splash/impl/src/main/java/com/team/prezel/feature/splash/impl/viewModel/SplashViewModel.kt
Show resolved
Hide resolved
* **docs: Navigator 클래스 내 내비게이션 정책 주석 추가**
* `Navigator` 클래스와 주요 메서드(`navigate`, `replaceRoot`, `pop`, `goToKey` 등)에 상세 동작 정책 및 파라미터 설명을 추가했습니다.
* 최상위 키(top-level)와 하위 스택(sub-stack) 관리 로직에 대한 설명을 보강하여 코드 가독성을 높였습니다.
* **refactor: LoggingDecorator 구현 단순화**
* `NavEntryDecorator` 생성 시 사용되던 불필요한 `onPop` 콜백과 확장 함수(`enterLog`, `exitLog`)를 제거했습니다.
* `Timber` 로거 주입 방식을 내부 직접 호출 방식으로 변경하고, 로그 포맷을 일관되게 정리했습니다.
* `DisposableEffect`를 통해 화면 진입(`SCREEN_ENTER`) 및 이탈(`SCREEN_EXIT`) 시점의 로그를 출력하도록 개선했습니다.
- 앱의 전반적인 네비게이션 구조를 명확히 하기 위해 내비게이션 키와 아이템의 명칭 및 가시성을 조정하였습니다.
디자인 시스템의 일관성을 위해 기존 `Snackbar`를 대체하는 `PrezelSnackbar`를 구현하고, 관련 상태 관리 로직을 고도화했습니다.
* **feat: PrezelSnackbar 및 PrezelSnackbarHost 구현**
* `Surface`를 사용하여 디자인 시스템 테마(`PrezelColorScheme.Dark`)가 적용된 커스텀 스낵바 레이아웃 구현
* `PrezelNavigationBar`에서 기본 `SnackbarHost` 대신 `PrezelSnackbarHost`를 사용하도록 변경
* 스낵바 노출 시 Y축 오프셋을 조절할 수 있는 `offsetY` 파라미터 추가
* **refactor: 스낵바 인터페이스 및 데이터 모델 변경**
* `PrezelSnackbarVisuals`에서 `actionLabel`을 필수 값으로 변경
* `showPrezelSnackbar` 확장 함수에서 `actionLabel`과 `onAction`을 필수로 받도록 수정하고, 기존의 null 체크 로직 제거
* `SnackbarData`의 구현체인 `PreviewSnackbarData`의 `dismiss`, `performAction` 반환 타입을 `Unit`으로 명시
* **style: UI 레이아웃 및 미리보기 개선**
* 스낵바 내부의 텍스트와 버튼 사이의 간격 및 패딩 값 조정
* `ThemePreview`에서 불필요한 `PreviewScaffold`를 제거하고 `Column`을 사용하여 케이스별 미리보기 구성
* `PrezelSnackbarLeadingIcon` 추가 및 아이콘 처리 로직 정리
`PrezelApp`에서 초기 실행 시 `SplashScreen`을 먼저 노출하고, 이후 메인 콘텐츠로 전환되는 로직을 구현했습니다. * `isSplashEnded` 상태를 `rememberSaveable`로 관리하여 스플래시 종료 여부를 추적합니다. * `isSplashEnded`가 `false`일 경우 `SplashScreen`을 렌더링하며, 애니메이션 종료 후 상태를 업데이트하여 `PrezelAppContent`로 진입하도록 개선했습니다.
* `libs.versions.toml`: `androidx.hilt.navigation.compose` 라이브러리 및 버전(`1.3.0`) 정의를 추가했습니다. * `AndroidFeatureImplConventionPlugin`: 모든 피처 모듈의 구현(`impl`) 플러그인에 `hilt-navigation-compose` 의존성을 기본으로 포함하도록 설정했습니다.
앱의 초기 진입점을 `SplashNavKey`로 변경하고, 화면 전환 시 공유 요소 애니메이션을 지원하기 위해 `SharedTransitionLayout`을 도입했습니다.
* **refactor: 앱 초기화 및 네비게이션 로직 수정**
* 앱의 시작 화면(`startKey`)을 `HomeNavKey`에서 `SplashNavKey`로 변경했습니다.
* `topLevelKeys` 구성에 `SplashNavKey`와 `LoginNavKey`를 추가하여 네비게이션 범위를 확장했습니다.
* `PrezelApp`에서 수동으로 관리하던 `isSplashEnded` 상태 로직을 제거하고, 네비게이션 시스템을 통해 스플래시 화면이 제어되도록 구조를 개선했습니다.
* **feat: SharedTransitionLayout 및 관련 Scope 도입**
* 애니메이션 최적화를 위해 `PrezelAppContent`를 `SharedTransitionLayout`으로 감싸고 `ProvideSharedTransitionScope`를 통해 하위 컴포저블에 공유 변환 컨텍스트를 제공합니다.
* `NavDisplay` 및 네비게이션 관련 컴포저블이 공유 요소 전환(Shared Element Transition)을 사용할 수 있는 기반을 마련했습니다.
* **style: 코드 정리 및 성능 최적화**
* `entryProvider` 생성 시 `navigator`에 대한 의존성을 추가하여 리샘플링을 방지했습니다.
* 불필요한 import 문과 사용되지 않는 상태 변수(`rememberSaveable`)를 정리했습니다.
`app` 모듈에 위치하던 로고 이미지 리소스를 `core:designsystem` 모듈로 이동하고, 디자인 시스템 리소스 명명 규칙에 맞춰 변경하였습니다. * `logo_prezel.xml` -> `core_designsystem_logo_prezel.xml` 로 이동 및 변경
`PrezelApp`의 전반적인 구조를 개선하여 공유 요소 전환(Shared Transition)을 위한 기반을 마련하고, 기존의 `SplashScreen` 로직을 정리했습니다.
* **feat: SharedTransitionLayout 및 관련 Scope 추가**
* `PrezelAppContent` 내부를 `SharedTransitionLayout`으로 감싸고 `ProvideSharedTransitionScope`를 통해 하위 컴포넌트에서 공유 요소 전환을 사용할 수 있도록 구조를 변경했습니다.
* `ExperimentalSharedTransitionApi` 옵트인 어노테이션을 추가했습니다.
* **refactor: SplashScreen 로직 제거**
* `PrezelApp` 컴포저블에서 `isSplashEnded` 상태와 `SplashScreen` 렌더링 로직을 제거하고, 즉시 `PrezelAppContent`가 표시되도록 수정했습니다.
* **refactor: entryProvider 생성 로직 최적화**
* `PrezelAppContent` 내에서 `provider` 생성 시 `navigator`를 `remember`의 키값으로 추가하여 의존성을 명확히 했습니다.
* 불필요한 `import` 문을 정리했습니다.
- 기존 `app` 모듈에 위치하던 Splash 로직을 별도 피처 모듈로 분리하고, Login 피처 모듈을 새롭게 추가하여 멀티 모듈 구조를 개선했습니다.
- `LoginScreen`의 최상위 컨테이너를 `Box`에서 `Column`으로 변경하고, 내부 컴포넌트들의 배치 로직을 개선하였습니다.
* refactor: AndroidFeatureImplConventionPlugin 의존성 수정
* `AndroidFeatureImplConventionPlugin`에서 사용되던 `androidx.hilt.navigation.compose` 라이브러리를 `androidx.hilt.lifecycle.viewmodel.compose`로 교체하였습니다.
* chore: libs.versions.toml 라이브러리 선언 변경
* `androidx-hilt-navigation-compose`를 `androidx-hilt-lifecycle-viewmodel-compose`로 변경하여 Hilt ViewModel 관련 의존성을 최신화했습니다.
`Navigator`의 `replaceRoot` 함수에서 루트 교체 시의 제약 조건을 추가하고, 모든 서브 스택을 초기화하도록 로직을 개선하였습니다. * `replaceRoot` 호출 시 전달된 키가 `topLevelKeys`에 포함되는지 확인하는 `require` 문을 추가하여 안정성을 높였습니다. * 특정 키의 서브 스택만 정리하던 로직을 모든 서브 스택(`state.subStacks.values`)을 순회하며 루트를 제외한 하위 히스토리를 삭제하도록 변경했습니다.
`PrezelApp.kt`에서 더 이상 사용되지 않는 `ExperimentalSharedTransitionApi` 임포트와 `@OptIn` 어노테이션을 삭제하였습니다.
스플래시 화면의 로그인 상태 확인 시점을 변경하고 네비게이션 동작을 최적화했습니다.
* **refactor: SplashViewModel 로그인 상태 체크 로직 수정**
* `checkLoginStatus` 함수를 `private`으로 변경하고 `init` 블록에서 호출하도록 수정하여 ViewModel 생성 시점에 로직이 실행되도록 변경했습니다.
* 클래스 선언부의 코드 포맷을 정리했습니다.
* **refactor: SplashScreen 생명주기 의존성 제거**
* `LifecycleEventEffect`를 사용해 `ON_RESUME` 시점에 호출하던 `viewModel.checkLoginStatus()` 로직을 제거했습니다. (ViewModel 내부에서 처리되도록 변경)
* 불필요한 import 문을 정리했습니다.
* **refactor: 로그인 화면 이동 시 replaceRoot 적용**
* `navigateToLogin` 호출 시 `navigator.navigate` 대신 `navigator.replaceRoot`를 사용하도록 수정하여 스플래시 화면이 백스택에 남지 않도록 개선했습니다.
`LocalSharedTransitionScope`의 정의를 `compositionLocalOf`에서 `staticCompositionLocalOf`로 수정하였습니다. 해당 값은 빈번하게 변경되지 않으므로, 정적 구성을 통해 불필요한 추적 오버헤드를 줄이고 성능을 최적화했습니다.
`SplashScreen`의 내부 UI 구현 코드를 별도의 `SplashScreen` 컴포저블로 추출하여 책임을 분리하고 가독성을 개선했습니다. * `SplashScreen` 내부의 `Box` 및 `Image` 레이아웃 로직을 분리된 컴포저블로 이동 * `sharedElement` 및 `animatedVisibilityScope`를 통한 로고 애니메이션 구조 유지
* **docs: Navigator 클래스 내 내비게이션 정책 주석 추가**
* `Navigator` 클래스와 주요 메서드(`navigate`, `replaceRoot`, `pop`, `goToKey` 등)에 상세 동작 정책 및 파라미터 설명을 추가했습니다.
* 최상위 키(top-level)와 하위 스택(sub-stack) 관리 로직에 대한 설명을 보강하여 코드 가독성을 높였습니다.
* **refactor: LoggingDecorator 구현 단순화**
* `NavEntryDecorator` 생성 시 사용되던 불필요한 `onPop` 콜백과 확장 함수(`enterLog`, `exitLog`)를 제거했습니다.
* `Timber` 로거 주입 방식을 내부 직접 호출 방식으로 변경하고, 로그 포맷을 일관되게 정리했습니다.
* `DisposableEffect`를 통해 화면 진입(`SCREEN_ENTER`) 및 이탈(`SCREEN_EXIT`) 시점의 로그를 출력하도록 개선했습니다.
- 앱의 전반적인 네비게이션 구조를 명확히 하기 위해 내비게이션 키와 아이템의 명칭 및 가시성을 조정하였습니다.
디자인 시스템의 일관성을 위해 기존 `Snackbar`를 대체하는 `PrezelSnackbar`를 구현하고, 관련 상태 관리 로직을 고도화했습니다.
* **feat: PrezelSnackbar 및 PrezelSnackbarHost 구현**
* `Surface`를 사용하여 디자인 시스템 테마(`PrezelColorScheme.Dark`)가 적용된 커스텀 스낵바 레이아웃 구현
* `PrezelNavigationBar`에서 기본 `SnackbarHost` 대신 `PrezelSnackbarHost`를 사용하도록 변경
* 스낵바 노출 시 Y축 오프셋을 조절할 수 있는 `offsetY` 파라미터 추가
* **refactor: 스낵바 인터페이스 및 데이터 모델 변경**
* `PrezelSnackbarVisuals`에서 `actionLabel`을 필수 값으로 변경
* `showPrezelSnackbar` 확장 함수에서 `actionLabel`과 `onAction`을 필수로 받도록 수정하고, 기존의 null 체크 로직 제거
* `SnackbarData`의 구현체인 `PreviewSnackbarData`의 `dismiss`, `performAction` 반환 타입을 `Unit`으로 명시
* **style: UI 레이아웃 및 미리보기 개선**
* 스낵바 내부의 텍스트와 버튼 사이의 간격 및 패딩 값 조정
* `ThemePreview`에서 불필요한 `PreviewScaffold`를 제거하고 `Column`을 사용하여 케이스별 미리보기 구성
* `PrezelSnackbarLeadingIcon` 추가 및 아이콘 처리 로직 정리
`PrezelSnackbar`를 식별하고 제어할 수 있도록 ID 시스템을 도입했습니다.
* **feat: PrezelSnackbarVisuals 및 showSnackbar에 ID 필드 추가**
* `PrezelSnackbarVisuals` 데이터 클래스에 선택적 파라미터인 `id: String?`를 추가했습니다.
* `SnackbarHostState.showSnackbar` 확장 함수에 `id` 파라미터를 추가하여 스낵바 생성 시 고유 식별자를 부여할 수 있도록 개선했습니다.
* **feat: ID를 이용한 스낵바 종료 함수 추가**
* `SnackbarHostState.dismissById(id: String)` 확장 함수를 추가했습니다.
* 현재 표시 중인 스낵바의 ID가 인자로 받은 ID와 일치할 경우 해당 스낵바를 즉시 종료(dismiss)하는 로직을 구현했습니다.
`PrezelSnackbar` 컴포넌트 내부에서 `actionLabel`을 처리하는 방식을 개선했습니다. * `visuals.actionLabel`에 대한 불필요한 null 체크 및 `let` 블록을 제거하고 직접 참조하도록 수정했습니다. * 액션 버튼과 텍스트 사이의 간격(`Spacer`) 및 `PrezelButton`이 조건부 렌더링 없이 항상 포함되도록 구조를 정리했습니다.
`LoggingDecorator`에서 출력하는 로그 메시지에서 불필요한 `[Navigation]` 접두어를 제거하여 가독성을 개선했습니다. * `SCREEN_ENTER` 및 `SCREEN_EXIT` 로그 메시지 내의 `[Navigation]` 문자열 삭제
`NavigationState` 내 로직을 개선하고 불필요한 공백을 제거하였습니다.
* **refactor: NavigationState의 isTopLevel 상태 관리 개선**
* `isTopLevel` 프로퍼티를 `derivedStateOf`를 사용하는 위임 프로퍼티로 변경하여 상태 변화에 따른 재계산 효율을 높였습니다.
* **style: DoubleBackToExitHandler 코드 정리**
* 불필요한 빈 줄을 제거하여 코드 가독성을 개선했습니다.
* **refactor: NavigationState 구조 및 상태 노출 방식 변경**
* `NavigationState` 클래스에 `@Stable` 어노테이션을 추가하여 Compose 최적화를 지원합니다.
* `currentTopLevelKey`, `currentKey` 프로퍼티의 `derivedStateOf`를 제거하고 커스텀 getter로 변경하였습니다.
* 불필요한 `isTopLevel` 파라미터를 삭제했습니다.
* **refactor: DoubleBackToExitHandler 파라미터 및 로직 수정**
* `DoubleBackToExitHandler`가 `PrezelAppState` 전체 대신 필요한 `NavigationState`만 전달받도록 수정했습니다.
* `isTopLevelScreen` 판정 로직을 `navigationState.currentKey in navigationState.topLevelKeys` 직접 비교 방식으로 변경했습니다.
* `PrezelApp`에서 변경된 파라미터 구조에 맞춰 호출부를 업데이트했습니다.
…#75-custom-splash # Conflicts: # Prezel/app/src/main/java/com/team/prezel/ui/DoubleBackToExitHandler.kt # Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt # Prezel/core/navigation/src/main/java/com/team/prezel/core/navigation/NavigationState.kt
📌 작업 내용
feature/*/(api, impl)모듈로 분리하고 NavKey/EntryBuilder를 추가했습니다.SplashNavKey로 변경하고,Theme.PrezelSplashScreen을 적용했습니다.SharedTransitionLayout기반으로 Splash ↔ Login 로고 shared element 전환을 적용했습니다.Navigator.replaceRoot()를 추가해 인증 이후 루트 전환(스택 초기화) 흐름을 정리했습니다.DoubleBackToExitHandler를 통해TopLevelKeys에서 뒤로가기 시 앱 종료 기능 구현했습니다.DesignSystem Snackbar가 적용되고 있지 않은 문제를 확인하여 적용 및 개선 작업을 진행했습니다.Shared Element Animation Spec
tween( durationMillis = 300, easing = EaseOut, delayMillis = 400, )🧩 관련 이슈
📸 스크린샷
Screen_recording_20260307_183456.webm
📢 논의하고 싶은 내용
SplashViewModel.checkLoginStatus()는 임시로NavigateToLogin만 emit하고 있습니다.TODO: 카카오 로그인).Summary by CodeRabbit
새로운 기능
개선