Feat/#43 자동 로그인 구현#44
Hidden character warning
Conversation
Walkthrough토큰 기반 인증자와 토큰 재발급 서비스, 데이터스토어 연동을 추가하고 스플래시에서 자동 로그인(토큰 재발급) 흐름을 구현합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as HTTP Client
participant TokenAuth as TokenAuthenticator
participant DataStore as Token DataStore
participant RefreshSvc as TokenRefreshService
Client->>TokenAuth: authenticate(response)
TokenAuth->>DataStore: read current tokens
DataStore-->>TokenAuth: accessToken, refreshToken
alt valid newer accessToken exists
TokenAuth->>Client: retry request with latest accessToken
else refresh needed
TokenAuth->>TokenAuth: enter synchronized refreshLock
TokenAuth->>RefreshSvc: refreshTokenData(refreshToken)
RefreshSvc-->>TokenAuth: TokenRefreshResponse(success) / error
alt refresh success
TokenAuth->>DataStore: write new access/refresh tokens & userId
DataStore-->>TokenAuth: ack
TokenAuth->>Client: retry request with new accessToken
else refresh failure
TokenAuth->>DataStore: clear auth data
TokenAuth->>Client: return null (stop retry)
end
end
sequenceDiagram
participant Splash as SplashScreen
participant VM as SplashViewModel
participant Repo as TokenRepository
participant DataStore as Token DataStore
Splash->>VM: tryAutoLogin()
VM->>VM: set hasStartedAutoLogin, record startTime
VM->>Repo: refreshTokenData()
Repo->>DataStore: read stored tokens
DataStore-->>Repo: accessToken, refreshToken
Repo->>RefreshSvc: (via network) request reissue
RefreshSvc-->>Repo: success / failure
alt success
Repo-->>VM: Result.success
VM->>VM: emit NavigateToHome, uiState.successAutoLogin = true
Splash->>App: navigateToHome()
else failure
Repo-->>VM: Result.failure
VM->>VM: emit NavigateToLanding, uiState.successAutoLogin = false
Splash->>App: navigateToLanding()
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. 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: 3
🤖 Fix all issues with AI agents
In `@core/network/src/main/java/com/umcspot/spot/network/TokenAuthenticator.kt`:
- Around line 62-69: The current runBlocking block around
tokenRefreshService.refreshTokenData(refreshToken) swallows exceptions without
logging, hindering diagnostics; update the catch to log the caught exception
(including message and stack trace) before calling clearAuthData() and returning
null — reference the TokenAuthenticator class, the runBlocking block where
refreshResponse is assigned, tokenRefreshService.refreshTokenData(...), and
clearAuthData() so you add a logger call (e.g., logger.error or appropriate
logging utility used in this class) that includes the exception object.
In `@feature/mypage/src/main/java/com/umcspot/spot/mypage/main/MyPageScreen.kt`:
- Around line 96-101: The logout flow currently calls viewmodel.logout() but
ignores its Result and always invokes onLogoutClick(), so handle the Result from
viewmodel.logout() inside the scope.launch: call onLogoutClick() only when the
Result indicates success; on failure, do not call onLogoutClick() and instead
surface the error (e.g., show a toast/snackbar or log via viewmodel/UiEvent) so
the user is informed and server session cleanup is respected; update the block
surrounding onLogoutClick, scope.launch, and viewmodel.logout() to branch on
success/failure accordingly.
In
`@feature/signup/src/main/java/com/umcspot/spot/signup/landing/LandingScreen.kt`:
- Around line 58-60: The screen currently calls viewModel.tryAutoLogin() inside
LaunchedEffect but provides no UI feedback while viewModel.isLoading is true;
update the LandingScreen composable to observe the viewModel.isLoading state and
render a visible loading indicator or full-screen overlay (e.g., a centered
CircularProgressIndicator or semi-opaque overlay that blocks input) while
isLoading is true, disabling buttons beneath; keep the
LaunchedEffect/tryAutoLogin call as-is and ensure the loading UI is
conditionally shown/hidden based on the viewModel.isLoading property.
🧹 Nitpick comments (1)
data/login/src/main/java/com/umcspot/spot/login/repositoryimpl/LoginRepositoryImpl.kt (1)
14-18: 파라미터 네이밍 개선 필요
loginDataStore파라미터가 실제로는LoginDataSource타입입니다.loginDataSource로 이름을 변경하면 코드의 명확성이 향상됩니다.🔧 제안하는 수정
class LoginRepositoryImpl `@Inject` constructor( - private val loginDataStore: LoginDataSource, + private val loginDataSource: LoginDataSource, private val spotTokenDataStore: DataStore<SpotTokenData>, private val spotUserIdDataStore: DataStore<SpotUserIdData> ) : TokenRepository {
| val refreshResponse = runBlocking { | ||
| try { | ||
| tokenRefreshService.refreshTokenData(refreshToken) | ||
| } catch (e: Exception) { | ||
| clearAuthData() | ||
| null | ||
| } | ||
| } |
There was a problem hiding this comment.
예외가 삼켜지고 있어 디버깅이 어려움
토큰 갱신 실패 시 예외가 로깅 없이 삼켜지고 있습니다. 프로덕션 환경에서 토큰 갱신 문제를 진단하기 어려워질 수 있습니다.
🛠️ 로깅 추가 제안
val refreshResponse = runBlocking {
try {
tokenRefreshService.refreshTokenData(refreshToken)
} catch (e: Exception) {
+ android.util.Log.e("TokenAuthenticator", "Token refresh failed", e)
clearAuthData()
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.
| val refreshResponse = runBlocking { | |
| try { | |
| tokenRefreshService.refreshTokenData(refreshToken) | |
| } catch (e: Exception) { | |
| clearAuthData() | |
| null | |
| } | |
| } | |
| val refreshResponse = runBlocking { | |
| try { | |
| tokenRefreshService.refreshTokenData(refreshToken) | |
| } catch (e: Exception) { | |
| android.util.Log.e("TokenAuthenticator", "Token refresh failed", e) | |
| clearAuthData() | |
| null | |
| } | |
| } |
🧰 Tools
🪛 detekt (1.23.8)
[warning] 65-65: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🤖 Prompt for AI Agents
In `@core/network/src/main/java/com/umcspot/spot/network/TokenAuthenticator.kt`
around lines 62 - 69, The current runBlocking block around
tokenRefreshService.refreshTokenData(refreshToken) swallows exceptions without
logging, hindering diagnostics; update the catch to log the caught exception
(including message and stack trace) before calling clearAuthData() and returning
null — reference the TokenAuthenticator class, the runBlocking block where
refreshResponse is assigned, tokenRefreshService.refreshTokenData(...), and
clearAuthData() so you add a logger call (e.g., logger.error or appropriate
logging utility used in this class) that includes the exception object.
| onLogoutClick = { | ||
| scope.launch { | ||
| viewmodel.logout() | ||
| onLogoutClick() | ||
| } | ||
| } |
There was a problem hiding this comment.
로그아웃 실패 시 에러 처리 누락
viewmodel.logout()의 Result를 처리하지 않고 있습니다. API 호출이 실패해도 onLogoutClick()이 호출되어 사용자는 로그아웃이 완료된 것으로 인식하지만, 실제로는 서버 세션이 정리되지 않을 수 있습니다.
🐛 에러 처리 추가 제안
onLogoutClick = {
scope.launch {
- viewmodel.logout()
- onLogoutClick()
+ viewmodel.logout()
+ .onSuccess {
+ onLogoutClick()
+ }
+ .onFailure {
+ // TODO: 로그아웃 실패 시 사용자에게 알림 (예: Snackbar, Toast)
+ }
}
}🤖 Prompt for AI Agents
In `@feature/mypage/src/main/java/com/umcspot/spot/mypage/main/MyPageScreen.kt`
around lines 96 - 101, The logout flow currently calls viewmodel.logout() but
ignores its Result and always invokes onLogoutClick(), so handle the Result from
viewmodel.logout() inside the scope.launch: call onLogoutClick() only when the
Result indicates success; on failure, do not call onLogoutClick() and instead
surface the error (e.g., show a toast/snackbar or log via viewmodel/UiEvent) so
the user is informed and server session cleanup is respected; update the block
surrounding onLogoutClick, scope.launch, and viewmodel.logout() to branch on
success/failure accordingly.
| LaunchedEffect(Unit) { | ||
| viewModel.tryAutoLogin() | ||
| } |
There was a problem hiding this comment.
자동 로그인 중 UI 피드백이 없어 혼동될 수 있습니다.
자동 로그인 동안 isLoading이 true인데 화면에는 변화가 없어 버튼이 무반응처럼 보일 수 있습니다. 로딩 인디케이터/오버레이 등 최소한의 피드백을 추가해 주세요.
🤖 Prompt for AI Agents
In
`@feature/signup/src/main/java/com/umcspot/spot/signup/landing/LandingScreen.kt`
around lines 58 - 60, The screen currently calls viewModel.tryAutoLogin() inside
LaunchedEffect but provides no UI feedback while viewModel.isLoading is true;
update the LandingScreen composable to observe the viewModel.isLoading state and
render a visible loading indicator or full-screen overlay (e.g., a centered
CircularProgressIndicator or semi-opaque overlay that blocks input) while
isLoading is true, disabling buttons beneath; keep the
LaunchedEffect/tryAutoLogin call as-is and ensure the loading UI is
conditionally shown/hidden based on the viewModel.isLoading property.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
feature/signup/src/main/java/com/umcspot/spot/signup/landing/LandingViewModel.kt (3)
69-69:⚠️ Potential issue | 🟠 Major액세스 토큰이 로그에 노출되고 있습니다.
Line 69와 Line 87에서 카카오/네이버 액세스 토큰을
Log.i로 출력하고 있습니다. 프로덕션 빌드에서도 노출될 수 있으므로 토큰 값을 로그에 포함하지 않거나, 최소한Log.d로 변경하고 릴리스 빌드에서는 제거되도록 처리해 주세요.Also applies to: 87-87
64-75:⚠️ Potential issue | 🟡 Minor카카오 콜백에서
error == null && token == null인 경우 처리가 누락되어 UI가 빈 화면으로 남을 수 있습니다.
error와token이 모두null인 경우 어느 분기도 실행되지 않아successAutoLogin이true로 유지되고, 사용자에게 빈 화면이 보입니다.🛡️ else 분기 추가 제안
UserApiClient.instance.loginWithKakaoAccount(context) { token, error -> if (error != null) { handleLoginError("카카오 로그인 실패", error) } else if (token != null) { Log.i(TAG, "카카오 로그인 성공: ${token.accessToken}") requestServerLogin(SocialLoginType.KAKAO, token.accessToken) + } else { + handleLoginError("카카오 로그인: 알 수 없는 오류") } }
62-76:⚠️ Potential issue | 🟠 Major
loginWithKakao()에Activity컨텍스트 전달이 필요합니다.
loginWithKakao()는 생성자에서 주입된@ApplicationContext context를 사용하지만, Kakao SDK의loginWithKakaoAccount는 로그인/동의 화면을 표시하기 위해 Activity 컨텍스트가 필요합니다.startSocialLogin()에서 받은activity파라미터가 전달되지 않고 있으며, 비교하면loginWithNaver(activity)는 올바르게 activity를 전달하고 있습니다.또한 접근 토큰이 로그에 기록되고 있습니다 (69줄, 87줄). 민감한 토큰은 로그에 남기지 않아야 합니다.
🐛 수정 제안
- private fun loginWithKakao() = viewModelScope.launch { + private fun loginWithKakao(activity: Activity) = viewModelScope.launch { try { - UserApiClient.instance.loginWithKakaoAccount(context) { token, error -> + UserApiClient.instance.loginWithKakaoAccount(activity) { token, error -> if (error != null) { handleLoginError("카카오 로그인 실패", error) } else if (token != null) { - Log.i(TAG, "카카오 로그인 성공: ${token.accessToken}") + Log.i(TAG, "카카오 로그인 성공") requestServerLogin(SocialLoginType.KAKAO, token.accessToken)
startSocialLogin호출부도 수정:when (type) { - SocialLoginType.KAKAO -> loginWithKakao() + SocialLoginType.KAKAO -> loginWithKakao(activity) SocialLoginType.NAVER -> loginWithNaver(activity) }네이버 로그인도 같은 방식으로 수정:
Log.i(TAG, "네이버 로그인 성공: $accessToken") + Log.i(TAG, "네이버 로그인 성공")
🧹 Nitpick comments (2)
feature/signup/src/main/java/com/umcspot/spot/signup/landing/LandingState.kt (1)
3-5:successAutoLogin네이밍이 실제 용도와 불일치합니다.ViewModel 코드를 보면
successAutoLogin은 로그인 시도 전에true로 설정되고, 성공/실패 여부와 관계없이 완료 후false로 재설정됩니다. 이는 의미적으로isLoading또는isAutoLoginInProgress에 해당하며, "성공"을 나타내는 플래그가 아닙니다. 현재 이름은 코드 읽는 사람에게 혼란을 줄 수 있습니다.♻️ 네이밍 개선 제안
data class LandingState( - val successAutoLogin: Boolean = false, + val isLoginInProgress: Boolean = false, )feature/signup/src/main/java/com/umcspot/spot/signup/landing/LandingViewModel.kt (1)
36-48:tryAutoLogin()실패 시 사용자에게 피드백이 없습니다 — 의도된 동작인지 확인 필요.자동 로그인 실패 시 조용히 Landing 화면을 보여주는 것이 의도된 동작이라면 괜찮지만, 네트워크 오류 등의 경우 사용자에게 알림이 필요할 수 있습니다. 현재 실패 시 snackbar 등의 피드백 없이 단순히 상태만 리셋됩니다.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashScreen.kt`:
- Around line 56-74: You create snackBarHostState but never attach it to the UI,
so showSnackbar calls do nothing; pass snackBarHostState into the SplashScreen
composable (e.g., add a parameter like snackBarHostState: SnackbarHostState) and
update the SplashScreen implementation to include a SnackbarHost (or a Scaffold
with snackbarHost) using that state so the launched LaunchedEffect uses the
attached host; update the call site (SplashScreen()) to pass snackBarHostState
and keep the existing LaunchedEffect(viewModel.sideEffect) logic unchanged.
In `@feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashState.kt`:
- Around line 3-10: The classes in this file are misnamed for the splash
package: rename data class LandingState to SplashState (keeping the
successAutoLogin property) and rename sealed interface LandingSideEffect to
SplashSideEffect (keeping members NavigateToHome, NavigateToLanding and
ShowSnackBar(message: String)), then update all references/imports across the
codebase to use SplashState and SplashSideEffect instead of
LandingState/LandingSideEffect so they no longer collide with the landing
package equivalents; ensure constructors, type usages, and any when/switch
branches or tests that pattern-match on LandingSideEffect members are updated to
the new Splash* names.
In
`@feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashViewModel.kt`:
- Around line 33-34: The MutableSharedFlow _sideEffect is created with default
replay/extraBufferCapacity which can lose emissions if tryAutoLogin() emits
before SplashRoute's LaunchedEffect collector is registered; change the creation
of _sideEffect (MutableSharedFlow<LandingSideEffect>) to include a non-zero
buffer (e.g., replay=1 or extraBufferCapacity=1) so emitted LandingSideEffect
values are retained until the sideEffect collector in
SplashRoute/LaunchedEffect(viewModel.sideEffect) subscribes, or alternatively
refactor ordering so tryAutoLogin() runs after the collector is registered.
- Around line 36-49: tryAutoLogin currently ignores exception details and can
crash if loginRepository.refreshTokenData throws; wrap the call inside
viewModelScope.launch with try/catch (or use runCatching) around
loginRepository.refreshTokenData(), log the caught Throwable (using your app
logger or Log/TAG) including message and stacktrace, and in the catch/failure
path emit LandingSideEffect.NavigateToLanding and set _uiState.update {
it.copy(successAutoLogin = false) } so failures never crash the coroutine;
update references: tryAutoLogin, loginRepository.refreshTokenData,
_sideEffect.emit, and _uiState.update.
🧹 Nitpick comments (2)
feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashViewModel.kt (1)
3-22: 사용되지 않는 import 및 의존성 정리 필요
Activity,ContentValues.TAG,Context(직접 사용),Log,UserApiClient,NidOAuth,NidOAuthCallback,SocialLoginType등 다수의 import가 사용되지 않고 있습니다. 또한 생성자에 주입된context도 현재 사용되지 않습니다. 다른 ViewModel에서 복사한 흔적으로 보입니다.♻️ 불필요한 import 및 의존성 제거
package com.umcspot.spot.signup.splash -import android.app.Activity -import android.content.ContentValues.TAG -import android.content.Context -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.kakao.sdk.user.UserApiClient -import com.navercorp.nid.NidOAuth -import com.navercorp.nid.oauth.util.NidOAuthCallback -import com.umcspot.spot.model.SocialLoginType import com.umcspot.spot.token.repository.TokenRepository import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject `@HiltViewModel` class SplashViewModel `@Inject` constructor( private val loginRepository: TokenRepository, - `@ApplicationContext` private val context: Context ) : ViewModel() {feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashScreen.kt (1)
3-42: 다수의 사용되지 않는 import 정리 필요
Activity,Image,Arrangement,PaddingValues,Spacer,WindowInsets,fillMaxWidth,height,padding,size,systemBars,Scaffold,SnackbarHost,Text,ContentScale,painterResource,TextAlign,dp, 디자인 시스템 리소스 (R,KakaoStartButton,NaverStartButton,B500),SocialLoginType,screenHeightDp,screenWidthDp등 대부분의 import가 현재 사용되지 않습니다. 미완성 상태인 것은 이해하지만, 작업 완료 시 정리해 주세요.
| val snackBarHostState = remember { SnackbarHostState() } | ||
|
|
||
| LaunchedEffect(Unit) { | ||
| viewModel.tryAutoLogin() | ||
| } | ||
|
|
||
| LaunchedEffect(viewModel.sideEffect) { | ||
| viewModel.sideEffect.collectLatest { effect -> | ||
| when (effect) { | ||
| is LandingSideEffect.NavigateToHome -> navigateToHome() | ||
| is LandingSideEffect.NavigateToLanding -> navigateToLanding() | ||
| is LandingSideEffect.ShowSnackBar -> { | ||
| snackBarHostState.showSnackbar(effect.message) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| SplashScreen() |
There was a problem hiding this comment.
snackBarHostState가 생성만 되고 UI 트리에 연결되지 않음
Line 56에서 SnackbarHostState를 생성하고 Line 68에서 showSnackbar를 호출하지만, SplashScreen() 컴포저블에 SnackbarHost가 포함되어 있지 않아 스낵바가 실제로 표시되지 않습니다.
🐛 SplashScreen에 SnackbarHost 연결 제안
`@Composable`
-fun SplashScreen(
-) {
- Column(
+fun SplashScreen(
+ snackBarHostState: SnackbarHostState = remember { SnackbarHostState() },
+) {
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackBarHostState) },
+ ) { paddingValues ->
+ Column(
modifier = Modifier
.fillMaxSize()
.background(SpotTheme.colors.white)
- ) {
-
+ .padding(paddingValues)
+ ) {
+ }
}
}🤖 Prompt for AI Agents
In `@feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashScreen.kt`
around lines 56 - 74, You create snackBarHostState but never attach it to the
UI, so showSnackbar calls do nothing; pass snackBarHostState into the
SplashScreen composable (e.g., add a parameter like snackBarHostState:
SnackbarHostState) and update the SplashScreen implementation to include a
SnackbarHost (or a Scaffold with snackbarHost) using that state so the launched
LaunchedEffect uses the attached host; update the call site (SplashScreen()) to
pass snackBarHostState and keep the existing
LaunchedEffect(viewModel.sideEffect) logic unchanged.
feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashState.kt
Outdated
Show resolved
Hide resolved
| private val _sideEffect = MutableSharedFlow<LandingSideEffect>() | ||
| val sideEffect = _sideEffect.asSharedFlow() |
There was a problem hiding this comment.
MutableSharedFlow의 기본 버퍼 설정으로 인한 사이드 이펙트 유실 가능성
MutableSharedFlow<LandingSideEffect>()는 replay=0, extraBufferCapacity=0으로 생성됩니다. SplashRoute에서 LaunchedEffect(viewModel.sideEffect)로 collector를 등록하기 전에 LaunchedEffect(Unit)에서 tryAutoLogin()이 호출되면, emit된 사이드 이펙트가 collector가 준비되기 전에 발생하여 유실될 수 있습니다.
이 경우 사용자는 splash 화면에 영원히 머물게 됩니다.
🐛 수정 제안: replay 또는 extraBufferCapacity 설정
- private val _sideEffect = MutableSharedFlow<LandingSideEffect>()
+ private val _sideEffect = MutableSharedFlow<LandingSideEffect>(extraBufferCapacity = 1)또는 SplashScreen.kt에서 side effect 수집과 auto-login 트리거 순서를 보장하도록 리팩터링하는 방법도 있습니다.
📝 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.
| private val _sideEffect = MutableSharedFlow<LandingSideEffect>() | |
| val sideEffect = _sideEffect.asSharedFlow() | |
| private val _sideEffect = MutableSharedFlow<LandingSideEffect>(extraBufferCapacity = 1) | |
| val sideEffect = _sideEffect.asSharedFlow() |
🤖 Prompt for AI Agents
In
`@feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashViewModel.kt`
around lines 33 - 34, The MutableSharedFlow _sideEffect is created with default
replay/extraBufferCapacity which can lose emissions if tryAutoLogin() emits
before SplashRoute's LaunchedEffect collector is registered; change the creation
of _sideEffect (MutableSharedFlow<LandingSideEffect>) to include a non-zero
buffer (e.g., replay=1 or extraBufferCapacity=1) so emitted LandingSideEffect
values are retained until the sideEffect collector in
SplashRoute/LaunchedEffect(viewModel.sideEffect) subscribes, or alternatively
refactor ordering so tryAutoLogin() runs after the collector is registered.
| fun tryAutoLogin() { | ||
| _uiState.update { it.copy(successAutoLogin = true) } | ||
|
|
||
| viewModelScope.launch { | ||
| val result = loginRepository.refreshTokenData() | ||
| if (result.isSuccess) { | ||
| _sideEffect.emit(LandingSideEffect.NavigateToHome) | ||
| _uiState.update { it.copy(successAutoLogin = false) } | ||
| } else { | ||
| _sideEffect.emit(LandingSideEffect.NavigateToLanding) | ||
| _uiState.update { it.copy(successAutoLogin = false) } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
tryAutoLogin() 실패 시 에러 로깅 및 예외 처리 부재
refreshTokenData()가 실패한 경우 결과에 포함된 예외 정보를 로깅하지 않아 디버깅이 어렵습니다. 또한 refreshTokenData() 자체에서 예외가 throw되면 코루틴이 crash할 수 있습니다.
🛡️ 에러 처리 개선 제안
fun tryAutoLogin() {
_uiState.update { it.copy(successAutoLogin = true) }
viewModelScope.launch {
- val result = loginRepository.refreshTokenData()
- if (result.isSuccess) {
- _sideEffect.emit(LandingSideEffect.NavigateToHome)
- _uiState.update { it.copy(successAutoLogin = false) }
- } else {
- _sideEffect.emit(LandingSideEffect.NavigateToLanding)
- _uiState.update { it.copy(successAutoLogin = false) }
- }
+ runCatching { loginRepository.refreshTokenData() }
+ .onSuccess { result ->
+ if (result.isSuccess) {
+ _sideEffect.emit(LandingSideEffect.NavigateToHome)
+ } else {
+ _sideEffect.emit(LandingSideEffect.NavigateToLanding)
+ }
+ }
+ .onFailure {
+ Log.e("SplashViewModel", "Auto login failed", it)
+ _sideEffect.emit(LandingSideEffect.NavigateToLanding)
+ }
+ _uiState.update { it.copy(successAutoLogin = false) }
}
}🤖 Prompt for AI Agents
In
`@feature/signup/src/main/java/com/umcspot/spot/signup/splash/SplashViewModel.kt`
around lines 36 - 49, tryAutoLogin currently ignores exception details and can
crash if loginRepository.refreshTokenData throws; wrap the call inside
viewModelScope.launch with try/catch (or use runCatching) around
loginRepository.refreshTokenData(), log the caught Throwable (using your app
logger or Log/TAG) including message and stacktrace, and in the catch/failure
path emit LandingSideEffect.NavigateToLanding and set _uiState.update {
it.copy(successAutoLogin = false) } so failures never crash the coroutine;
update references: tryAutoLogin, loginRepository.refreshTokenData,
_sideEffect.emit, and _uiState.update.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt (1)
143-148:⚠️ Potential issue | 🟠 Major로그아웃 시 토큰/세션 데이터 삭제 로직이 누락되어 있습니다.
주석에 "로그아웃 처리(데이터 삭제) 트리거"라고 적혀 있지만 실제 토큰 삭제 로직이 없습니다. Landing으로 네비게이션만 수행하면 DataStore에 저장된 토큰이 남아 있어, 다음 앱 실행 시 Splash에서 자동 로그인이 성공하게 됩니다.
토큰 삭제 로직 구현을 도와드릴까요? 또는 이 작업을 추적할 이슈를 생성할까요?
Related issue 🛠
Work Description 📝
Screenshot 📸
Uncompleted Tasks 😅
PR Point 📌
트러블 슈팅 💥
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정
개선사항