diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 40c235b5..1d05b3e1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -67,7 +67,7 @@ dependencies { implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation(libs.androidx.navigation.compose) - implementation(libs.androidx.foundation) + //implementation(libs.androidx.foundation) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.navigation.runtime.android) implementation(libs.kotlinx.serialization.json) @@ -91,7 +91,7 @@ dependencies { // Retrofit implementation(libs.retrofit) - implementation(libs.converter.gson) + //implementation(libs.converter.gson) implementation(libs.retrofit.kotlin.serialization.converter) // OkHttp @@ -102,7 +102,7 @@ dependencies { implementation("com.kakao.sdk:v2-all:2.21.6") // 토큰 저장을 위한 DataStore - implementation ("androidx.datastore:datastore-preferences:1.1.1") + //implementation ("androidx.datastore:datastore-preferences:1.1.1") // 구글 로그인 implementation(platform("com.google.firebase:firebase-bom:34.1.0")) diff --git a/app/src/main/java/com/texthip/thip/MainActivity.kt b/app/src/main/java/com/texthip/thip/MainActivity.kt index cc0d9d64..a6e16e42 100644 --- a/app/src/main/java/com/texthip/thip/MainActivity.kt +++ b/app/src/main/java/com/texthip/thip/MainActivity.kt @@ -40,7 +40,28 @@ fun RootNavHost() { startDestination = CommonRoutes.Splash ) { // --- 인증 관련 화면들 --- - authNavigation(navController) + authNavigation( + onNavigateToLogin = { + navController.navigate(CommonRoutes.Login) { + popUpTo(CommonRoutes.Splash) { inclusive = true } + } + }, + onNavigateToHome = { + navController.navigate(CommonRoutes.Main) { + popUpTo(CommonRoutes.Splash) { inclusive = true } + } + }, + onNavigateToSignup = { + navController.navigate(CommonRoutes.SignupFlow) + }, + onNavigateToMainAfterSignup = { + navController.navigate(CommonRoutes.Main) { // 혹은 MainGraph + popUpTo(CommonRoutes.Splash) { inclusive = true } + } + }, + navController = navController + ) + // --- 메인 관련 화면들 --- composable { // MainScreen으로 가는 경로 추가 @@ -48,7 +69,7 @@ fun RootNavHost() { onNavigateToLogin = { navController.navigate(CommonRoutes.Login) { // 메인 화면으로 돌아올 수 없도록 모든 화면 기록 삭제 - popUpTo(navController.graph.id) { + popUpTo(CommonRoutes.Main) { inclusive = true } } diff --git a/app/src/main/java/com/texthip/thip/ThipApplication.kt b/app/src/main/java/com/texthip/thip/ThipApplication.kt index 27619c91..58da5b7b 100644 --- a/app/src/main/java/com/texthip/thip/ThipApplication.kt +++ b/app/src/main/java/com/texthip/thip/ThipApplication.kt @@ -1,13 +1,17 @@ package com.texthip.thip -import com.kakao.sdk.common.KakaoSdk import android.app.Application +import com.kakao.sdk.common.KakaoSdk +import com.texthip.thip.data.manager.TokenManager import dagger.hilt.android.HiltAndroidApp -import com.texthip.thip.BuildConfig +import javax.inject.Inject @HiltAndroidApp class ThipApplication : Application(){ + @Inject + lateinit var tokenManager: TokenManager + override fun onCreate() { super.onCreate() diff --git a/app/src/main/java/com/texthip/thip/data/di/DataStoreModule.kt b/app/src/main/java/com/texthip/thip/data/di/DataStoreModule.kt new file mode 100644 index 00000000..ab84768c --- /dev/null +++ b/app/src/main/java/com/texthip/thip/data/di/DataStoreModule.kt @@ -0,0 +1,25 @@ +package com.texthip.thip.data.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.preferencesDataStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import androidx.datastore.preferences.core.Preferences +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DataStoreModule { + + private val Context.dataStore: DataStore by preferencesDataStore(name = "thip_tokens") + + @Provides + @Singleton + fun provideDataStore(@ApplicationContext context: Context): DataStore { + return context.dataStore + } +} diff --git a/app/src/main/java/com/texthip/thip/data/manager/TokenManager.kt b/app/src/main/java/com/texthip/thip/data/manager/TokenManager.kt index 66d20968..db370f35 100644 --- a/app/src/main/java/com/texthip/thip/data/manager/TokenManager.kt +++ b/app/src/main/java/com/texthip/thip/data/manager/TokenManager.kt @@ -1,94 +1,69 @@ package com.texthip.thip.data.manager -import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.preferencesDataStore -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import javax.inject.Inject import javax.inject.Singleton -private val Context.dataStore: DataStore by preferencesDataStore(name = "thip_tokens") @Singleton class TokenManager @Inject constructor( - @ApplicationContext private val context: Context + private val dataStore: DataStore ) { companion object { - //토큰저장에 사용되는 키 - private val APP_TOKEN_KEY = stringPreferencesKey("app_token") - private val TEMP_TOKEN_KEY = stringPreferencesKey("temp_token") - private val ACCESS_TOKEN_KEY = stringPreferencesKey("access_token") + private val APP_TOKEN_KEY = stringPreferencesKey("app_token") // 정식 액세스토큰 + private val TEMP_TOKEN_KEY = stringPreferencesKey("temp_token") // 임시 토큰 private val REFRESH_TOKEN_KEY = stringPreferencesKey("refresh_token") } - //토큰 저장 + // ====== 정식 토큰 ====== suspend fun saveToken(token: String) { - context.dataStore.edit { prefs -> + dataStore.edit { prefs -> prefs[APP_TOKEN_KEY] = token } } - //저장된 토큰을 Flow 형태로 불러옴 fun getToken(): Flow { - return context.dataStore.data.map { prefs -> - prefs[APP_TOKEN_KEY] - } + return dataStore.data.map { prefs -> prefs[APP_TOKEN_KEY] } + } + + suspend fun getTokenOnce(): String? { + return dataStore.data.map { prefs -> prefs[APP_TOKEN_KEY] }.first() } - //저장된 토큰 삭제 (로그아웃 시?) suspend fun deleteToken() { - context.dataStore.edit { prefs -> - prefs.remove(APP_TOKEN_KEY) - } + dataStore.edit { prefs -> prefs.remove(APP_TOKEN_KEY) } } - // 임시 토큰 저장 + // ====== 임시 토큰 ====== suspend fun saveTempToken(token: String) { - context.dataStore.edit { prefs -> - prefs[TEMP_TOKEN_KEY] = token - } + dataStore.edit { prefs -> prefs[TEMP_TOKEN_KEY] = token } } - // 임시 토큰 읽기 - suspend fun getTempToken(): String? { - return context.dataStore.data.map { prefs -> - prefs[TEMP_TOKEN_KEY] - }.first() // Flow에서 첫 번째 값을 한번만 읽어옴 + suspend fun getTempTokenOnce(): String? { + return dataStore.data.map { prefs -> prefs[TEMP_TOKEN_KEY] }.first() } - // 임시 토큰 삭제 suspend fun deleteTempToken() { - context.dataStore.edit { prefs -> - prefs.remove(TEMP_TOKEN_KEY) - } + dataStore.edit { prefs -> prefs.remove(TEMP_TOKEN_KEY) } } - - // 정식 토큰들(Access, Refresh) 저장 - suspend fun saveAccessTokens(accessToken: String, refreshToken: String) { - context.dataStore.edit { prefs -> - prefs[ACCESS_TOKEN_KEY] = accessToken - prefs[REFRESH_TOKEN_KEY] = refreshToken - } + // ====== Refresh 토큰 (추후 확장용) ====== + suspend fun saveRefreshToken(token: String) { + dataStore.edit { prefs -> prefs[REFRESH_TOKEN_KEY] = token } } - // Access Token 읽기 (Flow로 제공하여 토큰 변화를 감지할 수 있게 함) - fun getAccessToken(): kotlinx.coroutines.flow.Flow { - return context.dataStore.data.map { prefs -> - prefs[ACCESS_TOKEN_KEY] - } + suspend fun getRefreshTokenOnce(): String? { + return dataStore.data.map { prefs -> prefs[REFRESH_TOKEN_KEY] }.first() } - // 모든 토큰 삭제 (로그아웃 시) suspend fun clearTokens() { - context.dataStore.edit { prefs -> - prefs.clear() - } + dataStore.edit { prefs -> prefs.clear() } } -} \ No newline at end of file +} + diff --git a/app/src/main/java/com/texthip/thip/data/model/users/response/AliasChoiceResponse.kt b/app/src/main/java/com/texthip/thip/data/model/users/response/AliasChoiceResponse.kt index b4753a6f..1529507b 100644 --- a/app/src/main/java/com/texthip/thip/data/model/users/response/AliasChoiceResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/users/response/AliasChoiceResponse.kt @@ -1,17 +1,17 @@ package com.texthip.thip.data.model.users.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class AliasChoiceResponse( - @SerializedName("aliasChoices") val aliasChoices: List + @SerialName("aliasChoices") val aliasChoices: List ) @Serializable data class AliasChoice( - @SerializedName("aliasName") val aliasName: String, - @SerializedName("categoryName") val categoryName: String, - @SerializedName("imageUrl") val imageUrl: String, - @SerializedName("aliasColor") val aliasColor: String + @SerialName("aliasName") val aliasName: String, + @SerialName("categoryName") val categoryName: String, + @SerialName("imageUrl") val imageUrl: String, + @SerialName("aliasColor") val aliasColor: String ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/users/response/MyFollowingsResponse.kt b/app/src/main/java/com/texthip/thip/data/model/users/response/MyFollowingsResponse.kt index aa96cb6e..b16a183a 100644 --- a/app/src/main/java/com/texthip/thip/data/model/users/response/MyFollowingsResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/users/response/MyFollowingsResponse.kt @@ -1,22 +1,22 @@ package com.texthip.thip.data.model.users.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class MyFollowingsResponse( - @SerializedName("followings") val followings: List, - @SerializedName("totalFollowingCount") val totalFollowingCount: Int, - @SerializedName("nextCursor") val nextCursor: String?, - @SerializedName("isLast") val isLast: Boolean + @SerialName("followings") val followings: List, + @SerialName("totalFollowingCount") val totalFollowingCount: Int, + @SerialName("nextCursor") val nextCursor: String?, + @SerialName("isLast") val isLast: Boolean ) @Serializable data class FollowingList( - @SerializedName("userId") val userId: Long, - @SerializedName("nickname") val nickname: String, - @SerializedName("profileImageUrl") val profileImageUrl: String?, - @SerializedName("aliasName") val aliasName: String, - @SerializedName("aliasColor") val aliasColor: String, - @SerializedName("isFollowing") val isFollowing: Boolean + @SerialName("userId") val userId: Long, + @SerialName("nickname") val nickname: String, + @SerialName("profileImageUrl") val profileImageUrl: String?, + @SerialName("aliasName") val aliasName: String, + @SerialName("aliasColor") val aliasColor: String, + @SerialName("isFollowing") val isFollowing: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/users/response/OthersFollowersResponse.kt b/app/src/main/java/com/texthip/thip/data/model/users/response/OthersFollowersResponse.kt index 25c21ffa..502b9acc 100644 --- a/app/src/main/java/com/texthip/thip/data/model/users/response/OthersFollowersResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/users/response/OthersFollowersResponse.kt @@ -1,23 +1,23 @@ package com.texthip.thip.data.model.users.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class OthersFollowersResponse( - @SerializedName("followers") val followers: List, - @SerializedName("totalFollowerCount") val totalFollowerCount: Int, - @SerializedName("nextCursor") val nextCursor: String?, - @SerializedName("isLast") val isLast: Boolean + @SerialName("followers") val followers: List, + @SerialName("totalFollowerCount") val totalFollowerCount: Int, + @SerialName("nextCursor") val nextCursor: String?, + @SerialName("isLast") val isLast: Boolean ) @Serializable data class FollowerList( - @SerializedName("userId") val userId: Long, - @SerializedName("nickname") val nickname: String, - @SerializedName("profileImageUrl") val profileImageUrl: String?, - @SerializedName("aliasName") val aliasName: String, - @SerializedName("aliasColor") val aliasColor: String, - @SerializedName("followerCount") val followerCount: Int, - @SerializedName("isMyself") val isMyself: Boolean + @SerialName("userId") val userId: Long, + @SerialName("nickname") val nickname: String, + @SerialName("profileImageUrl") val profileImageUrl: String?, + @SerialName("aliasName") val aliasName: String, + @SerialName("aliasColor") val aliasColor: String, + @SerialName("followerCount") val followerCount: Int, + @SerialName("isMyself") val isMyself: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/users/response/SignupResponse.kt b/app/src/main/java/com/texthip/thip/data/model/users/response/SignupResponse.kt index 9d0c63d6..ac7ff41c 100644 --- a/app/src/main/java/com/texthip/thip/data/model/users/response/SignupResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/users/response/SignupResponse.kt @@ -1,10 +1,10 @@ package com.texthip.thip.data.model.users.response -import com.google.gson.annotations.SerializedName +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable data class SignupResponse( - @SerializedName("accessToken") val accessToken: String, - @SerializedName("userId") val userId: Long + @SerialName("accessToken") val accessToken: String, + @SerialName("userId") val userId: Long ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/model/users/response/UserSearchResponse.kt b/app/src/main/java/com/texthip/thip/data/model/users/response/UserSearchResponse.kt index 9267f7d7..0558eaff 100644 --- a/app/src/main/java/com/texthip/thip/data/model/users/response/UserSearchResponse.kt +++ b/app/src/main/java/com/texthip/thip/data/model/users/response/UserSearchResponse.kt @@ -1,7 +1,7 @@ package com.texthip.thip.data.model.users.response -import com.google.gson.annotations.SerializedName import kotlinx.serialization.Serializable +import kotlinx.serialization.SerialName @Serializable data class UserSearchResponse( @@ -9,10 +9,10 @@ data class UserSearchResponse( ) @Serializable data class UserItem( - @SerializedName("userId") val userId: Int, - @SerializedName("nickname") val nickname: String, - @SerializedName("profileImageUrl") val profileImageUrl: String?, - @SerializedName("aliasName") val aliasName: String, - @SerializedName("aliasColor") val aliasColor: String, - @SerializedName("followerCount") val followerCount: Int + @SerialName("userId") val userId: Int, + @SerialName("nickname") val nickname: String, + @SerialName("profileImageUrl") val profileImageUrl: String?, + @SerialName("aliasName") val aliasName: String, + @SerialName("aliasColor") val aliasColor: String, + @SerialName("followerCount") val followerCount: Int ) \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/data/repository/UserRepository.kt b/app/src/main/java/com/texthip/thip/data/repository/UserRepository.kt index 2b55e4f4..96afd970 100644 --- a/app/src/main/java/com/texthip/thip/data/repository/UserRepository.kt +++ b/app/src/main/java/com/texthip/thip/data/repository/UserRepository.kt @@ -115,7 +115,7 @@ class UserRepository @Inject constructor( suspend fun signup(request: SignupRequest): Result { Log.d("SignupDebug", "UserRepository.signup() 호출됨. 요청 닉네임: ${request.nickname}") - val tempToken = tokenManager.getTempToken() + val tempToken = tokenManager.getTempTokenOnce() Log.d("SignupDebug", "가져온 임시 토큰: $tempToken") diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt b/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt index 8ce331c8..4b884548 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.mypage.screen +import android.content.Context import android.content.Intent import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -58,6 +59,7 @@ fun MyPageScreen( onDeleteAccount: () -> Unit, onNavigateToLogin: () -> Unit ) { + val context = LocalContext.current val uiState by viewModel.uiState.collectAsState() LaunchedEffect(Unit) { viewModel.fetchMyPageInfo() @@ -75,7 +77,9 @@ fun MyPageScreen( onCustomerServiceClick = onCustomerService, onLogoutClick = { viewModel.onLogoutClick() }, onDismissLogoutDialog = { viewModel.onDismissLogoutDialog() }, - onConfirmLogout = { viewModel.confirmLogout() }, + onConfirmLogout = { viewModel.confirmLogout( + context = context + ) }, onDeleteAccount = onDeleteAccount ) } diff --git a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/MyPageViewModel.kt b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/MyPageViewModel.kt index 2cb54f5a..4cb77adc 100644 --- a/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/MyPageViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/mypage/viewmodel/MyPageViewModel.kt @@ -1,7 +1,12 @@ package com.texthip.thip.ui.mypage.viewmodel +import android.content.Context +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.google.android.gms.auth.api.signin.GoogleSignIn +import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import com.kakao.sdk.user.UserApiClient import com.texthip.thip.data.manager.TokenManager import com.texthip.thip.data.repository.UserRepository import dagger.hilt.android.lifecycle.HiltViewModel @@ -62,9 +67,22 @@ class MyPageViewModel @Inject constructor( _uiState.update { it.copy(showLogoutDialog = false) } } - fun confirmLogout() { + fun confirmLogout(context: Context) { viewModelScope.launch { tokenManager.clearTokens() + // 2. 카카오 SDK에서 로그아웃 + UserApiClient.instance.unlink { error -> + if (error != null) { + Log.e("MyPageViewModel", "카카오 로그아웃 실패", error) + } else { + Log.d("MyPageViewModel", "카카오 로그아웃 성공") + } + } + + // 3. 구글 SDK에서 로그아웃 + val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).build() + GoogleSignIn.getClient(context, gso).signOut() + _uiState.update { it.copy(showLogoutDialog = false, isLogoutCompleted = true) } } } diff --git a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/AuthNavigation.kt b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/AuthNavigation.kt index d4c1a89b..84662a7f 100644 --- a/app/src/main/java/com/texthip/thip/ui/navigator/navigations/AuthNavigation.kt +++ b/app/src/main/java/com/texthip/thip/ui/navigator/navigations/AuthNavigation.kt @@ -1,5 +1,6 @@ package com.texthip.thip.ui.navigator.navigations +import SplashScreen import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.hilt.navigation.compose.hiltViewModel @@ -8,29 +9,33 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import androidx.navigation.navigation -import com.texthip.thip.ui.navigator.extensions.navigateToLogin import com.texthip.thip.ui.navigator.extensions.navigateToMainAfterSignup -import com.texthip.thip.ui.navigator.extensions.navigateToSignup import com.texthip.thip.ui.navigator.routes.CommonRoutes import com.texthip.thip.ui.signin.mock.SignupUserInfo import com.texthip.thip.ui.signin.screen.LoginScreen import com.texthip.thip.ui.signin.screen.SignupDoneScreen import com.texthip.thip.ui.signin.screen.SignupGenreScreen import com.texthip.thip.ui.signin.screen.SignupNicknameScreen -import com.texthip.thip.ui.signin.screen.SplashScreen import com.texthip.thip.ui.signin.screen.TutorialScreen import com.texthip.thip.ui.signin.viewmodel.SignupViewModel -fun NavGraphBuilder.authNavigation(navController: NavHostController) { +fun NavGraphBuilder.authNavigation( + navController: NavHostController, + onNavigateToLogin: () -> Unit, + onNavigateToHome: () -> Unit, + onNavigateToSignup: () -> Unit, + onNavigateToMainAfterSignup: () -> Unit +) { composable { SplashScreen( - onNavigateToLogin = { navController.navigateToLogin() } + onNavigateToLogin = onNavigateToLogin, + onNavigateToHome = onNavigateToHome ) } composable { LoginScreen( - onNavigateToSignup = { navController.navigateToSignup() }, - onLoginSuccess = { navController.navigateToMainAfterSignup() } + onNavigateToSignup = onNavigateToSignup, + onLoginSuccess = onNavigateToMainAfterSignup ) } navigation( diff --git a/app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt b/app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt index ad79335c..ca14c54b 100644 --- a/app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt +++ b/app/src/main/java/com/texthip/thip/ui/signin/screen/SplashScreen.kt @@ -1,5 +1,3 @@ -package com.texthip.thip.ui.signin.screen - import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -24,6 +22,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.texthip.thip.R +import com.texthip.thip.ui.signin.viewmodel.SplashDestination import com.texthip.thip.ui.signin.viewmodel.SplashViewModel import com.texthip.thip.ui.theme.Purple import com.texthip.thip.ui.theme.ThipTheme.colors @@ -32,13 +31,16 @@ import com.texthip.thip.ui.theme.ThipTheme.typography @Composable fun SplashScreen( viewModel: SplashViewModel = hiltViewModel(), - onNavigateToLogin: () -> Unit = {} + onNavigateToLogin: () -> Unit = {}, + onNavigateToHome: () -> Unit = {} ) { - val navigateToLogin by viewModel.navigateToLogin.collectAsStateWithLifecycle() + val destination by viewModel.destination.collectAsStateWithLifecycle() - LaunchedEffect(navigateToLogin) { - if (navigateToLogin) { - onNavigateToLogin() + LaunchedEffect(destination) { + when (destination) { + SplashDestination.NavigateToLogin -> onNavigateToLogin() + SplashDestination.NavigateToHome -> onNavigateToHome() + else -> {} } } diff --git a/app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt b/app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt index 72b07245..d0abc258 100644 --- a/app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt +++ b/app/src/main/java/com/texthip/thip/ui/signin/viewmodel/SplashViewModel.kt @@ -2,6 +2,7 @@ package com.texthip.thip.ui.signin.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.texthip.thip.data.manager.TokenManager import dagger.hilt.android.lifecycle.HiltViewModel import jakarta.inject.Inject import kotlinx.coroutines.delay @@ -9,15 +10,35 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +sealed interface SplashDestination { + data object Loading : SplashDestination + data object NavigateToLogin : SplashDestination + data object NavigateToHome : SplashDestination +} + @HiltViewModel -class SplashViewModel @Inject constructor() : ViewModel() { - private val _navigateToLogin = MutableStateFlow(false) - val navigateToLogin = _navigateToLogin.asStateFlow() +class SplashViewModel @Inject constructor( + private val tokenManager: TokenManager +) : ViewModel() { + + private val _destination = MutableStateFlow(SplashDestination.Loading) + val destination = _destination.asStateFlow() init { + checkLoginStatus() + } + + private fun checkLoginStatus() { viewModelScope.launch { - delay(3000) - _navigateToLogin.value = true + delay(2000L) // 스플래시 최소 노출 시간 + + val token = tokenManager.getTokenOnce() + + if (token.isNullOrBlank()) { + _destination.value = SplashDestination.NavigateToLogin + } else { + _destination.value = SplashDestination.NavigateToHome + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt b/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt index d9d8c0e1..71e9f700 100644 --- a/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt +++ b/app/src/main/java/com/texthip/thip/utils/auth/AuthInterceptor.kt @@ -1,7 +1,6 @@ package com.texthip.thip.utils.auth import com.texthip.thip.data.manager.TokenManager -import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Response @@ -11,26 +10,24 @@ class AuthInterceptor @Inject constructor( private val tokenManager: TokenManager ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { - val originalRequest = chain.request() + val original = chain.request() - // 1. 요청에 이미 Authorization 헤더가 있는지 확인합니다. - if (originalRequest.header("Authorization") != null) { - // 이미 헤더가 있다면, 아무 작업도 하지 않고 그대로 보냅니다. - return chain.proceed(originalRequest) + if (original.header("Authorization") != null) { + return chain.proceed(original) } - val appToken = runBlocking { tokenManager.getToken().first() } - val tempToken = runBlocking { tokenManager.getTempToken() } - val tokenToSend = appToken ?: tempToken - val requestBuilder = chain.request().newBuilder() + // 1. 정식 토큰을 먼저 확인합니다. + val token = runBlocking { tokenManager.getTokenOnce() } + // 2. 정식 토큰이 없으면, 임시 토큰을 확인합니다. + val tempToken = runBlocking { tokenManager.getTempTokenOnce() } - if (!tokenToSend.isNullOrBlank()) { - requestBuilder.addHeader( - "Authorization", - "Bearer $tokenToSend" - ) - } + // 보낼 토큰을 결정합니다 (정식 토큰 우선). + val tokenToSend = token ?: tempToken + + val newRequest = original.newBuilder().apply { + tokenToSend?.let { addHeader("Authorization", "Bearer $it") } + }.build() - return chain.proceed(requestBuilder.build()) + return chain.proceed(newRequest) } -} \ No newline at end of file +}