Conversation
📝 Walkthrough📋 Walkthrough네트워크 에러 감지 및 표시 시스템을 구현합니다. OkHttp 인터셉터를 통해 HTTP 응답 및 예외를 감시하고, SharedFlow로 에러를 관리하며, MainActivity에서 UI 모달로 사용자에게 에러를 표시합니다. 📊 Changes
🔄 Sequence Diagram(s)sequenceDiagram
actor User
participant OkHttpClient
participant NetworkErrorInterceptor
participant NetworkErrorManager
participant MainActivity
User->>OkHttpClient: HTTP 요청 발송
OkHttpClient->>NetworkErrorInterceptor: intercept() 호출
alt HTTP 응답 상태 코드 300-599
NetworkErrorInterceptor->>NetworkErrorManager: emitError(ConnectionError)
NetworkErrorManager->>MainActivity: networkError SharedFlow 방출
MainActivity->>User: 에러 모달 표시
else UnknownHostException
NetworkErrorInterceptor->>NetworkErrorManager: emitError(NoInternet)
NetworkErrorManager->>MainActivity: networkError SharedFlow 방출
MainActivity->>User: 에러 모달 표시
else SocketTimeoutException
NetworkErrorInterceptor->>NetworkErrorManager: emitError(Timeout)
NetworkErrorManager->>MainActivity: networkError SharedFlow 방출
MainActivity->>User: 에러 모달 표시
else IOException
NetworkErrorInterceptor->>NetworkErrorManager: emitError(UnknownError)
NetworkErrorManager->>MainActivity: networkError SharedFlow 방출
MainActivity->>User: 에러 모달 표시
end
User->>MainActivity: 모달 확인 버튼 클릭
MainActivity->>MainActivity: restartApplication() 실행
MainActivity->>User: 앱 재시작
⏱️ Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes 새로운 에러 관리 시스템 구축으로 인한 다중 파일 수정, SharedFlow 및 Coroutine 로직 검토, 의존성 주입 설정 확인이 필요합니다. 🐰 Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@app/src/main/java/com/flint/presentation/MainActivity.kt`:
- Around line 49-60: The modal cannot be dismissed and the error state isn't
cleared because onDismiss is a no-op and networkError remains set; update the
OneButtonModal invocation so onDismiss clears the error state (e.g., call the
state setter to set networkError to null/empty) and also ensure the onConfirm
(restartApplication) clears the same networkError before/after calling
restartApplication; reference the networkError state variable and the
OneButtonModal usage and modify both onDismiss and the onConfirm callback to
reset the error state.
- Around line 65-72: In restartApplication, avoid the unsafe intent!! and abrupt
exitProcess(0): first retrieve the launch intent via
PackageManager.getLaunchIntentForPackage(this.packageName) into a nullable
variable and bail with a logged warning or fallback if null instead of
force-unwrapping; then build the restart intent using
Intent.makeRestartActivityTask(component) (or create a new Intent(this,
MainActivity::class.java) with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK
if component is unavailable) and call startActivity(restartIntent) followed by
finishAffinity() (or finish() where appropriate) to close activities gracefully,
removing the exitProcess call; update references in restartApplication to use
these safer flows.
🧹 Nitpick comments (4)
app/src/main/java/com/flint/data/di/interceptor/NetworkErrorManager.kt (1)
8-16: SharedFlow 버퍼 설정 검토 필요
MutableSharedFlow의 기본 설정(replay=0,extraBufferCapacity=0)으로는 collector가 없을 때emit()이 호출되면 에러가 유실될 수 있습니다. 앱 시작 직후 네트워크 에러가 발생하면 UI가 아직 collect하지 않는 상태일 수 있습니다.♻️ 버퍼 추가 제안
`@Singleton` class NetworkErrorManager `@Inject` constructor() { - private val _networkError = MutableSharedFlow<NetworkError>() + private val _networkError = MutableSharedFlow<NetworkError>( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) val networkError = _networkError.asSharedFlow()app/src/main/java/com/flint/data/di/interceptor/NetworkErrorInterceptor.kt (2)
14-17: CoroutineScope 생명주기 관리 필요
CoroutineScope가 클래스 내부에서 생성되지만 취소되지 않습니다. 이 인터셉터는 Singleton으로 유지되므로 큰 문제는 아니지만, 구조적으로 scope 관리가 없는 fire-and-forget 패턴입니다.만약 emit 실패 시 에러 핸들링이 필요하다면,
launch에CoroutineExceptionHandler를 추가하는 것을 고려하세요.
26-37: 3xx 리다이렉트 응답도 에러로 처리됨
300..599범위에는 3xx 리다이렉트 응답도 포함됩니다. OkHttp는 기본적으로 리다이렉트를 자동 처리하므로 일반적으로 3xx 응답이 여기까지 도달하지 않지만, 리다이렉트가 비활성화된 경우 사용자에게 불필요한 에러 모달이 표시될 수 있습니다.♻️ 4xx, 5xx만 에러로 처리하는 것이 일반적
if (!response.isSuccessful) { when (response.code) { - in 300..599 -> { + in 400..599 -> { scope.launch { networkErrorManager.emitError(app/src/main/java/com/flint/presentation/MainActivity.kt (1)
17-18: 아키텍처 위반: Presentation 레이어에서 Data 레이어 직접 참조
NetworkError와NetworkErrorManager가com.flint.data.di.interceptor패키지에 있어 presentation 레이어에서 data 레이어를 직접 참조합니다. Google의 Android App Architecture 권장사항에 따르면, domain 레이어를 통해 간접 참조하는 것이 좋습니다.Based on learnings, 이 저장소는 Google의 Android App Architecture를 따르고 있습니다.
| networkError?.let { error -> | ||
| OneButtonModal( | ||
| icon = R.drawable.ic_gradient_none, | ||
| title = "문제가 발생했어요", | ||
| message = "잠시 후 다시 시도해주세요", | ||
| buttonText = "다시 시작하기", | ||
| onConfirm = { | ||
| restartApplication() | ||
| }, | ||
| onDismiss = {} | ||
| ) | ||
| } |
There was a problem hiding this comment.
모달을 닫을 수 없음 & 에러 상태 미초기화
onDismiss = {}가 빈 람다여서 사용자가 모달 외부를 탭해도 아무 동작이 없습니다.- 에러가 발생하면
networkError상태가 초기화되지 않아 모달이 계속 표시됩니다.
🐛 에러 상태 초기화 추가
networkError?.let { error ->
OneButtonModal(
icon = R.drawable.ic_gradient_none,
title = "문제가 발생했어요",
message = "잠시 후 다시 시도해주세요",
buttonText = "다시 시작하기",
onConfirm = {
restartApplication()
},
- onDismiss = {}
+ onDismiss = { networkError = null }
)
}📝 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.
| networkError?.let { error -> | |
| OneButtonModal( | |
| icon = R.drawable.ic_gradient_none, | |
| title = "문제가 발생했어요", | |
| message = "잠시 후 다시 시도해주세요", | |
| buttonText = "다시 시작하기", | |
| onConfirm = { | |
| restartApplication() | |
| }, | |
| onDismiss = {} | |
| ) | |
| } | |
| networkError?.let { error -> | |
| OneButtonModal( | |
| icon = R.drawable.ic_gradient_none, | |
| title = "문제가 발생했어요", | |
| message = "잠시 후 다시 시도해주세요", | |
| buttonText = "다시 시작하기", | |
| onConfirm = { | |
| restartApplication() | |
| }, | |
| onDismiss = { networkError = null } | |
| ) | |
| } |
🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/MainActivity.kt` around lines 49 -
60, The modal cannot be dismissed and the error state isn't cleared because
onDismiss is a no-op and networkError remains set; update the OneButtonModal
invocation so onDismiss clears the error state (e.g., call the state setter to
set networkError to null/empty) and also ensure the onConfirm
(restartApplication) clears the same networkError before/after calling
restartApplication; reference the networkError state variable and the
OneButtonModal usage and modify both onDismiss and the onConfirm callback to
reset the error state.
| private fun restartApplication() { | ||
| val packageManager: PackageManager = this.packageManager | ||
| val intent = packageManager.getLaunchIntentForPackage(this.packageName) | ||
| val componentName = intent!!.component | ||
| val mainIntent = Intent.makeRestartActivityTask(componentName) | ||
| this.startActivity(mainIntent) | ||
| exitProcess(0) | ||
| } |
There was a problem hiding this comment.
Null 안전성 및 강제 종료 방식 검토
intent!!는getLaunchIntentForPackage가 null을 반환할 경우 NPE를 발생시킵니다.exitProcess(0)는 앱을 강제 종료하여 저장되지 않은 데이터가 유실될 수 있습니다.
🐛 안전한 재시작 로직
private fun restartApplication() {
val packageManager: PackageManager = this.packageManager
val intent = packageManager.getLaunchIntentForPackage(this.packageName)
- val componentName = intent!!.component
- val mainIntent = Intent.makeRestartActivityTask(componentName)
- this.startActivity(mainIntent)
- exitProcess(0)
+ intent?.let {
+ val componentName = it.component
+ val mainIntent = Intent.makeRestartActivityTask(componentName)
+ this.startActivity(mainIntent)
+ Runtime.getRuntime().exit(0)
+ } ?: run {
+ // Fallback: just finish and let system restart
+ finishAffinity()
+ }
}🤖 Prompt for AI Agents
In `@app/src/main/java/com/flint/presentation/MainActivity.kt` around lines 65 -
72, In restartApplication, avoid the unsafe intent!! and abrupt exitProcess(0):
first retrieve the launch intent via
PackageManager.getLaunchIntentForPackage(this.packageName) into a nullable
variable and bail with a logged warning or fallback if null instead of
force-unwrapping; then build the restart intent using
Intent.makeRestartActivityTask(component) (or create a new Intent(this,
MainActivity::class.java) with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK
if component is unavailable) and call startActivity(restartIntent) followed by
finishAffinity() (or finish() where appropriate) to close activities gracefully,
removing the exitProcess call; update references in restartApplication to use
these safer flows.
📮 관련 이슈
📌 작업 내용
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.