[Feat/#210] authenticator 도입#211
Conversation
… logic in interceptor
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a dedicated authenticator pattern to handle token refresh logic and implements an app restart mechanism. The changes separate authentication concerns from the main network interceptor and provide a cleaner architecture for handling authentication failures.
Key changes:
- Extracted token refresh logic from TokenInterceptor to a dedicated TokenAuthenticator
- Implemented AppReStarter interface and implementation for app restart functionality
- Added utility functions for suspend error handling
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| TokenAuthenticator.kt | New authenticator handling 401 responses and token refresh logic |
| TokenInterceptor.kt | Simplified to only add authorization headers, removed refresh logic |
| AppReStarter.kt & DefaultAppReStarter.kt | New app restart abstraction with toast functionality |
| runSuspendCatching.kt | Utility function for suspend error handling |
| RetrofitModule.kt | Updated DI configuration to support authenticator pattern |
| getErrorMessage.kt | Added logging for error tracking |
| ?.component | ||
| ?.let(Intent::makeRestartActivityTask) | ||
|
|
||
| context.startActivity(restartIntent) |
There was a problem hiding this comment.
The restartIntent could be null if getLaunchIntentForPackage returns null or if the component is null. This will cause a crash when passed to startActivity. Add a null check before calling startActivity.
| context.startActivity(restartIntent) | |
| if (restartIntent != null) { | |
| context.startActivity(restartIntent) | |
| } else { | |
| Toast.makeText(context, "Unable to restart the app.", Toast.LENGTH_SHORT).show() | |
| } |
| private val scope = CoroutineScope(Dispatchers.Main) | ||
|
|
There was a problem hiding this comment.
Creating a CoroutineScope with Dispatchers.Main without proper lifecycle management can lead to memory leaks. Consider using a supervised scope or injecting a properly managed CoroutineScope.
| private val scope = CoroutineScope(Dispatchers.Main) | |
| private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) | |
| fun close() { | |
| scope.cancel() | |
| } |
| runSuspendCatching { | ||
| loginService.postReissueToken(refreshToken) | ||
| }.onSuccess { | ||
| val newAccess = it.data?.accessToken.orEmpty() |
There was a problem hiding this comment.
Using orEmpty() for accessToken means an empty string will be saved and used for authentication if the token is null, which will likely cause authentication failures. Consider returning null or throwing an exception when accessToken is null.
| val newAccess = it.data?.accessToken.orEmpty() | |
| val newAccess = it.data?.accessToken ?: return null |
| scope.launch { | ||
| val restartIntent = context.packageManager | ||
| .getLaunchIntentForPackage(context.packageName) | ||
| ?.component | ||
| ?.let(Intent::makeRestartActivityTask) | ||
|
|
||
| context.startActivity(restartIntent) | ||
| } |
There was a problem hiding this comment.
The restartApp() method launches a coroutine but doesn't need to be async since startActivity is a synchronous operation. The coroutine launch is unnecessary and could potentially cause timing issues.
| scope.launch { | |
| val restartIntent = context.packageManager | |
| .getLaunchIntentForPackage(context.packageName) | |
| ?.component | |
| ?.let(Intent::makeRestartActivityTask) | |
| context.startActivity(restartIntent) | |
| } | |
| val restartIntent = context.packageManager | |
| .getLaunchIntentForPackage(context.packageName) | |
| ?.component | |
| ?.let(Intent::makeRestartActivityTask) | |
| context.startActivity(restartIntent) |
| scope.launch { | ||
| currentToast?.cancel() | ||
| currentToast = Toast.makeText(context, message, Toast.LENGTH_SHORT) | ||
| currentToast?.show() | ||
| } |
There was a problem hiding this comment.
The makeToast() method launches a coroutine but doesn't need to be async since Toast operations are already main-thread safe. The coroutine launch is unnecessary.
| scope.launch { | |
| currentToast?.cancel() | |
| currentToast = Toast.makeText(context, message, Toast.LENGTH_SHORT) | |
| currentToast?.show() | |
| } | |
| currentToast?.cancel() | |
| currentToast = Toast.makeText(context, message, Toast.LENGTH_SHORT) | |
| currentToast?.show() |
✅ 𝗖𝗵𝗲𝗰𝗸-𝗟𝗶𝘀𝘁
📌 𝗜𝘀𝘀𝘂𝗲𝘀
📎𝗪𝗼𝗿𝗸 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻
📷 𝗦𝗰𝗿𝗲𝗲𝗻𝘀𝗵𝗼𝘁
💬 𝗧𝗼 𝗥𝗲𝘃𝗶𝗲𝘄𝗲𝗿𝘀
잘 지내셨죠?
Android 15(API 수준 35) -> 이거 하려고 한건데 다른거 했네요
서버가 내려간 것 같아 테스트는 불가..