-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] SnackbarHost 설정 및 Snackbar Compose 마이그레이션 #47
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
base: develop
Are you sure you want to change the base?
Conversation
기존에 `Toast`로 처리되거나 주석 처리되어 있던 사용자 알림 기능을 `Snackbar` 기반으로 통합하고, 이를 중앙에서 관리할 수 있는 `SnackbarManager`를 도입했습니다. 이로 인해 앱 전체에서 일관된 스타일의 알림을 제공하고, 에러 처리 로직을 개선했습니다.
- **`common/component/SnackBar.kt` 추가:**
- `FestabookSnackbar` 컴포저블을 추가하여 앱의 디자인 시스템에 맞는 스낵바를 구현했습니다.
- `SnackbarManager` 클래스를 도입하여 스낵바 메시지 표시(`show`) 및 에러 메시지 처리(`showError`) 로직을 중앙에서 관리하도록 했습니다.
- `rememberAppSnackbarManager` 함수를 추가하여 `SnackbarManager`를 Compose 환경에서 쉽게 생성하고 재사용할 수 있도록 했습니다.
- **`main/component/MainScreen.kt` 수정:**
- `Scaffold`에 `SnackbarHost`를 추가하고, `FestabookSnackbar`를 사용하도록 설정했습니다.
- `rememberAppSnackbarManager`를 이용해 `SnackbarManager` 인스턴스를 생성했습니다.
- 기존 `Toast` 알림(뒤로가기 종료 알림)과 주석 처리되었던 알림(알림 설정 성공)을 `snackbarManager.show()` 호출로 대체했습니다.
- `homeNavGraph`에 `snackbarManager`를 전달하여 하위 화면(Home, Setting 등)에서 스낵바를 사용할 수 있도록 `onShowSnackBar`와 `onShowErrorSnackBar` 콜백을 구현했습니다.
장소 상세 정보를 불러오는 데 실패했을 때 사용자에게 스낵바를 통해 에러를 알려주는 기능을 추가했습니다.
- **`PlaceDetailScreen.kt` 수정:**
- `PlaceDetailRoute`와 `PlaceDetailScreen`에 `onShowErrorSnackbar` 콜백 파라미터를 추가했습니다.
- `LaunchedEffect`를 사용하여 `placeDetailUiState`가 `Error` 상태일 때, `onShowErrorSnackbar` 콜백을 호출하도록 구현했습니다.
- **`PlaceMapNavigation.kt` 및 `MainScreen.kt` 수정:**
- `placeMapNavGraph`를 통해 `MainScreen`의 `snackbarManager::showError` 함수를 `PlaceDetailRoute`까지 전달하여 스낵바가 표시되도록 연결했습니다.
각 화면(`Home`, `Schedule`, `News`)의 ViewModel에서 발생하는 `Error` 상태를 감지하여 스낵바로 사용자에게 에러 메시지를 표시하는 기능을 추가했습니다. `MainScreen`에서 스낵바 표시 로직을 중앙에서 관리하고, 각 화면에는 에러 발생 시 호출할 콜백(`onShowErrorSnackbar`)을 전달하는 방식으로 구현했습니다.
- **`MainScreen.kt` 수정:**
- `homeNavGraph`, `scheduleNavGraph`, `newsNavGraph`에 `snackbarManager::showError`를 `onShowErrorSnackbar` 콜백으로 전달하여 에러 처리를 위임했습니다.
- **`HomeScreen.kt`, `ScheduleScreen.kt`, `NewsScreen.kt` 수정:**
- `onShowErrorSnackbar: (Throwable) -> Unit` 파라미터를 추가했습니다.
- `LaunchedEffect`를 사용하여 각 ViewModel의 UI 상태(`festivalUiState`, `scheduleUiState`, `noticeUiState` 등)가 `Error`일 경우, `onShowErrorSnackbar` 콜백을 호출하도록 구현했습니다.
- `rememberUpdatedState`를 사용하여 항상 최신의 콜백을 참조하도록 하여 안정성을 높였습니다.
- **관련 `NavGraph` 파일들 수정:**
- `homeNavGraph`, `scheduleNavGraph`, `newsNavGraph` 함수에 `onShowErrorSnackbar` 파라미터를 추가하고, 이를 각 화면 컴포저블로 전달하도록 수정했습니다.
기존 스낵바가 표시된 상태에서 새로운 스낵바가 호출될 경우, 이전 스낵바를 즉시 닫고 새로운 스낵바가 표시되도록 `show` 함수의 동작을 수정했습니다. 또한, 불필요한 `Dismiss` 버튼을 제거하여 UI를 간소화했습니다.
- **`SnackBar.kt` 수정:**
- `show` 함수 호출 시, `hostState.currentSnackbarData?.dismiss()`를 먼저 실행하여 현재 표시 중인 스낵바를 닫는 로직을 추가했습니다.
- `showSnackbar`의 `withDismissAction` 파라미터를 제거하여, 스낵바에 별도의 '닫기' 버튼이 나타나지 않도록 변경했습니다.
- **`MainScreen.kt` 수정:**
- 설정 화면에서 알림 활성화 성공 시 스낵바를 표시하던 `ObserveAsEvents` 로직을 제거했습니다. 이는 새로운 정책에 따라 더 이상 필요하지 않은 기능입니다.
`PlaceMapRoute`에서 발생하는 오류를 상위 컴포저블(`MainScreen`)로 전달하여 스낵바를 통해 사용자에게 표시할 수 있도록 구조를 개선했습니다.
- **`PlaceMapScreen.kt` 수정:**
- `PlaceMapRoute` 컴포저블에 `onShowErrorSnackBar: (Throwable) -> Unit` 콜백 파라미터를 추가했습니다.
- `PlaceMapSideEffectHandler` 내에서 `ShowErrorSnackBar` 이펙트가 발생했을 때, `onShowErrorSnackBar` 콜백을 호출하여 `Throwable`을 전달하도록 수정했습니다.
- **`MainScreen.kt` 수정:**
- `PlaceMapRoute`를 호출할 때 `onShowErrorSnackBar` 파라미터에 `snackbarManager::showError`를 전달하여, 지도 화면에서 오류가 발생하면 `MainScreen`의 스낵바 매니저가 오류 메시지를 표시하도록 연결했습니다.
📝 Walkthrough순서 정보이 PR은 안드로이드 Compose 앱 전체에 걸쳐 중앙 집중식 스낵바 시스템을 추가합니다. 새로운 SnackbarManager를 도입하고 여러 화면 및 네비게이션 그래프를 업데이트하여 오류 상태를 스낵바 시스템으로 라우팅하는 onShowErrorSnackbar 콜백을 수락하도록 합니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant Screen as 화면 컴포넌트
participant Effect as LaunchedEffect
participant Callback as onShowErrorSnackbar
participant Manager as SnackbarManager
participant Host as SnackbarHost
Screen->>Effect: UiState.Error 감지
Effect->>Callback: throwable 전달
Callback->>Manager: showError(throwable)
Manager->>Manager: 오류 타입별 메시지 맵핑
Manager->>Host: show(message)
Host->>Host: 스낵바 표시
예상 코드 리뷰 노력🎯 3 (Moderate) | ⏱️ ~25 minutes 관련 가능성 있는 PR
제안 검토자
시
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/daedan/festabook/presentation/placeMap/component/PlaceMapScreen.kt (1)
81-98:onShowErrorSnackBar콜백이remember키에서 누락됨
PlaceMapSideEffectHandler가remember블록 안에서 생성되지만,onShowErrorSnackBar콜백이 키에 포함되지 않았습니다. 부모 컴포저블이 다른 콜백 인스턴스로 리컴포지션되면 핸들러는 재생성되지 않아 이전(stale) 콜백이 호출될 수 있습니다.🐛 제안: remember 키에 콜백 추가
val placeMapSideEffectHandler = - remember(placeMapViewModel, logger, mapManagerDelegate) { + remember(placeMapViewModel, logger, mapManagerDelegate, onShowErrorSnackBar) { PlaceMapSideEffectHandler( mapManagerDelegate = mapManagerDelegate, bottomSheetState = bottomSheetState, viewModel = placeMapViewModel, logger = logger, onStartPlaceDetail = onStartPlaceDetail, onPreloadImages = { preloadImages( context = context, scope = scope, places = it.places, ) }, onShowErrorSnackBar = { onShowErrorSnackBar(it.error.throwable) }, ) }또는
onStartPlaceDetail도 동일한 문제가 있으므로 함께 키에 추가하는 것을 권장합니다.
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/daedan/festabook/presentation/main/component/MainScreen.kt`:
- Around line 63-66: The local val noticeEnabledMessage declared via
noticeEnabledMessage = stringResource(R.string.setting_notice_enabled) inside
the MainScreen composable is unused; either remove that declaration (and any
now-unneeded imports) or actually use it where intended (e.g., pass it to a
Snackbar, dialog, or UI text) — if it’s meant as a placeholder for future work,
replace the declaration with a short TODO comment referencing its intended use;
locate the val noticeEnabledMessage in MainScreen.kt to apply the change.
🧹 Nitpick comments (1)
app/src/main/java/com/daedan/festabook/presentation/common/component/SnackBar.kt (1)
66-78:errorMessages맵의remember키 누락 검토 필요
errorMessages맵이remember {}블록 안에서 키 없이 생성되지만, 맵의 값들(clientErrorMessage등)은 블록 외부에서stringResource로 가져옵니다. 앱 실행 중 로케일이 변경되면stringResource값은 갱신되지만remember블록 내부의 맵은 재생성되지 않아 이전 로케일의 메시지가 유지될 수 있습니다.만약 런타임 로케일 변경을 지원해야 한다면
remember키에 문자열 값들을 포함하거나, 맵 생성을remember외부로 이동하는 것을 고려해 주세요.♻️ 제안: remember 키에 문자열 값 추가
val errorMessages = - remember { + remember(clientErrorMessage, serverErrorMessage, networkErrorMessage, unknownErrorMessage) { mapOf( ApiResultException.ClientException::class to clientErrorMessage, ApiResultException.ServerException::class to serverErrorMessage, ApiResultException.NetworkException::class to networkErrorMessage, ApiResultException.UnknownException::class to unknownErrorMessage, ) }
| val snackbarHostState = remember { SnackbarHostState() } | ||
| val snackbarManager = rememberAppSnackbarManager(snackbarHostState) | ||
| val backPressExitMessage = stringResource(R.string.back_press_exit_message) | ||
| val noticeEnabledMessage = stringResource(R.string.setting_notice_enabled) |
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.
사용되지 않는 변수 확인 필요
noticeEnabledMessage가 선언되었지만 이 파일 내에서 사용되지 않습니다. 향후 구현을 위해 의도적으로 추가한 것인지, 아니면 제거해야 하는지 확인해 주세요.
🔧 사용하지 않는 경우 제거 제안
val snackbarHostState = remember { SnackbarHostState() }
val snackbarManager = rememberAppSnackbarManager(snackbarHostState)
val backPressExitMessage = stringResource(R.string.back_press_exit_message)
-val noticeEnabledMessage = stringResource(R.string.setting_notice_enabled)📝 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.
| val snackbarHostState = remember { SnackbarHostState() } | |
| val snackbarManager = rememberAppSnackbarManager(snackbarHostState) | |
| val backPressExitMessage = stringResource(R.string.back_press_exit_message) | |
| val noticeEnabledMessage = stringResource(R.string.setting_notice_enabled) | |
| val snackbarHostState = remember { SnackbarHostState() } | |
| val snackbarManager = rememberAppSnackbarManager(snackbarHostState) | |
| val backPressExitMessage = stringResource(R.string.back_press_exit_message) |
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/daedan/festabook/presentation/main/component/MainScreen.kt`
around lines 63 - 66, The local val noticeEnabledMessage declared via
noticeEnabledMessage = stringResource(R.string.setting_notice_enabled) inside
the MainScreen composable is unused; either remove that declaration (and any
now-unneeded imports) or actually use it where intended (e.g., pass it to a
Snackbar, dialog, or UI text) — if it’s meant as a placeholder for future work,
replace the declaration with a short TODO comment referencing its intended use;
locate the val noticeEnabledMessage in MainScreen.kt to apply the change.
parkjiminnnn
left a 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.
고생하셨습니다. 밀러!
스낵바가 야무지게 잘뜨네요잉
| LaunchedEffect(festivalUiState) { | ||
| when (val state = festivalUiState) { | ||
| is FestivalUiState.Error -> { | ||
| currentOnShowErrorSnackbar(state.throwable) | ||
| } | ||
|
|
||
| else -> { | ||
| Unit | ||
| } | ||
| } |
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.
when문을 사용하신 이유는 error 일때 Snackbar말고 다른 상태에 대한 snackbar도 사용할 경우도 있어서 확장성을 고려하신걸까요?
| mainViewModel: MainViewModel = viewModel(), | ||
| homeViewModel: HomeViewModel = viewModel(), | ||
| scheduleViewModel: ScheduleViewModel = viewModel(), | ||
| placeMapViewModel: PlaceMapViewModel = viewModel(), | ||
| newsViewModel: NewsViewModel = viewModel(), | ||
| settingViewModel: SettingViewModel = viewModel(), |
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.
오 요렇게 하면 해당 타입의 viewModel을 mainActivity에 연결된 defaultViewModelFactory(metroViewModelFactory)에서
생성해서 자동주입해는 것 같은데 제가 이해한게 맞나요?
이런게 있는지 몰랐어요.. 한 수 배워갑니당
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.
허허 에러상태일 때 새로고침이랑 화면 구성을 좀 손봐야겠네요.. ㅋㅋ
제 과실이니 제가이슈 새로파서 하겠습니다.
| fun showError(throwable: Throwable) { | ||
| val message = errorMessages[throwable::class] ?: throwable.message ?: defaultErrorMessage |
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.
여기서 throwable.message 를 노출하면 사용자에게 필요없는 정보가 노출되지는 않을까요?
defaultErrorMessgae만 보이도록하는 건 어떨까요?
#️⃣ 이슈 번호
🛠️ 작업 내용
를 진행함과 동시에 마이그레이션 하는 것이 좋을 것 같습니다.
🙇🏻 중점 리뷰 요청
각자 작업하신 화면에서 스낵바 호출이 누락된 부분이 있다면 코멘트 부탁드립니다 !
📸 이미지 첨부 (Optional)
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.