From f8dbc6eddc1bdcc045a30a698aa20636c6e1f45c Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 14:03:42 +0900 Subject: [PATCH 01/36] =?UTF-8?q?refactor:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/datastore/build.gradle.kts | 4 ++ .../account/AccountDataSourceModule.kt | 16 +++++ .../account/DefaultAccountDataSource.kt | 60 +++++++++++++++++++ .../websoso/core/datastore/datasource/gitkeep | 0 .../core/datastore/di/DataStoreModule.kt | 1 + .../core/datastore/di/DataStoreQualifier.kt | 7 +++ .../account/AccountDataSourceModule.kt | 2 +- .../websoso/data/account/AccountEntity.kt | 2 +- .../datasource/AccountLocalDataSource.kt | 15 +++++ .../AccountRemoteDataSource.kt | 3 +- 10 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt create mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt delete mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/gitkeep create mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt create mode 100644 data/account/src/main/java/com/into/websoso/data/account/datasource/AccountLocalDataSource.kt rename data/account/src/main/java/com/into/websoso/data/account/{ => datasource}/AccountRemoteDataSource.kt (70%) diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index 72febb4a1..422e502e9 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -9,5 +9,9 @@ android { } dependencies { + // 데이터 레이어 의존성 + implementation(projects.data.account) + + // Datastore 라이브러리 implementation(libs.datastore.preferences) } diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt new file mode 100644 index 000000000..98ac373d2 --- /dev/null +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt @@ -0,0 +1,16 @@ +package com.into.websoso.core.datastore.datasource.account + +import com.into.websoso.data.account.datasource.AccountLocalDataSource +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface AccountDataSourceModule { + @Binds + @Singleton + fun bindAccountLocalDataSource(defaultAccountDataSource: DefaultAccountDataSource): AccountLocalDataSource +} diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt new file mode 100644 index 000000000..21614a211 --- /dev/null +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt @@ -0,0 +1,60 @@ +package com.into.websoso.core.datastore.datasource.account + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import com.into.websoso.core.datastore.di.AccountDataStore +import com.into.websoso.data.account.datasource.AccountLocalDataSource +import kotlinx.coroutines.flow.last +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +class DefaultAccountDataSource + @Inject + constructor( + @AccountDataStore private val accountDataStore: DataStore, + ) : AccountLocalDataSource { + override suspend fun accessToken(): String = + accountDataStore.data + .map { preferences -> + preferences[ACCESS_TOKEN].orEmpty() + }.last() + + override suspend fun refreshToken(): String = + accountDataStore.data + .map { preferences -> + preferences[REFRESH_TOKEN].orEmpty() + }.last() + + override suspend fun isAutoLogin(): Boolean = + accountDataStore.data + .map { preferences -> + preferences[IS_LOGIN] ?: false + }.last() + + override suspend fun saveAccessToken(accessToken: String) { + accountDataStore.edit { preferences -> + preferences[ACCESS_TOKEN] = accessToken + } + } + + override suspend fun saveRefreshToken(refreshToken: String) { + accountDataStore.edit { preferences -> + preferences[REFRESH_TOKEN] = refreshToken + } + } + + override suspend fun saveIsAutoLogin(isAutoLogin: Boolean) { + accountDataStore.edit { preferences -> + preferences[IS_LOGIN] = isAutoLogin + } + } + + companion object { + private val ACCESS_TOKEN = stringPreferencesKey("ACCESS_TOKEN") + private val REFRESH_TOKEN = stringPreferencesKey("REFRESH_TOKEN") + private val IS_LOGIN = booleanPreferencesKey("IS_LOGIN") + } + } diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/gitkeep b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt index ad142bf26..7620dc357 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt @@ -19,6 +19,7 @@ internal object DataStoreModule { @Singleton @Provides + @AccountDataStore fun provideAccountPreferencesDataStore( @ApplicationContext context: Context, ): DataStore = context.accountDataStore diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt new file mode 100644 index 000000000..d4b5dc92f --- /dev/null +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt @@ -0,0 +1,7 @@ +package com.into.websoso.core.datastore.di + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class AccountDataStore diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt index e3de40538..844b97080 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt @@ -1,6 +1,6 @@ package com.into.websoso.core.network.datasource.account -import com.into.websoso.data.account.AccountRemoteDataSource +import com.into.websoso.data.account.datasource.AccountRemoteDataSource import dagger.Binds import dagger.Module import dagger.hilt.InstallIn diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt index 7510ce030..43c4782f5 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt @@ -1,7 +1,7 @@ package com.into.websoso.data.account data class AccountEntity( - val authorization: String, + val accessToken: String, val refreshToken: String, val isRegister: Boolean, ) diff --git a/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountLocalDataSource.kt b/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountLocalDataSource.kt new file mode 100644 index 000000000..584daa6b9 --- /dev/null +++ b/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountLocalDataSource.kt @@ -0,0 +1,15 @@ +package com.into.websoso.data.account.datasource + +interface AccountLocalDataSource { + suspend fun accessToken(): String + + suspend fun refreshToken(): String + + suspend fun isAutoLogin(): Boolean + + suspend fun saveAccessToken(accessToken: String) + + suspend fun saveRefreshToken(refreshToken: String) + + suspend fun saveIsAutoLogin(isAutoLogin: Boolean) +} diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountRemoteDataSource.kt b/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt similarity index 70% rename from data/account/src/main/java/com/into/websoso/data/account/AccountRemoteDataSource.kt rename to data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt index 14790f206..e6aed5221 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountRemoteDataSource.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt @@ -1,7 +1,8 @@ -package com.into.websoso.data.account +package com.into.websoso.data.account.datasource import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken +import com.into.websoso.data.account.AccountEntity interface AccountRemoteDataSource { suspend fun postLogin( From e59830dd391dc1a12288caced10262310975d079 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 14:06:21 +0900 Subject: [PATCH 02/36] =?UTF-8?q?refactor:=20=EB=B0=98=ED=99=98=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/account/DefaultAccountDataSource.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt index afbf4b2f0..f7682d861 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt @@ -2,7 +2,8 @@ package com.into.websoso.core.network.datasource.account import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken -import com.into.websoso.data.account.AccountRemoteDataSource +import com.into.websoso.data.account.AccountEntity +import com.into.websoso.data.account.datasource.AccountRemoteDataSource import retrofit2.Retrofit import retrofit2.create import javax.inject.Inject @@ -19,7 +20,8 @@ class DefaultAccountDataSource override suspend fun postLogin( platform: AuthPlatform, authToken: AuthToken, - ) = when (platform) { - AuthPlatform.KAKAO -> accountApi.postLoginWithKakao(authToken.accessToken).toData() - } + ): AccountEntity = + when (platform) { + AuthPlatform.KAKAO -> accountApi.postLoginWithKakao(authToken.accessToken).toData() + } } From d8e2460fdab12d3f7b22304dac602c02c1bd498b Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 14:06:29 +0900 Subject: [PATCH 03/36] =?UTF-8?q?refactor:=20=EC=8B=9C=EA=B7=B8=EB=8B=88?= =?UTF-8?q?=EC=B2=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/network/datasource/account/KakaoLoginResponseDto.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt index 6b765f2c9..eb115fbcf 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt @@ -15,7 +15,7 @@ internal class KakaoLoginResponseDto( ) { fun toData(): AccountEntity = AccountEntity( - authorization = authorization, + accessToken = authorization, refreshToken = refreshToken, isRegister = isRegister, ) From f3e24ef1d3a517b169eae11142dd192b8377d373 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 14:06:55 +0900 Subject: [PATCH 04/36] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20api=20?= =?UTF-8?q?=ED=86=B5=EC=8B=A0=20=EB=B0=8F=20=EC=A0=80=EC=9E=A5=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websoso/data/account/AccountRepository.kt | 13 +++++++++++-- .../websoso/feature/signin/SignInViewModel.kt | 18 ++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt index 2c5a6d2a9..bbada0065 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt @@ -2,20 +2,29 @@ package com.into.websoso.data.account import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken +import com.into.websoso.data.account.datasource.AccountLocalDataSource +import com.into.websoso.data.account.datasource.AccountRemoteDataSource import javax.inject.Inject class AccountRepository @Inject constructor( private val accountRemoteDataSource: AccountRemoteDataSource, + private val accountLocalDataSource: AccountLocalDataSource, ) { suspend fun saveToken( platform: AuthPlatform, authToken: AuthToken, - ) { - accountRemoteDataSource.postLogin( + ): Boolean { + val account = accountRemoteDataSource.postLogin( platform = platform, authToken = authToken, ) + + accountLocalDataSource.saveAccessToken(account.accessToken) + accountLocalDataSource.saveRefreshToken(account.refreshToken) + if (accountLocalDataSource.isAutoLogin().not()) accountLocalDataSource.saveIsAutoLogin(true) + + return account.isRegister } } diff --git a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt index dc11914ba..ecc1fa635 100644 --- a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt +++ b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt @@ -45,10 +45,16 @@ class SignInViewModel authToken: AuthToken, ) { viewModelScope.launch { - accountRepository.saveToken( - platform = platform, - authToken = authToken, - ) + runCatching { + // result 객체 + accountRepository.saveToken( + platform = platform, + authToken = authToken, + ) + }.onSuccess { isRegister -> + // navigateToHome.emit() + }.onFailure { + } } } @@ -77,3 +83,7 @@ sealed interface UiEffect { data object ShowToast : UiEffect } + +sealed interface UiEvent { + data object NavigateToHome : UiEvent +} From 95ea494626c4a648ef61178a03c74e78625da2f3 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 14:23:24 +0900 Subject: [PATCH 05/36] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EB=B7=B0=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + .../into/websoso/feature/signin/SignInScreen.kt | 14 ++++++++++++-- .../into/websoso/feature/signin/SignInViewModel.kt | 13 ++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b84334df1..72afed46a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -87,6 +87,7 @@ dependencies { implementation(projects.core.auth) implementation(projects.core.authKakao) implementation(projects.core.network) + implementation(projects.core.datastore) implementation(projects.feature.signin) diff --git a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt index b82955222..f5bf11094 100644 --- a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt +++ b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt @@ -20,6 +20,8 @@ import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.common.extensions.collectAsEventWithLifecycle import com.into.websoso.core.designsystem.theme.Gray50 import com.into.websoso.core.designsystem.theme.WebsosoTheme +import com.into.websoso.feature.signin.UiEffect.NavigateToHome +import com.into.websoso.feature.signin.UiEffect.NavigateToOnboarding import com.into.websoso.feature.signin.UiEffect.ScrollToPage import com.into.websoso.feature.signin.UiEffect.ShowToast import com.into.websoso.feature.signin.component.OnboardingDotsIndicator @@ -37,15 +39,23 @@ fun SignInScreen( latestEvent.collectAsEventWithLifecycle { event -> when (event) { - is ScrollToPage -> { + ScrollToPage -> { pagerState.animateScrollToPage( page = (pagerState.currentPage + 1) % pagerState.pageCount, ) } - is ShowToast -> { + ShowToast -> { // TODO: 실패 시 커스텀 스낵 바 구현 } + + NavigateToHome -> { + // TODO: 홈 이동 + } + + NavigateToOnboarding -> { + // TODO: 온보딩 이동 + } } } diff --git a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt index ecc1fa635..03b29bdef 100644 --- a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt +++ b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInViewModel.kt @@ -46,14 +46,17 @@ class SignInViewModel ) { viewModelScope.launch { runCatching { - // result 객체 accountRepository.saveToken( platform = platform, authToken = authToken, ) }.onSuccess { isRegister -> - // navigateToHome.emit() + when (isRegister) { + true -> _uiEvent.send(UiEffect.NavigateToHome) + false -> _uiEvent.send(UiEffect.NavigateToOnboarding) + } }.onFailure { + signInWithFailure() } } } @@ -82,8 +85,8 @@ sealed interface UiEffect { data object ScrollToPage : UiEffect data object ShowToast : UiEffect -} -sealed interface UiEvent { - data object NavigateToHome : UiEvent + data object NavigateToHome : UiEffect + + data object NavigateToOnboarding : UiEffect } From 4515e378fccbd51552bc53d24bb25a3e20bc183b Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 15:23:54 +0900 Subject: [PATCH 06/36] =?UTF-8?q?feat:=20AccountTokenProvider=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ccountDataSourceModule.kt => AccountModule.kt} | 7 ++++++- .../account/DefaultAccountTokenProvider.kt | 15 +++++++++++++++ .../websoso/data/account/AccountTokenProvider.kt | 5 +++++ 3 files changed, 26 insertions(+), 1 deletion(-) rename core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/{AccountDataSourceModule.kt => AccountModule.kt} (67%) create mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt create mode 100644 data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt similarity index 67% rename from core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt rename to core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt index 98ac373d2..2977b93bd 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountDataSourceModule.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt @@ -1,5 +1,6 @@ package com.into.websoso.core.datastore.datasource.account +import com.into.websoso.data.account.AccountTokenProvider import com.into.websoso.data.account.datasource.AccountLocalDataSource import dagger.Binds import dagger.Module @@ -9,8 +10,12 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface AccountDataSourceModule { +internal interface AccountModule { @Binds @Singleton fun bindAccountLocalDataSource(defaultAccountDataSource: DefaultAccountDataSource): AccountLocalDataSource + + @Binds + @Singleton + fun bindAccountTokenProvider(accountLocalDataSource: AccountLocalDataSource): AccountTokenProvider } diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt new file mode 100644 index 000000000..3378ca999 --- /dev/null +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt @@ -0,0 +1,15 @@ +package com.into.websoso.core.datastore.datasource.account + +import com.into.websoso.data.account.AccountTokenProvider +import com.into.websoso.data.account.datasource.AccountLocalDataSource +import javax.inject.Inject + +class DefaultAccountTokenProvider + @Inject + constructor( + private val accountDataStore: AccountLocalDataSource, + ) : AccountTokenProvider { + override suspend fun invoke() { + accountDataStore.accessToken() + } + } diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt new file mode 100644 index 000000000..5d418cdbf --- /dev/null +++ b/data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt @@ -0,0 +1,5 @@ +package com.into.websoso.data.account + +fun interface AccountTokenProvider { + suspend operator fun invoke() +} From 57270d1ca15fe44ca8d0c11e30ecb01e776924cc Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 17:21:58 +0900 Subject: [PATCH 07/36] =?UTF-8?q?refactor:=20network=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EA=B0=80=EC=8B=9C=EC=84=B1=EC=A0=9C=EC=96=B4=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/datasource/account/AccountApi.kt | 2 +- .../account/KakaoLoginResponseDto.kt | 2 +- .../websoso/core/network/di/NetworkModule.kt | 20 +++++++++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt index 7d40cfbaf..bb91f11dd 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt @@ -21,5 +21,5 @@ internal interface AccountApi { internal object AccountApiModule { @Provides @Singleton - fun provideAccountApi(retrofit: Retrofit): AccountApi = retrofit.create(AccountApi::class.java) + internal fun provideAccountApi(retrofit: Retrofit): AccountApi = retrofit.create(AccountApi::class.java) } diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt index eb115fbcf..862a6f44e 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt @@ -13,7 +13,7 @@ internal class KakaoLoginResponseDto( @SerialName("isRegister") val isRegister: Boolean, ) { - fun toData(): AccountEntity = + internal fun toData(): AccountEntity = AccountEntity( accessToken = authorization, refreshToken = refreshToken, diff --git a/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt index e499653db..b8f254e8a 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt @@ -1,6 +1,7 @@ package com.into.websoso.core.network.di import com.into.websoso.core.network.BuildConfig +import com.into.websoso.core.network.interceptor.AuthorizationInterceptor import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module import dagger.Provides @@ -15,13 +16,18 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object NetworkModule { +internal object NetworkModule { private const val BASE_URL = BuildConfig.BASE_URL private const val CONTENT_TYPE = "application/json" + private val httpLoggingInterceptor: HttpLoggingInterceptor by lazy { + HttpLoggingInterceptor().apply { + if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY) + } + } @Provides @Singleton - fun provideRetrofit( + internal fun provideRetrofit( json: Json, client: OkHttpClient, ): Retrofit = @@ -38,12 +44,10 @@ object NetworkModule { @Provides @Singleton - internal fun provideOkHttpClient(): OkHttpClient = + internal fun provideOkHttpClient(authorizationInterceptor: AuthorizationInterceptor): OkHttpClient = OkHttpClient .Builder() - .addInterceptor( - HttpLoggingInterceptor().apply { - if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY) - }, - ).build() + .addInterceptor(httpLoggingInterceptor) + .addInterceptor(authorizationInterceptor) + .build() } From 3a20765b01bcdcaa7a38fa8c0ff1e0cb8c67a0bb Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 17:24:05 +0900 Subject: [PATCH 08/36] =?UTF-8?q?refactor:=20app=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 8 -- .../com/into/websoso/data/di/ApiModule.kt | 42 ++----- .../com/into/websoso/data/di/NetworkModule.kt | 115 ------------------ .../com/into/websoso/data/di/OAuthModule.kt | 25 ---- .../data/interceptor/AuthInterceptor.kt | 22 ---- .../websoso/data/mapper/KakaoTokenMapper.kt | 11 -- .../into/websoso/data/mapper/LoginMapper.kt | 11 -- .../into/websoso/data/model/LoginEntity.kt | 7 -- .../com/into/websoso/data/model/OAuthToken.kt | 10 -- .../com/into/websoso/data/qualifier/Auth.kt | 11 -- .../websoso/data/qualifier/Interceptor.kt | 11 -- .../into/websoso/data/remote/api/AuthApi.kt | 6 - .../data/remote/api/KakaoAuthService.kt | 90 -------------- .../websoso/data/remote/api/OAuthService.kt | 11 -- .../remote/response/KakaoLoginResponseDto.kt | 14 --- .../websoso/data/repository/AuthRepository.kt | 9 -- .../into/websoso/ui/login/LoginViewModel.kt | 66 ---------- .../websoso/ui/login/model/LoginUiState.kt | 15 --- ...hite_radius_14dp_stroke_primary100_1dp.xml | 8 -- app/src/main/res/layout/activity_login.xml | 64 ---------- 20 files changed, 10 insertions(+), 546 deletions(-) delete mode 100644 app/src/main/java/com/into/websoso/data/di/NetworkModule.kt delete mode 100644 app/src/main/java/com/into/websoso/data/di/OAuthModule.kt delete mode 100644 app/src/main/java/com/into/websoso/data/interceptor/AuthInterceptor.kt delete mode 100644 app/src/main/java/com/into/websoso/data/mapper/KakaoTokenMapper.kt delete mode 100644 app/src/main/java/com/into/websoso/data/mapper/LoginMapper.kt delete mode 100644 app/src/main/java/com/into/websoso/data/model/LoginEntity.kt delete mode 100644 app/src/main/java/com/into/websoso/data/model/OAuthToken.kt delete mode 100644 app/src/main/java/com/into/websoso/data/qualifier/Auth.kt delete mode 100644 app/src/main/java/com/into/websoso/data/qualifier/Interceptor.kt delete mode 100644 app/src/main/java/com/into/websoso/data/remote/api/KakaoAuthService.kt delete mode 100644 app/src/main/java/com/into/websoso/data/remote/api/OAuthService.kt delete mode 100644 app/src/main/java/com/into/websoso/data/remote/response/KakaoLoginResponseDto.kt delete mode 100644 app/src/main/java/com/into/websoso/ui/login/LoginViewModel.kt delete mode 100644 app/src/main/java/com/into/websoso/ui/login/model/LoginUiState.kt delete mode 100644 app/src/main/res/drawable/bg_login_white_radius_14dp_stroke_primary100_1dp.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 72afed46a..da1a7ece4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -41,10 +41,6 @@ android { isDebuggable = true applicationIdSuffix = ".debug" - buildConfigs(rootDir) { - string(name = "BASE_URL", key = "debug.base.url") - } - manifestPlaceholders { "appName" to "@string/app_name_debug" "appIcon" to "@mipmap/ic_wss_logo_debug" @@ -61,10 +57,6 @@ android { "proguard-rules.pro", ) - buildConfigs(rootDir) { - string(name = "BASE_URL", key = "release.base.url") - } - manifestPlaceholders { "appName" to "@string/app_name" "appIcon" to "@mipmap/ic_wss_logo" diff --git a/app/src/main/java/com/into/websoso/data/di/ApiModule.kt b/app/src/main/java/com/into/websoso/data/di/ApiModule.kt index c6f808287..8f303f6d5 100644 --- a/app/src/main/java/com/into/websoso/data/di/ApiModule.kt +++ b/app/src/main/java/com/into/websoso/data/di/ApiModule.kt @@ -1,7 +1,5 @@ package com.into.websoso.data.di -import com.into.websoso.data.qualifier.Secured -import com.into.websoso.data.qualifier.Unsecured import com.into.websoso.data.remote.api.AuthApi import com.into.websoso.data.remote.api.AvatarApi import com.into.websoso.data.remote.api.FeedApi @@ -24,61 +22,41 @@ import javax.inject.Singleton object ApiModule { @Provides @Singleton - fun provideAuthApi( - @Unsecured retrofit: Retrofit, - ): AuthApi = retrofit.create(AuthApi::class.java) + fun provideAuthApi(retrofit: Retrofit): AuthApi = retrofit.create(AuthApi::class.java) @Provides @Singleton - fun provideNovelApi( - @Secured retrofit: Retrofit, - ): NovelApi = retrofit.create(NovelApi::class.java) + fun provideNovelApi(retrofit: Retrofit): NovelApi = retrofit.create(NovelApi::class.java) @Provides @Singleton - fun provideUserNovelApi( - @Secured retrofit: Retrofit, - ): UserNovelApi = retrofit.create(UserNovelApi::class.java) + fun provideUserNovelApi(retrofit: Retrofit): UserNovelApi = retrofit.create(UserNovelApi::class.java) @Provides @Singleton - fun provideNotificationApi( - @Secured retrofit: Retrofit, - ): NotificationApi = retrofit.create(NotificationApi::class.java) + fun provideNotificationApi(retrofit: Retrofit): NotificationApi = retrofit.create(NotificationApi::class.java) @Provides @Singleton - fun provideFeedApi( - @Secured retrofit: Retrofit, - ): FeedApi = retrofit.create(FeedApi::class.java) + fun provideFeedApi(retrofit: Retrofit): FeedApi = retrofit.create(FeedApi::class.java) @Provides @Singleton - fun provideUserApi( - @Secured retrofit: Retrofit, - ): UserApi = retrofit.create(UserApi::class.java) + fun provideUserApi(retrofit: Retrofit): UserApi = retrofit.create(UserApi::class.java) @Provides @Singleton - fun provideKeywordApi( - @Secured retrofit: Retrofit, - ): KeywordApi = retrofit.create(KeywordApi::class.java) + fun provideKeywordApi(retrofit: Retrofit): KeywordApi = retrofit.create(KeywordApi::class.java) @Provides @Singleton - fun provideAvatarApi( - @Secured retrofit: Retrofit, - ): AvatarApi = retrofit.create(AvatarApi::class.java) + fun provideAvatarApi(retrofit: Retrofit): AvatarApi = retrofit.create(AvatarApi::class.java) @Provides @Singleton - fun provideVersionApi( - @Secured retrofit: Retrofit, - ): VersionApi = retrofit.create(VersionApi::class.java) + fun provideVersionApi(retrofit: Retrofit): VersionApi = retrofit.create(VersionApi::class.java) @Provides @Singleton - fun providePushMessageApi( - @Secured retrofit: Retrofit, - ): PushMessageApi = retrofit.create(PushMessageApi::class.java) + fun providePushMessageApi(retrofit: Retrofit): PushMessageApi = retrofit.create(PushMessageApi::class.java) } diff --git a/app/src/main/java/com/into/websoso/data/di/NetworkModule.kt b/app/src/main/java/com/into/websoso/data/di/NetworkModule.kt deleted file mode 100644 index 1cabf301f..000000000 --- a/app/src/main/java/com/into/websoso/data/di/NetworkModule.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.into.websoso.data.di - -import com.into.websoso.BuildConfig -import com.into.websoso.data.authenticator.WebsosoAuthenticator -import com.into.websoso.data.interceptor.AuthInterceptor -import com.into.websoso.data.qualifier.Auth -import com.into.websoso.data.qualifier.Logging -import com.into.websoso.data.qualifier.Secured -import com.into.websoso.data.qualifier.Unsecured -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.serialization.json.Json -import okhttp3.Interceptor -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Converter -import retrofit2.Retrofit -import java.util.concurrent.TimeUnit -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object NetworkModule { - private const val BASE_URL = BuildConfig.BASE_URL - private const val CONTENT_TYPE = "application/json" - - @Provides - @Singleton - fun provideJson(): Json = Json { ignoreUnknownKeys = true } - - @Provides - @Singleton - fun provideJsonConverterFactory(json: Json): Converter.Factory = json.asConverterFactory(CONTENT_TYPE.toMediaType()) - - @Provides - @Singleton - @Logging - fun provideLoggingInterceptor(): Interceptor = - HttpLoggingInterceptor().apply { - level = if (BuildConfig.DEBUG) { - HttpLoggingInterceptor.Level.BODY - } else { - HttpLoggingInterceptor.Level.NONE - } - } - - @Provides - @Singleton - @Auth - fun provideAuthInterceptor(interceptor: AuthInterceptor): Interceptor = interceptor - - @Provides - @Singleton - @Secured - fun provideSecuredOkHttpClient( - @Logging loggingInterceptor: Interceptor, - @Auth authInterceptor: Interceptor, - websosoAuthenticator: WebsosoAuthenticator, - ): OkHttpClient = - OkHttpClient - .Builder() - .addInterceptor(loggingInterceptor) - .addInterceptor(authInterceptor) - .authenticator(websosoAuthenticator) - .connectTimeout(60, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(15, TimeUnit.SECONDS) - .build() - - @Provides - @Singleton - @Unsecured - fun provideUnsecuredOkHttpClient( - @Logging loggingInterceptor: Interceptor, - ): OkHttpClient = - OkHttpClient - .Builder() - .addInterceptor(loggingInterceptor) - .connectTimeout(60, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(15, TimeUnit.SECONDS) - .build() - - @Provides - @Singleton - @Secured - fun provideSecuredRetrofit( - @Secured client: OkHttpClient, - converterFactory: Converter.Factory, - ): Retrofit = - Retrofit - .Builder() - .baseUrl(BASE_URL) - .client(client) - .addConverterFactory(converterFactory) - .build() - - @Provides - @Singleton - @Unsecured - fun provideUnsecuredRetrofit( - @Unsecured client: OkHttpClient, - converterFactory: Converter.Factory, - ): Retrofit = - Retrofit - .Builder() - .baseUrl(BASE_URL) - .client(client) - .addConverterFactory(converterFactory) - .build() -} diff --git a/app/src/main/java/com/into/websoso/data/di/OAuthModule.kt b/app/src/main/java/com/into/websoso/data/di/OAuthModule.kt deleted file mode 100644 index 45a0a0e04..000000000 --- a/app/src/main/java/com/into/websoso/data/di/OAuthModule.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.into.websoso.data.di - -import com.into.websoso.data.remote.api.KakaoAuthService -import com.into.websoso.data.remote.api.OAuthService -import com.kakao.sdk.user.UserApiClient -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -object OAuthModule { - @Provides - fun provideKakaoApiClient(): UserApiClient = UserApiClient.instance - - @Module - @InstallIn(ActivityComponent::class) - interface Binder { - @Binds - fun bindKakaoAuthService(service: KakaoAuthService): OAuthService - } -} diff --git a/app/src/main/java/com/into/websoso/data/interceptor/AuthInterceptor.kt b/app/src/main/java/com/into/websoso/data/interceptor/AuthInterceptor.kt deleted file mode 100644 index 243e45c53..000000000 --- a/app/src/main/java/com/into/websoso/data/interceptor/AuthInterceptor.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.into.websoso.data.interceptor - -import com.into.websoso.data.repository.AuthRepository -import okhttp3.Interceptor -import okhttp3.Response -import javax.inject.Inject - -class AuthInterceptor - @Inject - constructor( - private val authRepository: AuthRepository, - ) : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain - .request() - .newBuilder() - .header("Authorization", "Bearer ${authRepository.accessToken}") - .build() - - return chain.proceed(request) - } - } diff --git a/app/src/main/java/com/into/websoso/data/mapper/KakaoTokenMapper.kt b/app/src/main/java/com/into/websoso/data/mapper/KakaoTokenMapper.kt deleted file mode 100644 index 6fcfde8a8..000000000 --- a/app/src/main/java/com/into/websoso/data/mapper/KakaoTokenMapper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.into.websoso.data.mapper - -import com.into.websoso.data.model.KakaoOAuthToken - -typealias KakaoToken = com.kakao.sdk.auth.model.OAuthToken - -fun KakaoToken.toOAuthToken() = - KakaoOAuthToken( - accessToken = accessToken, - refreshToken = refreshToken, - ) diff --git a/app/src/main/java/com/into/websoso/data/mapper/LoginMapper.kt b/app/src/main/java/com/into/websoso/data/mapper/LoginMapper.kt deleted file mode 100644 index f6cb55e84..000000000 --- a/app/src/main/java/com/into/websoso/data/mapper/LoginMapper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.into.websoso.data.mapper - -import com.into.websoso.data.model.LoginEntity -import com.into.websoso.data.remote.response.KakaoLoginResponseDto - -fun KakaoLoginResponseDto.toData(): LoginEntity = - LoginEntity( - authorization = this.authorization, - refreshToken = this.refreshToken, - isRegister = this.isRegister, - ) diff --git a/app/src/main/java/com/into/websoso/data/model/LoginEntity.kt b/app/src/main/java/com/into/websoso/data/model/LoginEntity.kt deleted file mode 100644 index fe1801468..000000000 --- a/app/src/main/java/com/into/websoso/data/model/LoginEntity.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.into.websoso.data.model - -data class LoginEntity( - val authorization: String, - val refreshToken: String, - val isRegister: Boolean, -) diff --git a/app/src/main/java/com/into/websoso/data/model/OAuthToken.kt b/app/src/main/java/com/into/websoso/data/model/OAuthToken.kt deleted file mode 100644 index af3201733..000000000 --- a/app/src/main/java/com/into/websoso/data/model/OAuthToken.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.into.websoso.data.model - -sealed interface OAuthToken { - val accessToken: String -} - -data class KakaoOAuthToken( - override val accessToken: String, - val refreshToken: String, -) : OAuthToken diff --git a/app/src/main/java/com/into/websoso/data/qualifier/Auth.kt b/app/src/main/java/com/into/websoso/data/qualifier/Auth.kt deleted file mode 100644 index 17c08e7d9..000000000 --- a/app/src/main/java/com/into/websoso/data/qualifier/Auth.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.into.websoso.data.qualifier - -import javax.inject.Qualifier - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class Secured - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class Unsecured diff --git a/app/src/main/java/com/into/websoso/data/qualifier/Interceptor.kt b/app/src/main/java/com/into/websoso/data/qualifier/Interceptor.kt deleted file mode 100644 index 020d72e6f..000000000 --- a/app/src/main/java/com/into/websoso/data/qualifier/Interceptor.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.into.websoso.data.qualifier - -import javax.inject.Qualifier - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class Logging - -@Qualifier -@Retention(AnnotationRetention.BINARY) -annotation class Auth diff --git a/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt b/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt index a20f75d7e..00d15e15c 100644 --- a/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt +++ b/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt @@ -5,7 +5,6 @@ import com.into.websoso.data.remote.request.LogoutRequestDto import com.into.websoso.data.remote.request.TokenReissueRequestDto import com.into.websoso.data.remote.request.UserProfileRequestDto import com.into.websoso.data.remote.request.WithdrawRequestDto -import com.into.websoso.data.remote.response.KakaoLoginResponseDto import com.into.websoso.data.remote.response.KakaoTokenReissueResponseDto import com.into.websoso.data.remote.response.UserNicknameValidityResponseDto import retrofit2.http.Body @@ -15,11 +14,6 @@ import retrofit2.http.POST import retrofit2.http.Query interface AuthApi { - @POST("auth/login/kakao") - suspend fun loginWithKakao( - @Header("Kakao-Access-Token") accessToken: String, - ): KakaoLoginResponseDto - @GET("users/nickname/check") suspend fun getNicknameValidity( @Header("Authorization") authorization: String, diff --git a/app/src/main/java/com/into/websoso/data/remote/api/KakaoAuthService.kt b/app/src/main/java/com/into/websoso/data/remote/api/KakaoAuthService.kt deleted file mode 100644 index 3c92bc8e5..000000000 --- a/app/src/main/java/com/into/websoso/data/remote/api/KakaoAuthService.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.into.websoso.data.remote.api - -import android.content.Context -import com.google.firebase.Firebase -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.analytics -import com.google.firebase.analytics.logEvent -import com.into.websoso.data.mapper.toOAuthToken -import com.into.websoso.data.model.OAuthToken -import com.kakao.sdk.common.model.ClientError -import com.kakao.sdk.common.model.ClientErrorCause -import com.kakao.sdk.user.UserApiClient -import dagger.hilt.android.qualifiers.ActivityContext -import kotlinx.coroutines.suspendCancellableCoroutine -import javax.inject.Inject -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException - -class KakaoAuthService - @Inject - constructor( - @ActivityContext private val context: Context, - private val client: UserApiClient, - ) : OAuthService { - private val firebaseAnalytics: FirebaseAnalytics = Firebase.analytics - private val isKakaoTalkLoginAvailable: Boolean - get() = client.isKakaoTalkLoginAvailable(context) - - override suspend fun login(): OAuthToken = - suspendCancellableCoroutine { - if (isKakaoTalkLoginAvailable) { - client.loginWithKakaoTalk(context) { token, error -> - if (error != null) { - // 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우, - // 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기) - if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { - return@loginWithKakaoTalk - } - - // Firebase Analytics에 실패 이벤트 로그 기록 - firebaseAnalytics.logEvent("kakao_login_failure") { - param("error_message", error.message ?: "Unknown Error") - param("login_method", "kakao_talk") - } - - // 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인 시도 - client.loginWithKakaoAccount(context) { accountToken, accountError -> - if (accountError != null) { - it.resumeWithException(accountError) - } else if (accountToken != null) { - it.resume(accountToken.toOAuthToken()) - } - } - } else if (token != null) { - it.resume(token.toOAuthToken()) - } - } - } else { - client.loginWithKakaoAccount(context) { token, error -> - if (error != null) { - it.resumeWithException(error) - } else if (token != null) { - it.resume(token.toOAuthToken()) - } - } - } - } - - override suspend fun logout() = - suspendCancellableCoroutine { - client.logout { error -> - if (error != null) { - it.resumeWithException(error) - } else { - it.resume(Unit) - } - } - } - - override suspend fun withdraw() = - suspendCancellableCoroutine { - client.unlink { error -> - if (error != null) { - it.resumeWithException(error) - } else { - it.resume(Unit) - } - } - } - } diff --git a/app/src/main/java/com/into/websoso/data/remote/api/OAuthService.kt b/app/src/main/java/com/into/websoso/data/remote/api/OAuthService.kt deleted file mode 100644 index 70ab8ecdd..000000000 --- a/app/src/main/java/com/into/websoso/data/remote/api/OAuthService.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.into.websoso.data.remote.api - -import com.into.websoso.data.model.OAuthToken - -interface OAuthService { - suspend fun login(): OAuthToken - - suspend fun logout() - - suspend fun withdraw() -} diff --git a/app/src/main/java/com/into/websoso/data/remote/response/KakaoLoginResponseDto.kt b/app/src/main/java/com/into/websoso/data/remote/response/KakaoLoginResponseDto.kt deleted file mode 100644 index 9dcbbb58e..000000000 --- a/app/src/main/java/com/into/websoso/data/remote/response/KakaoLoginResponseDto.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.into.websoso.data.remote.response - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -class KakaoLoginResponseDto( - @SerialName("Authorization") - val authorization: String, - @SerialName("refreshToken") - val refreshToken: String, - @SerialName("isRegister") - val isRegister: Boolean, -) diff --git a/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt b/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt index 10c80ca73..36404788d 100644 --- a/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt +++ b/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt @@ -1,8 +1,6 @@ package com.into.websoso.data.repository import android.content.SharedPreferences -import com.into.websoso.data.mapper.toData -import com.into.websoso.data.model.LoginEntity import com.into.websoso.data.remote.api.AuthApi import com.into.websoso.data.remote.request.FCMTokenRequestDto import com.into.websoso.data.remote.request.LogoutRequestDto @@ -29,13 +27,6 @@ class AuthRepository get() = authStorage.getBoolean(AUTO_LOGIN_KEY, false) private set(value) = authStorage.edit().putBoolean(AUTO_LOGIN_KEY, value).apply() - suspend fun loginWithKakao(accessToken: String): LoginEntity { - val response = authApi.loginWithKakao(accessToken) - this.accessToken = response.authorization - this.refreshToken = response.refreshToken - return response.toData() - } - suspend fun fetchNicknameValidity( authorization: String, nickname: String, diff --git a/app/src/main/java/com/into/websoso/ui/login/LoginViewModel.kt b/app/src/main/java/com/into/websoso/ui/login/LoginViewModel.kt deleted file mode 100644 index dabae48b4..000000000 --- a/app/src/main/java/com/into/websoso/ui/login/LoginViewModel.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.into.websoso.ui.login - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.into.websoso.core.resource.R.drawable.img_login_1 -import com.into.websoso.core.resource.R.drawable.img_login_2 -import com.into.websoso.core.resource.R.drawable.img_login_3 -import com.into.websoso.core.resource.R.drawable.img_login_4 -import com.into.websoso.data.repository.AuthRepository -import com.into.websoso.ui.login.model.LoginUiState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class LoginViewModel - @Inject - constructor( - private val authRepository: AuthRepository, - ) : ViewModel() { - private val _loginUiState = MutableLiveData() - val loginUiState: LiveData get() = _loginUiState - - private val _loginImages = MutableLiveData>() - val loginImages: LiveData> = _loginImages - - lateinit var accessToken: String - private set - lateinit var refreshToken: String - private set - - init { - _loginImages.value = listOf( - img_login_1, - img_login_2, - img_login_3, - img_login_4, - ) - } - - fun loginWithKakao(kakaoAccessToken: String) { - viewModelScope.launch { - _loginUiState.value = LoginUiState.Loading - runCatching { - authRepository.loginWithKakao(kakaoAccessToken) - }.onSuccess { loginEntity -> - accessToken = loginEntity.authorization - refreshToken = loginEntity.refreshToken - - if (loginEntity.isRegister) { - authRepository.updateAccessToken(loginEntity.authorization) - authRepository.updateRefreshToken(loginEntity.refreshToken) - authRepository.updateIsAutoLogin(true) - } - - _loginUiState.value = LoginUiState.Success( - isRegistered = loginEntity.isRegister, - ) - }.onFailure { error -> - _loginUiState.value = LoginUiState.Failure(error) - } - } - } - } diff --git a/app/src/main/java/com/into/websoso/ui/login/model/LoginUiState.kt b/app/src/main/java/com/into/websoso/ui/login/model/LoginUiState.kt deleted file mode 100644 index 1128e5880..000000000 --- a/app/src/main/java/com/into/websoso/ui/login/model/LoginUiState.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.into.websoso.ui.login.model - -sealed class LoginUiState { - data object Idle : LoginUiState() - - data object Loading : LoginUiState() - - data class Success( - val isRegistered: Boolean, - ) : LoginUiState() - - data class Failure( - val error: Throwable, - ) : LoginUiState() -} diff --git a/app/src/main/res/drawable/bg_login_white_radius_14dp_stroke_primary100_1dp.xml b/app/src/main/res/drawable/bg_login_white_radius_14dp_stroke_primary100_1dp.xml deleted file mode 100644 index c278e68a6..000000000 --- a/app/src/main/res/drawable/bg_login_white_radius_14dp_stroke_primary100_1dp.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 731f10745..790133b2b 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,6 +1,5 @@ @@ -10,70 +9,7 @@ - - - - - - - - - - From b80380e77ddc51143b21e7764199d36a8beb59fc Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 17:24:32 +0900 Subject: [PATCH 09/36] =?UTF-8?q?feat:=20AuthorizationInterceptor=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/account/AccountModule.kt | 2 +- .../interceptor/AuthorizationInterceptor.kt | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt index 2977b93bd..66c432759 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt @@ -17,5 +17,5 @@ internal interface AccountModule { @Binds @Singleton - fun bindAccountTokenProvider(accountLocalDataSource: AccountLocalDataSource): AccountTokenProvider + fun bindAccountTokenProvider(defaultAccountTokenProvider: DefaultAccountTokenProvider): AccountTokenProvider } diff --git a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt new file mode 100644 index 000000000..2ed18b06e --- /dev/null +++ b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt @@ -0,0 +1,41 @@ +package com.into.websoso.core.network.interceptor + +import com.into.websoso.data.account.AccountTokenProvider +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class AuthorizationInterceptor + @Inject + constructor( + private val accountToken: AccountTokenProvider, + ) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + + if (isSkippedPath(request)) return chain.proceed(request) + + val newRequest = request + .newBuilder() + .addHeader("Authorization", "Bearer $accountToken") + .build() + + return chain.proceed(newRequest) + } + + private fun isSkippedPath(request: Request): Boolean = + EXCLUDED_PATHS.any { path -> + request.url.encodedPath.contains(path) + } + + companion object { + private val EXCLUDED_PATHS = listOf( + "auth/login/kakao", + "reissue", + "minimum-version", + ) + } + } From 300f13e2fdb9cd386093894191c819a1ae819cc2 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 17:25:01 +0900 Subject: [PATCH 10/36] =?UTF-8?q?refactor:=20datastore=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EA=B0=80=EC=8B=9C=EC=84=B1=20=EC=A0=9C=EC=96=B4?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datastore/datasource/account/DefaultAccountDataSource.kt | 2 +- .../datasource/account/DefaultAccountTokenProvider.kt | 2 +- .../com/into/websoso/core/datastore/di/DataStoreModule.kt | 4 ++-- .../com/into/websoso/core/datastore/di/DataStoreQualifier.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt index 21614a211..31d2e51e1 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.last import kotlinx.coroutines.flow.map import javax.inject.Inject -class DefaultAccountDataSource +internal class DefaultAccountDataSource @Inject constructor( @AccountDataStore private val accountDataStore: DataStore, diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt index 3378ca999..e2b6de82d 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt @@ -4,7 +4,7 @@ import com.into.websoso.data.account.AccountTokenProvider import com.into.websoso.data.account.datasource.AccountLocalDataSource import javax.inject.Inject -class DefaultAccountTokenProvider +internal class DefaultAccountTokenProvider @Inject constructor( private val accountDataStore: AccountLocalDataSource, diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt index 7620dc357..9d7d13423 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreModule.kt @@ -17,10 +17,10 @@ internal object DataStoreModule { private const val ACCOUNT_DATASTORE = "ACCOUNT_DATASTORE" private val Context.accountDataStore: DataStore by preferencesDataStore(name = ACCOUNT_DATASTORE) - @Singleton @Provides + @Singleton @AccountDataStore - fun provideAccountPreferencesDataStore( + internal fun provideAccountPreferencesDataStore( @ApplicationContext context: Context, ): DataStore = context.accountDataStore } diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt index d4b5dc92f..bcf9786f0 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/di/DataStoreQualifier.kt @@ -4,4 +4,4 @@ import javax.inject.Qualifier @Qualifier @Retention(AnnotationRetention.BINARY) -annotation class AccountDataStore +internal annotation class AccountDataStore From faef7c0b0a88a98527d7c09a1fd67c43f7087ba6 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 17:25:31 +0900 Subject: [PATCH 11/36] =?UTF-8?q?refactor:=20DefaultAccountDataSource=20?= =?UTF-8?q?=EC=8B=B1=EA=B8=80=ED=86=A4=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/network/datasource/account/DefaultAccountDataSource.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt index ec66482e4..db26c4922 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt @@ -5,9 +5,7 @@ import com.into.websoso.core.auth.AuthToken import com.into.websoso.data.account.AccountEntity import com.into.websoso.data.account.datasource.AccountRemoteDataSource import javax.inject.Inject -import javax.inject.Singleton -@Singleton internal class DefaultAccountDataSource @Inject constructor( From 84eb2f29f1d94314b48d8dfd000b88240b73e454 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 8 May 2025 18:36:04 +0900 Subject: [PATCH 12/36] =?UTF-8?q?refactor:=20AuthorizationInterceptor=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=88=9C?= =?UTF-8?q?=ED=99=98=EC=B0=B8=EC=A1=B0=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/network/interceptor/AuthorizationInterceptor.kt | 9 ++++++--- .../com/into/websoso/data/account/AccountRepository.kt | 5 +++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt index 2ed18b06e..582f261f5 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt @@ -1,26 +1,29 @@ package com.into.websoso.core.network.interceptor -import com.into.websoso.data.account.AccountTokenProvider +import com.into.websoso.data.account.AccountRepository +import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response import javax.inject.Inject +import javax.inject.Provider import javax.inject.Singleton @Singleton internal class AuthorizationInterceptor @Inject constructor( - private val accountToken: AccountTokenProvider, + private val accountRepository: Provider, ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() if (isSkippedPath(request)) return chain.proceed(request) + val token = runBlocking { accountRepository.get().accessToken() } val newRequest = request .newBuilder() - .addHeader("Authorization", "Bearer $accountToken") + .addHeader("Authorization", "Bearer $token") .build() return chain.proceed(newRequest) diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt index bbada0065..66c1d6bf8 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt @@ -12,6 +12,10 @@ class AccountRepository private val accountRemoteDataSource: AccountRemoteDataSource, private val accountLocalDataSource: AccountLocalDataSource, ) { + suspend fun accessToken(): String = accountLocalDataSource.accessToken() + + suspend fun refreshToken(): String = accountLocalDataSource.refreshToken() + suspend fun saveToken( platform: AuthPlatform, authToken: AuthToken, @@ -28,3 +32,4 @@ class AccountRepository return account.isRegister } } +// TODO: 인스턴스 싱글톤 참고하기 From bb2b4011151499aa86fc26af0a01753c5e331704 Mon Sep 17 00:00:00 2001 From: s9hn Date: Fri, 9 May 2025 23:56:12 +0900 Subject: [PATCH 13/36] =?UTF-8?q?feat:=20AuthorizationAuthenticator=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationAuthenticator.kt | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt diff --git a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt new file mode 100644 index 000000000..afe42fd80 --- /dev/null +++ b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt @@ -0,0 +1,82 @@ +package com.into.websoso.core.network.authenticator + +import com.into.websoso.core.common.navigator.WebsosoNavigatorProvider +import com.into.websoso.data.account.AccountRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import okhttp3.Authenticator +import okhttp3.Request +import okhttp3.Response +import okhttp3.Route +import javax.inject.Inject +import javax.inject.Provider +import javax.inject.Singleton + +@Singleton +class AuthorizationAuthenticator + @Inject + constructor( + private val accountRepository: Provider, + private val navigator: WebsosoNavigatorProvider, + ) : Authenticator { + private val mutex = Mutex() + + override fun authenticate( + route: Route?, + response: Response, + ): Request? { + if (response.request.header("Authorization").isNullOrBlank() || + responseCount(response) >= MAX_ATTEMPT_COUNT + ) { + return null + } + + return runBlocking(Dispatchers.IO) { + mutex.withLock { + val updatedAccessToken = accountRepository.get().accessToken() + if (updatedAccessToken.isNotBlank()) { + return@runBlocking response.request + .newBuilder() + .header("Authorization", "Bearer $updatedAccessToken") + .build() + } + + val refreshToken = accountRepository.get().refreshToken() + if (refreshToken.isBlank()) { + navigator.navigateToLoginActivity() + return@runBlocking null + } + + return@runBlocking runCatching { + accountRepository.get().renewToken() + val newAccessToken = accountRepository.get().accessToken() + response.request + .newBuilder() + .header("Authorization", "Bearer $newAccessToken") + .build() + }.getOrElse { + navigator.navigateToLoginActivity() + null + } + } + } + } + + private fun responseCount(response: Response): Int { + var count = 1 + var current = response.priorResponse + while (current != null) { + count++ + current = current.priorResponse + } + return count + } + + private fun responseCount2(response: Response): Int = generateSequence(response) { it.priorResponse }.count() + + companion object { + private const val MAX_ATTEMPT_COUNT = 2 + } + } From 8f750c55b2b439e3e513821c873246abd2cdede6 Mon Sep 17 00:00:00 2001 From: s9hn Date: Sat, 10 May 2025 23:57:55 +0900 Subject: [PATCH 14/36] =?UTF-8?q?refactor:=20common=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/network/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 4bf8ef41f..1d037d422 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { // 프로젝트 의존성 implementation(projects.core.auth) + implementation(projects.core.common) // 네트워크 관련 라이브러리 implementation(libs.retrofit) From 38b09429aca1f4e737a7bd5478dfa7450abb4198 Mon Sep 17 00:00:00 2001 From: s9hn Date: Sun, 11 May 2025 23:40:54 +0900 Subject: [PATCH 15/36] =?UTF-8?q?refactor:=20common=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index da1a7ece4..30944de36 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,6 +76,7 @@ dependencies { // 프로젝트 의존성 implementation(projects.core.resource) implementation(projects.core.designsystem) + implementation(projects.core.common) implementation(projects.core.auth) implementation(projects.core.authKakao) implementation(projects.core.network) From cf55bd930405f0da3156f5fc2c26dbb98e8a855f Mon Sep 17 00:00:00 2001 From: s9hn Date: Mon, 12 May 2025 15:06:43 +0900 Subject: [PATCH 16/36] =?UTF-8?q?refactor:=20=ED=86=A0=ED=81=B0=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/{ => model}/KakaoLoginResponseDto.kt | 11 +++++++---- .../com/into/websoso/data/account/AccountEntity.kt | 7 ------- .../into/websoso/data/account/model/AccountEntity.kt | 6 ++++++ .../into/websoso/data/account/model/TokenEntity.kt | 6 ++++++ 4 files changed, 19 insertions(+), 11 deletions(-) rename core/network/src/main/java/com/into/websoso/core/network/datasource/account/{ => model}/KakaoLoginResponseDto.kt (58%) delete mode 100644 data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt create mode 100644 data/account/src/main/java/com/into/websoso/data/account/model/AccountEntity.kt create mode 100644 data/account/src/main/java/com/into/websoso/data/account/model/TokenEntity.kt diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/KakaoLoginResponseDto.kt similarity index 58% rename from core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt rename to core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/KakaoLoginResponseDto.kt index 862a6f44e..ab428c724 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/KakaoLoginResponseDto.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/KakaoLoginResponseDto.kt @@ -1,6 +1,7 @@ -package com.into.websoso.core.network.datasource.account +package com.into.websoso.core.network.datasource.account.model -import com.into.websoso.data.account.AccountEntity +import com.into.websoso.data.account.model.AccountEntity +import com.into.websoso.data.account.model.TokenEntity import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -15,8 +16,10 @@ internal class KakaoLoginResponseDto( ) { internal fun toData(): AccountEntity = AccountEntity( - accessToken = authorization, - refreshToken = refreshToken, + token = TokenEntity( + accessToken = authorization, + refreshToken = refreshToken, + ), isRegister = isRegister, ) } diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt deleted file mode 100644 index 43c4782f5..000000000 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountEntity.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.into.websoso.data.account - -data class AccountEntity( - val accessToken: String, - val refreshToken: String, - val isRegister: Boolean, -) diff --git a/data/account/src/main/java/com/into/websoso/data/account/model/AccountEntity.kt b/data/account/src/main/java/com/into/websoso/data/account/model/AccountEntity.kt new file mode 100644 index 000000000..b19d6ffa3 --- /dev/null +++ b/data/account/src/main/java/com/into/websoso/data/account/model/AccountEntity.kt @@ -0,0 +1,6 @@ +package com.into.websoso.data.account.model + +data class AccountEntity( + val token: TokenEntity, + val isRegister: Boolean, +) diff --git a/data/account/src/main/java/com/into/websoso/data/account/model/TokenEntity.kt b/data/account/src/main/java/com/into/websoso/data/account/model/TokenEntity.kt new file mode 100644 index 000000000..fafc884f0 --- /dev/null +++ b/data/account/src/main/java/com/into/websoso/data/account/model/TokenEntity.kt @@ -0,0 +1,6 @@ +package com.into.websoso.data.account.model + +data class TokenEntity( + val accessToken: String, + val refreshToken: String, +) From 9a483dd57f7712f79a0b8c433caf74d46bce6a1c Mon Sep 17 00:00:00 2001 From: s9hn Date: Mon, 12 May 2025 15:09:12 +0900 Subject: [PATCH 17/36] =?UTF-8?q?refactor:=20=EB=A6=AC=EC=9D=B4=EC=8A=88ap?= =?UTF-8?q?i=20=EB=AA=A8=EB=93=88=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authenticator/WebsosoAuthenticator.kt | 56 ------------------- .../into/websoso/data/remote/api/AuthApi.kt | 7 --- .../response/KakaoTokenReissueResponseDto.kt | 12 ---- .../websoso/data/repository/AuthRepository.kt | 12 ---- .../into/websoso/ui/main/home/HomeFragment.kt | 5 +- .../ui/onboarding/OnboardingActivity.kt | 10 ---- .../network/datasource/account/AccountApi.kt | 9 +++ .../account/AccountDataSourceModule.kt | 16 ------ .../account/DefaultAccountDataSource.kt | 34 ++++++++++- .../account/model}/TokenReissueRequestDto.kt | 4 +- .../account/model/TokenReissueResponseDto.kt | 19 +++++++ .../websoso/data/account/AccountRepository.kt | 23 ++++++-- .../datasource/AccountRemoteDataSource.kt | 5 +- 13 files changed, 87 insertions(+), 125 deletions(-) delete mode 100644 app/src/main/java/com/into/websoso/data/authenticator/WebsosoAuthenticator.kt delete mode 100644 app/src/main/java/com/into/websoso/data/remote/response/KakaoTokenReissueResponseDto.kt delete mode 100644 core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt rename {app/src/main/java/com/into/websoso/data/remote/request => core/network/src/main/java/com/into/websoso/core/network/datasource/account/model}/TokenReissueRequestDto.kt (60%) create mode 100644 core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueResponseDto.kt diff --git a/app/src/main/java/com/into/websoso/data/authenticator/WebsosoAuthenticator.kt b/app/src/main/java/com/into/websoso/data/authenticator/WebsosoAuthenticator.kt deleted file mode 100644 index fe26783ab..000000000 --- a/app/src/main/java/com/into/websoso/data/authenticator/WebsosoAuthenticator.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.into.websoso.data.authenticator - -import android.content.Context -import com.into.websoso.data.repository.AuthRepository -import com.into.websoso.ui.login.LoginActivity -import com.kakao.sdk.user.UserApiClient -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.runBlocking -import okhttp3.Authenticator -import okhttp3.Request -import okhttp3.Response -import okhttp3.Route -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class WebsosoAuthenticator - @Inject - constructor( - private val authRepository: AuthRepository, - @ApplicationContext private val context: Context, - ) : Authenticator { - override fun authenticate( - route: Route?, - response: Response, - ): Request? { - if (response.request.header("Authorization") == null) { - return null - } - - if (response.code == 401) { - if (authRepository.refreshToken.isBlank()) { - return null - } - - val newAccessToken = runCatching { - runBlocking { - authRepository.reissueToken() - } - }.onFailure { - runBlocking { - authRepository.clearTokens() - UserApiClient.instance.logout { - context.startActivity(LoginActivity.getIntent(context)) - } - } - }.getOrThrow() - - return response.request - .newBuilder() - .header("Authorization", "Bearer $newAccessToken") - .build() - } - return null - } - } diff --git a/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt b/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt index 00d15e15c..34ba169e6 100644 --- a/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt +++ b/app/src/main/java/com/into/websoso/data/remote/api/AuthApi.kt @@ -2,10 +2,8 @@ package com.into.websoso.data.remote.api import com.into.websoso.data.remote.request.FCMTokenRequestDto import com.into.websoso.data.remote.request.LogoutRequestDto -import com.into.websoso.data.remote.request.TokenReissueRequestDto import com.into.websoso.data.remote.request.UserProfileRequestDto import com.into.websoso.data.remote.request.WithdrawRequestDto -import com.into.websoso.data.remote.response.KakaoTokenReissueResponseDto import com.into.websoso.data.remote.response.UserNicknameValidityResponseDto import retrofit2.http.Body import retrofit2.http.GET @@ -26,11 +24,6 @@ interface AuthApi { @Body userProfileRequestDto: UserProfileRequestDto, ) - @POST("reissue") - suspend fun reissueToken( - @Body tokenReissueRequestDto: TokenReissueRequestDto, - ): KakaoTokenReissueResponseDto - @POST("auth/logout") suspend fun logout( @Header("Authorization") authorization: String, diff --git a/app/src/main/java/com/into/websoso/data/remote/response/KakaoTokenReissueResponseDto.kt b/app/src/main/java/com/into/websoso/data/remote/response/KakaoTokenReissueResponseDto.kt deleted file mode 100644 index 0e29c603c..000000000 --- a/app/src/main/java/com/into/websoso/data/remote/response/KakaoTokenReissueResponseDto.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.into.websoso.data.remote.response - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -class KakaoTokenReissueResponseDto( - @SerialName("Authorization") - val authorization: String, - @SerialName("refreshToken") - val refreshToken: String, -) diff --git a/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt b/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt index 36404788d..1214ea6b4 100644 --- a/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt +++ b/app/src/main/java/com/into/websoso/data/repository/AuthRepository.kt @@ -4,7 +4,6 @@ import android.content.SharedPreferences import com.into.websoso.data.remote.api.AuthApi import com.into.websoso.data.remote.request.FCMTokenRequestDto import com.into.websoso.data.remote.request.LogoutRequestDto -import com.into.websoso.data.remote.request.TokenReissueRequestDto import com.into.websoso.data.remote.request.UserProfileRequestDto import com.into.websoso.data.remote.request.WithdrawRequestDto import javax.inject.Inject @@ -80,17 +79,6 @@ class AuthRepository } } - suspend fun reissueToken(): String? = - runCatching { - val response = authApi.reissueToken(TokenReissueRequestDto(refreshToken)) - accessToken = response.authorization - refreshToken = response.refreshToken - response.authorization - }.getOrElse { - it.printStackTrace() - null - } - fun updateAccessToken(accessToken: String) { this.accessToken = accessToken } diff --git a/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt b/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt index 31996f282..0ab0139f6 100644 --- a/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt +++ b/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt @@ -4,6 +4,7 @@ import android.Manifest import android.content.Intent import android.os.Build import android.os.Bundle +import android.util.Log import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.RequestPermission @@ -22,7 +23,6 @@ import com.into.websoso.core.common.ui.model.ResultFrom.ProfileEditSuccess import com.into.websoso.core.common.util.collectWithLifecycle import com.into.websoso.core.common.util.tracker.Tracker import com.into.websoso.core.resource.R.string.home_nickname_interest_feed -import com.into.websoso.data.repository.AuthRepository import com.into.websoso.databinding.FragmentHomeBinding import com.into.websoso.ui.feedDetail.FeedDetailActivity import com.into.websoso.ui.main.MainViewModel @@ -43,8 +43,6 @@ class HomeFragment : BaseFragment(fragment_home) { @Inject lateinit var tracker: Tracker - @Inject - lateinit var authRepository: AuthRepository private val homeViewModel: HomeViewModel by viewModels() private val mainViewModel: MainViewModel by activityViewModels() @@ -153,6 +151,7 @@ class HomeFragment : BaseFragment(fragment_home) { !uiState.loading -> { binding.wllHome.setWebsosoLoadingVisibility(false) + Log.d("123123", uiState.popularNovels.toString()) popularNovelsAdapter.submitList(uiState.popularNovels) popularFeedsAdapter.submitList(uiState.popularFeeds) updateUserInterestFeedsVisibility(uiState.userInterestFeeds.isEmpty()) diff --git a/app/src/main/java/com/into/websoso/ui/onboarding/OnboardingActivity.kt b/app/src/main/java/com/into/websoso/ui/onboarding/OnboardingActivity.kt index 20925b8e1..5d09232c2 100644 --- a/app/src/main/java/com/into/websoso/ui/onboarding/OnboardingActivity.kt +++ b/app/src/main/java/com/into/websoso/ui/onboarding/OnboardingActivity.kt @@ -95,15 +95,5 @@ class OnboardingActivity : BaseActivity(R.layout.acti const val REFRESH_TOKEN_KEY = "REFRESH_TOKEN" fun getIntent(context: Context): Intent = Intent(context, OnboardingActivity::class.java) - - fun getIntent( - context: Context, - accessToken: String, - refreshToken: String, - ): Intent = - Intent(context, OnboardingActivity::class.java).apply { - putExtra(ACCESS_TOKEN_KEY, accessToken) - putExtra(REFRESH_TOKEN_KEY, refreshToken) - } } } diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt index bb91f11dd..c9f15a18d 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountApi.kt @@ -1,10 +1,14 @@ package com.into.websoso.core.network.datasource.account +import com.into.websoso.core.network.datasource.account.model.KakaoLoginResponseDto +import com.into.websoso.core.network.datasource.account.model.TokenReissueRequestDto +import com.into.websoso.core.network.datasource.account.model.TokenReissueResponseDto import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit +import retrofit2.http.Body import retrofit2.http.Header import retrofit2.http.POST import javax.inject.Singleton @@ -14,6 +18,11 @@ internal interface AccountApi { suspend fun postLoginWithKakao( @Header("Kakao-Access-Token") accessToken: String, ): KakaoLoginResponseDto + + @POST("reissue") + suspend fun postReissueToken( + @Body tokenReissueRequestDto: TokenReissueRequestDto, + ): TokenReissueResponseDto } @Module diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt deleted file mode 100644 index 844b97080..000000000 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/AccountDataSourceModule.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.into.websoso.core.network.datasource.account - -import com.into.websoso.data.account.datasource.AccountRemoteDataSource -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal interface AccountDataSourceModule { - @Binds - @Singleton - fun bindAccountRemoteDataSource(defaultAccountDataSource: DefaultAccountDataSource): AccountRemoteDataSource -} diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt index db26c4922..b43e409c8 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt @@ -2,9 +2,17 @@ package com.into.websoso.core.network.datasource.account import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken -import com.into.websoso.data.account.AccountEntity +import com.into.websoso.core.network.datasource.account.model.TokenReissueRequestDto import com.into.websoso.data.account.datasource.AccountRemoteDataSource +import com.into.websoso.data.account.model.AccountEntity +import com.into.websoso.data.account.model.TokenEntity +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.withTimeout import javax.inject.Inject +import javax.inject.Singleton internal class DefaultAccountDataSource @Inject @@ -16,6 +24,28 @@ internal class DefaultAccountDataSource authToken: AuthToken, ): AccountEntity = when (platform) { - AuthPlatform.KAKAO -> accountApi.postLoginWithKakao(authToken.accessToken).toData() + AuthPlatform.KAKAO -> + accountApi + .postLoginWithKakao( + accessToken = authToken.accessToken, + ).toData() + } + + override suspend fun postReissue(refreshToken: String): TokenEntity = + withTimeout(2000) { + accountApi + .postReissueToken( + tokenReissueRequestDto = TokenReissueRequestDto( + refreshToken = refreshToken, + ), + ).toData() } } + +@Module +@InstallIn(SingletonComponent::class) +internal interface AccountDataSourceModule { + @Binds + @Singleton + fun bindAccountRemoteDataSource(defaultAccountDataSource: DefaultAccountDataSource): AccountRemoteDataSource +} diff --git a/app/src/main/java/com/into/websoso/data/remote/request/TokenReissueRequestDto.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueRequestDto.kt similarity index 60% rename from app/src/main/java/com/into/websoso/data/remote/request/TokenReissueRequestDto.kt rename to core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueRequestDto.kt index 7c7254cb0..0561ac4ad 100644 --- a/app/src/main/java/com/into/websoso/data/remote/request/TokenReissueRequestDto.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueRequestDto.kt @@ -1,10 +1,10 @@ -package com.into.websoso.data.remote.request +package com.into.websoso.core.network.datasource.account.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class TokenReissueRequestDto( +internal data class TokenReissueRequestDto( @SerialName("refreshToken") val refreshToken: String, ) diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueResponseDto.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueResponseDto.kt new file mode 100644 index 000000000..9b4159bcc --- /dev/null +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/model/TokenReissueResponseDto.kt @@ -0,0 +1,19 @@ +package com.into.websoso.core.network.datasource.account.model + +import com.into.websoso.data.account.model.TokenEntity +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class TokenReissueResponseDto( + @SerialName("Authorization") + val authorization: String, + @SerialName("refreshToken") + val refreshToken: String, +) { + internal fun toData(): TokenEntity = + TokenEntity( + accessToken = authorization, + refreshToken = refreshToken, + ) +} diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt index 66c1d6bf8..ea3c667ac 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt @@ -4,8 +4,12 @@ import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken import com.into.websoso.data.account.datasource.AccountLocalDataSource import com.into.websoso.data.account.datasource.AccountRemoteDataSource +import kotlinx.coroutines.delay import javax.inject.Inject +import javax.inject.Singleton +// TODO: 인스턴스 싱글톤 참고하기 +@Singleton class AccountRepository @Inject constructor( @@ -25,11 +29,22 @@ class AccountRepository authToken = authToken, ) - accountLocalDataSource.saveAccessToken(account.accessToken) - accountLocalDataSource.saveRefreshToken(account.refreshToken) - if (accountLocalDataSource.isAutoLogin().not()) accountLocalDataSource.saveIsAutoLogin(true) + accountLocalDataSource.saveAccessToken(account.token.accessToken) + accountLocalDataSource.saveRefreshToken(account.token.refreshToken) + + if (accountLocalDataSource.isAutoLogin().not()) { + accountLocalDataSource.saveIsAutoLogin(true) + } return account.isRegister } + + suspend fun renewToken(): String { + val tokens = accountRemoteDataSource.postReissue(refreshToken = refreshToken()) + + accountLocalDataSource.saveAccessToken(tokens.accessToken) + accountLocalDataSource.saveRefreshToken(tokens.refreshToken) + delay(100) + return tokens.accessToken + } } -// TODO: 인스턴스 싱글톤 참고하기 diff --git a/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt b/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt index e6aed5221..bc13f2444 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/datasource/AccountRemoteDataSource.kt @@ -2,11 +2,14 @@ package com.into.websoso.data.account.datasource import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken -import com.into.websoso.data.account.AccountEntity +import com.into.websoso.data.account.model.AccountEntity +import com.into.websoso.data.account.model.TokenEntity interface AccountRemoteDataSource { suspend fun postLogin( platform: AuthPlatform, authToken: AuthToken, ): AccountEntity + + suspend fun postReissue(refreshToken: String): TokenEntity } From c5d3c736e251655fe3f62dbf48e7f54d6dcdc516 Mon Sep 17 00:00:00 2001 From: s9hn Date: Mon, 12 May 2025 15:09:50 +0900 Subject: [PATCH 18/36] =?UTF-8?q?refactor:=20AuthorizationInterceptor=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EB=94=94=EC=8A=A4=ED=8C=A8=EC=B2=98=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network/interceptor/AuthorizationInterceptor.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt index 582f261f5..d84a7eb46 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt @@ -1,6 +1,9 @@ package com.into.websoso.core.network.interceptor +import com.into.websoso.core.common.dispatchers.Dispatcher +import com.into.websoso.core.common.dispatchers.WebsosoDispatchers import com.into.websoso.data.account.AccountRepository +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Request @@ -14,13 +17,14 @@ internal class AuthorizationInterceptor @Inject constructor( private val accountRepository: Provider, + @Dispatcher(WebsosoDispatchers.IO) private val dispatcher: CoroutineDispatcher, ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() - if (isSkippedPath(request)) return chain.proceed(request) + if (shouldSkipCondition(request)) return chain.proceed(request) - val token = runBlocking { accountRepository.get().accessToken() } + val token = runBlocking(dispatcher) { accountRepository.get().accessToken() } val newRequest = request .newBuilder() .addHeader("Authorization", "Bearer $token") @@ -29,7 +33,7 @@ internal class AuthorizationInterceptor return chain.proceed(newRequest) } - private fun isSkippedPath(request: Request): Boolean = + private fun shouldSkipCondition(request: Request): Boolean = EXCLUDED_PATHS.any { path -> request.url.encodedPath.contains(path) } From 0f4e1065c7f2c3fb40eb024c21a4c0d8715c7039 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:34:08 +0900 Subject: [PATCH 19/36] =?UTF-8?q?feat:=20AuthSessionManager=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/auth/build.gradle.kts | 1 + .../websoso/core/auth/AuthSessionManager.kt | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt diff --git a/core/auth/build.gradle.kts b/core/auth/build.gradle.kts index 03b973b77..4a4ae1bee 100644 --- a/core/auth/build.gradle.kts +++ b/core/auth/build.gradle.kts @@ -1,4 +1,5 @@ plugins { id("websoso.jvm.kotlin") + id("websoso.kotlin.coroutines") id("websoso.dagger") } diff --git a/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt b/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt new file mode 100644 index 000000000..d7029b558 --- /dev/null +++ b/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt @@ -0,0 +1,17 @@ +package com.into.websoso.core.auth + +import kotlinx.coroutines.flow.StateFlow + +interface AuthSessionManager { + val sessionState: StateFlow + + fun clearSessionState() + + suspend fun updateSessionState(sessionState: SessionState) +} + +sealed interface SessionState { + data object Expired : SessionState + + data object Idle : SessionState +} From 454c204931f30b5ee6fd97c2d328c8ce451768d4 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:34:32 +0900 Subject: [PATCH 20/36] =?UTF-8?q?feat:=20DispatchersModule=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/dispatchers/DispatchersModule.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 core/common/src/main/java/com/into/websoso/core/common/dispatchers/DispatchersModule.kt diff --git a/core/common/src/main/java/com/into/websoso/core/common/dispatchers/DispatchersModule.kt b/core/common/src/main/java/com/into/websoso/core/common/dispatchers/DispatchersModule.kt new file mode 100644 index 000000000..49c7e3f5e --- /dev/null +++ b/core/common/src/main/java/com/into/websoso/core/common/dispatchers/DispatchersModule.kt @@ -0,0 +1,27 @@ +package com.into.websoso.core.common.dispatchers + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import javax.inject.Qualifier + +@Module +@InstallIn(SingletonComponent::class) +internal object DispatchersModule { + @Provides + @Dispatcher(WebsosoDispatchers.IO) + fun provideIODispatcher(): CoroutineDispatcher = Dispatchers.IO +} + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class Dispatcher( + val dispatchers: WebsosoDispatchers, +) + +enum class WebsosoDispatchers { + IO, +} From a52717282cba52ca21500875de69b4a6db90f3ac Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:35:46 +0900 Subject: [PATCH 21/36] =?UTF-8?q?feat:=20NavigatorProvider=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/util/navigator/WebsosoNavigator.kt | 43 +++++++++++++++++++ .../common/navigator/NavigatorProvider.kt | 19 ++++++++ 2 files changed, 62 insertions(+) create mode 100644 app/src/main/java/com/into/websoso/core/common/util/navigator/WebsosoNavigator.kt create mode 100644 core/common/src/main/java/com/into/websoso/core/common/navigator/NavigatorProvider.kt diff --git a/app/src/main/java/com/into/websoso/core/common/util/navigator/WebsosoNavigator.kt b/app/src/main/java/com/into/websoso/core/common/util/navigator/WebsosoNavigator.kt new file mode 100644 index 000000000..e719ccfae --- /dev/null +++ b/app/src/main/java/com/into/websoso/core/common/util/navigator/WebsosoNavigator.kt @@ -0,0 +1,43 @@ +package com.into.websoso.core.common.util.navigator + +import android.content.Context +import com.into.websoso.core.common.navigator.NavigatorProvider +import com.into.websoso.ui.login.LoginActivity +import com.into.websoso.ui.main.MainActivity +import com.into.websoso.ui.onboarding.OnboardingActivity +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Inject +import javax.inject.Singleton + +internal class WebsosoNavigator + @Inject + constructor( + @ApplicationContext private val context: Context, + ) : NavigatorProvider { + override fun navigateToLoginActivity() { + val intent = LoginActivity.getIntent(context) + context.startActivity(intent) + } + + override fun navigateToMainActivity() { + val intent = MainActivity.getIntent(context, true) + context.startActivity(intent) + } + + override fun navigateToOnboardingActivity() { + val intent = OnboardingActivity.getIntent(context) + context.startActivity(intent) + } + } + +@Module +@InstallIn(SingletonComponent::class) +internal interface NavigatorModule { + @Binds + @Singleton + fun bindWebsosoNavigator(websosoNavigator: WebsosoNavigator): NavigatorProvider +} diff --git a/core/common/src/main/java/com/into/websoso/core/common/navigator/NavigatorProvider.kt b/core/common/src/main/java/com/into/websoso/core/common/navigator/NavigatorProvider.kt new file mode 100644 index 000000000..86308dc32 --- /dev/null +++ b/core/common/src/main/java/com/into/websoso/core/common/navigator/NavigatorProvider.kt @@ -0,0 +1,19 @@ +package com.into.websoso.core.common.navigator + +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +interface NavigatorProvider { + fun navigateToLoginActivity() + + fun navigateToMainActivity() + + fun navigateToOnboardingActivity() +} + +@EntryPoint +@InstallIn(SingletonComponent::class) +interface NavigatorEntryPoint { + fun provideNavigator(): NavigatorProvider +} From 86de330e05bbbca49c1459b0d7760fd6c1e9749a Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:36:55 +0900 Subject: [PATCH 22/36] =?UTF-8?q?refactor:=20DefaultAccountDataSource=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/account/DefaultAccountDataSource.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt index 31d2e51e1..df722ee94 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt @@ -7,7 +7,7 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import com.into.websoso.core.datastore.di.AccountDataStore import com.into.websoso.data.account.datasource.AccountLocalDataSource -import kotlinx.coroutines.flow.last +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import javax.inject.Inject @@ -20,19 +20,19 @@ internal class DefaultAccountDataSource accountDataStore.data .map { preferences -> preferences[ACCESS_TOKEN].orEmpty() - }.last() + }.first() override suspend fun refreshToken(): String = accountDataStore.data .map { preferences -> preferences[REFRESH_TOKEN].orEmpty() - }.last() + }.first() override suspend fun isAutoLogin(): Boolean = accountDataStore.data .map { preferences -> preferences[IS_LOGIN] ?: false - }.last() + }.first() override suspend fun saveAccessToken(accessToken: String) { accountDataStore.edit { preferences -> From d8389d8172e20cb355590f62540c5874349e7df4 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:37:55 +0900 Subject: [PATCH 23/36] =?UTF-8?q?refactor:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20SignInScreen=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/into/websoso/ui/login/LoginActivity.kt | 5 +++++ .../com/into/websoso/feature/signin/SignInScreen.kt | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/into/websoso/ui/login/LoginActivity.kt b/app/src/main/java/com/into/websoso/ui/login/LoginActivity.kt index 1d13428d6..c0646328c 100644 --- a/app/src/main/java/com/into/websoso/ui/login/LoginActivity.kt +++ b/app/src/main/java/com/into/websoso/ui/login/LoginActivity.kt @@ -13,6 +13,7 @@ import com.google.firebase.analytics.analytics import com.into.websoso.R.layout.activity_login import com.into.websoso.core.auth.AuthClient import com.into.websoso.core.auth.AuthPlatform +import com.into.websoso.core.common.navigator.NavigatorProvider import com.into.websoso.core.common.ui.base.BaseActivity import com.into.websoso.core.designsystem.theme.WebsosoTheme import com.into.websoso.databinding.ActivityLoginBinding @@ -28,6 +29,9 @@ class LoginActivity : BaseActivity(activity_login) { @Inject lateinit var authClient: Map + @Inject + lateinit var websosoNavigator: NavigatorProvider + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -38,6 +42,7 @@ class LoginActivity : BaseActivity(activity_login) { authClient = { platform -> authClient[platform] ?: throw IllegalStateException() }, + websosoNavigator = websosoNavigator, ) } } diff --git a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt index f5bf11094..be68a169a 100644 --- a/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt +++ b/feature/signin/src/main/java/com/into/websoso/feature/signin/SignInScreen.kt @@ -18,6 +18,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.into.websoso.core.auth.AuthClient import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.common.extensions.collectAsEventWithLifecycle +import com.into.websoso.core.common.navigator.NavigatorProvider import com.into.websoso.core.designsystem.theme.Gray50 import com.into.websoso.core.designsystem.theme.WebsosoTheme import com.into.websoso.feature.signin.UiEffect.NavigateToHome @@ -32,6 +33,7 @@ import com.into.websoso.feature.signin.component.SignInButtons @Composable fun SignInScreen( authClient: (platform: AuthPlatform) -> AuthClient, + websosoNavigator: NavigatorProvider, signInViewModel: SignInViewModel = hiltViewModel(), ) { val latestEvent by rememberUpdatedState(signInViewModel.uiEvent) @@ -49,13 +51,9 @@ fun SignInScreen( // TODO: 실패 시 커스텀 스낵 바 구현 } - NavigateToHome -> { - // TODO: 홈 이동 - } + NavigateToHome -> websosoNavigator.navigateToMainActivity() - NavigateToOnboarding -> { - // TODO: 온보딩 이동 - } + NavigateToOnboarding -> websosoNavigator.navigateToOnboardingActivity() } } From 4c0b9777a05fc4389d279ddc65f5afad0fef49ec Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:38:12 +0900 Subject: [PATCH 24/36] =?UTF-8?q?feat:=20WebsosoAuthSessionManager=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WebsosoAuthSessionManager.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt diff --git a/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt b/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt new file mode 100644 index 000000000..4ccf8df83 --- /dev/null +++ b/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt @@ -0,0 +1,38 @@ +package com.into.websoso.core.common.util.sessionManager + +import com.into.websoso.core.auth.AuthSessionManager +import com.into.websoso.core.auth.SessionState +import com.into.websoso.core.auth.SessionState.Idle +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject +import javax.inject.Singleton + +internal class WebsosoAuthSessionManager + @Inject + constructor() : AuthSessionManager { + private val _sessionState = MutableStateFlow(Idle) + override val sessionState: StateFlow get() = _sessionState.asStateFlow() + + override fun clearSessionState() { + _sessionState.update { Idle } + } + + override suspend fun updateSessionState(sessionState: SessionState) { + _sessionState.update { sessionState } + } + } + +@Module +@InstallIn(SingletonComponent::class) +internal interface WebsosoAuthSessionManagerModule { + @Binds + @Singleton + fun bindWebsosoAuthSessionManager(websosoAuthSessionManager: WebsosoAuthSessionManager): AuthSessionManager +} From 8cf130afe71daa2b6d3aeba02d7ee10a01d9d8c5 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:39:03 +0900 Subject: [PATCH 25/36] =?UTF-8?q?refactor:=20AuthorizationAuthenticator=20?= =?UTF-8?q?=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationAuthenticator.kt | 91 ++++++++++--------- .../websoso/core/network/di/NetworkModule.kt | 14 ++- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt index afe42fd80..6aa358187 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt @@ -1,8 +1,11 @@ package com.into.websoso.core.network.authenticator -import com.into.websoso.core.common.navigator.WebsosoNavigatorProvider +import com.into.websoso.core.auth.AuthSessionManager +import com.into.websoso.core.auth.SessionState.Expired +import com.into.websoso.core.common.dispatchers.Dispatcher +import com.into.websoso.core.common.dispatchers.WebsosoDispatchers import com.into.websoso.data.account.AccountRepository -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -15,66 +18,72 @@ import javax.inject.Provider import javax.inject.Singleton @Singleton -class AuthorizationAuthenticator +internal class AuthorizationAuthenticator @Inject constructor( private val accountRepository: Provider, - private val navigator: WebsosoNavigatorProvider, + private val sessionManager: AuthSessionManager, + @Dispatcher(WebsosoDispatchers.IO) private val dispatcher: CoroutineDispatcher, ) : Authenticator { - private val mutex = Mutex() + private val mutex: Mutex = Mutex() override fun authenticate( route: Route?, response: Response, ): Request? { - if (response.request.header("Authorization").isNullOrBlank() || - responseCount(response) >= MAX_ATTEMPT_COUNT - ) { - return null - } + if (shouldSkipCondition(response)) return null - return runBlocking(Dispatchers.IO) { + val renewedToken = runBlocking(dispatcher) { mutex.withLock { - val updatedAccessToken = accountRepository.get().accessToken() - if (updatedAccessToken.isNotBlank()) { - return@runBlocking response.request - .newBuilder() - .header("Authorization", "Bearer $updatedAccessToken") - .build() - } - - val refreshToken = accountRepository.get().refreshToken() - if (refreshToken.isBlank()) { - navigator.navigateToLoginActivity() + if (accountRepository.get().refreshToken().isBlank()) { + sessionManager.updateSessionState(Expired) return@runBlocking null } - return@runBlocking runCatching { - accountRepository.get().renewToken() - val newAccessToken = accountRepository.get().accessToken() - response.request - .newBuilder() - .header("Authorization", "Bearer $newAccessToken") - .build() - }.getOrElse { - navigator.navigateToLoginActivity() - null + if (response.isRefreshNeeded()) { + renewToken(response) + } else { + return@withLock accountRepository.get().accessToken() } } } - } - private fun responseCount(response: Response): Int { - var count = 1 - var current = response.priorResponse - while (current != null) { - count++ - current = current.priorResponse + return renewedToken.let { token -> + response.request + .newBuilder() + .header("Authorization", "Bearer $token") + .build() } - return count } - private fun responseCount2(response: Response): Int = generateSequence(response) { it.priorResponse }.count() + private suspend fun Response.isRefreshNeeded(): Boolean { + val updatedAccessToken = accountRepository.get().accessToken() + val oldAccessToken = request + .header("Authorization") + ?.removePrefix("Bearer ") + + return oldAccessToken == updatedAccessToken + } + + private suspend fun renewToken(response: Response): String? = + runCatching { + accountRepository.get().renewToken() + }.fold( + onSuccess = { updatedAccessToken -> updatedAccessToken }, + onFailure = { + sessionManager.updateSessionState(Expired) + null + }, + ) + + private fun shouldSkipCondition(response: Response): Boolean = + response.request.header("Authorization").isNullOrBlank() || + response.retryAttemptCount() >= MAX_ATTEMPT_COUNT + + private fun Response.retryAttemptCount(): Int = + generateSequence(this) { + it.priorResponse + }.count() companion object { private const val MAX_ATTEMPT_COUNT = 2 diff --git a/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt index b8f254e8a..fbf9bd275 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt @@ -1,6 +1,7 @@ package com.into.websoso.core.network.di import com.into.websoso.core.network.BuildConfig +import com.into.websoso.core.network.authenticator.AuthorizationAuthenticator import com.into.websoso.core.network.interceptor.AuthorizationInterceptor import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module @@ -12,12 +13,16 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit +import java.util.concurrent.TimeUnit import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) internal object NetworkModule { private const val BASE_URL = BuildConfig.BASE_URL + private const val CONNECT_TIME_LIMIT = 60L + private const val READ_TIME_LIMIT = 30L + private const val WRITE_TIME_LIMIT = 15L private const val CONTENT_TYPE = "application/json" private val httpLoggingInterceptor: HttpLoggingInterceptor by lazy { HttpLoggingInterceptor().apply { @@ -44,10 +49,17 @@ internal object NetworkModule { @Provides @Singleton - internal fun provideOkHttpClient(authorizationInterceptor: AuthorizationInterceptor): OkHttpClient = + internal fun provideOkHttpClient( + authorizationAuthenticator: AuthorizationAuthenticator, + authorizationInterceptor: AuthorizationInterceptor, + ): OkHttpClient = OkHttpClient .Builder() .addInterceptor(httpLoggingInterceptor) .addInterceptor(authorizationInterceptor) + .authenticator(authorizationAuthenticator) + .connectTimeout(CONNECT_TIME_LIMIT, TimeUnit.SECONDS) + .readTimeout(READ_TIME_LIMIT, TimeUnit.SECONDS) + .writeTimeout(WRITE_TIME_LIMIT, TimeUnit.SECONDS) .build() } From 19a426b9c7e5a2e71d2e420141309bab9280b0b6 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 00:39:32 +0900 Subject: [PATCH 26/36] =?UTF-8?q?feat:=20WebsosoApp=20subscribeSessionStat?= =?UTF-8?q?e=20=ED=95=A8=EC=88=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/into/websoso/WebsosoApp.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/src/main/java/com/into/websoso/WebsosoApp.kt b/app/src/main/java/com/into/websoso/WebsosoApp.kt index 63f2a2d23..fb67179cf 100644 --- a/app/src/main/java/com/into/websoso/WebsosoApp.kt +++ b/app/src/main/java/com/into/websoso/WebsosoApp.kt @@ -2,16 +2,38 @@ package com.into.websoso import android.app.Application import androidx.appcompat.app.AppCompatDelegate +import androidx.lifecycle.ProcessLifecycleOwner import com.into.websoso.BuildConfig.KAKAO_APP_KEY +import com.into.websoso.core.auth.AuthSessionManager +import com.into.websoso.core.auth.SessionState +import com.into.websoso.core.common.navigator.NavigatorProvider +import com.into.websoso.core.common.util.collectWithLifecycle import com.kakao.sdk.common.KakaoSdk import dagger.hilt.android.HiltAndroidApp +import javax.inject.Inject @HiltAndroidApp class WebsosoApp : Application() { + @Inject + lateinit var sessionManager: AuthSessionManager + + @Inject + lateinit var navigatorProvider: NavigatorProvider + override fun onCreate() { super.onCreate() AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + subscribeSessionState() KakaoSdk.init(this, KAKAO_APP_KEY) } + + private fun subscribeSessionState() { + sessionManager.sessionState.collectWithLifecycle(ProcessLifecycleOwner.get()) { state -> + if (state is SessionState.Expired) { + sessionManager.clearSessionState() + navigatorProvider.navigateToLoginActivity() + } + } + } } From 119fd05e9da51293c8a0c3a65192db451adabc50 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 18:36:07 +0900 Subject: [PATCH 27/36] =?UTF-8?q?feat:=20ThrottleHelper=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/common/extensions/ThrottleHelper.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 core/common/src/main/java/com/into/websoso/core/common/extensions/ThrottleHelper.kt diff --git a/core/common/src/main/java/com/into/websoso/core/common/extensions/ThrottleHelper.kt b/core/common/src/main/java/com/into/websoso/core/common/extensions/ThrottleHelper.kt new file mode 100644 index 000000000..aeb6ff35b --- /dev/null +++ b/core/common/src/main/java/com/into/websoso/core/common/extensions/ThrottleHelper.kt @@ -0,0 +1,42 @@ +package com.into.websoso.core.common.extensions + +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.TimeMark +import kotlin.time.TimeSource + +@Singleton +class ThrottleHelper + @Inject + constructor() { + private val mutex = Mutex() + private var lastExecutionTime: TimeMark? = null + + suspend operator fun invoke( + durationMillis: Long = DEFAULT_THROTTLE_DURATION, + block: suspend () -> T, + ): T? { + val now = TimeSource.Monotonic.markNow() + val shouldExecute = mutex.withLock { + val canRun = if (lastExecutionTime == null) { + true + } else { + val elapsed = lastExecutionTime!!.elapsedNow() + elapsed >= durationMillis.milliseconds + } + + if (canRun) lastExecutionTime = now + + canRun + } + + return if (shouldExecute) block() else null + } + + companion object { + private const val DEFAULT_THROTTLE_DURATION = 1000L + } + } From b0163a3f0c9a8a012e3f803700de028b78ad5cb1 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 18:36:51 +0900 Subject: [PATCH 28/36] =?UTF-8?q?refactor:=20maxRequestsPerHost=2020?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/DefaultAccountDataSource.kt | 15 ++++++--------- .../into/websoso/core/network/di/NetworkModule.kt | 7 +++++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt index b43e409c8..9b5447a87 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/datasource/account/DefaultAccountDataSource.kt @@ -10,7 +10,6 @@ import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.withTimeout import javax.inject.Inject import javax.inject.Singleton @@ -32,14 +31,12 @@ internal class DefaultAccountDataSource } override suspend fun postReissue(refreshToken: String): TokenEntity = - withTimeout(2000) { - accountApi - .postReissueToken( - tokenReissueRequestDto = TokenReissueRequestDto( - refreshToken = refreshToken, - ), - ).toData() - } + accountApi + .postReissueToken( + tokenReissueRequestDto = TokenReissueRequestDto( + refreshToken = refreshToken, + ), + ).toData() } @Module diff --git a/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt index fbf9bd275..a232550b9 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/di/NetworkModule.kt @@ -9,6 +9,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kotlinx.serialization.json.Json +import okhttp3.Dispatcher import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -29,6 +30,11 @@ internal object NetworkModule { if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY) } } + private val dispatcher: Dispatcher by lazy { + Dispatcher().apply { + maxRequestsPerHost = 20 + } + } @Provides @Singleton @@ -55,6 +61,7 @@ internal object NetworkModule { ): OkHttpClient = OkHttpClient .Builder() + .dispatcher(dispatcher) .addInterceptor(httpLoggingInterceptor) .addInterceptor(authorizationInterceptor) .authenticator(authorizationAuthenticator) From 5c90e3ff4eca4f1d105cd6ed4ea6aebf90565460 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 18:37:33 +0900 Subject: [PATCH 29/36] =?UTF-8?q?refactor:=20AuthorizationAuthenticator?= =?UTF-8?q?=EC=97=90=20=EC=8A=A4=EB=A1=9C=ED=8B=80=EB=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=A0=84?= =?UTF-8?q?=ED=8C=8C=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/into/websoso/WebsosoApp.kt | 8 ++--- .../WebsosoAuthSessionManager.kt | 21 +++++--------- .../into/websoso/ui/main/home/HomeFragment.kt | 2 -- .../websoso/core/auth/AuthSessionManager.kt | 14 ++------- .../AuthorizationAuthenticator.kt | 29 ++++++++++--------- 5 files changed, 27 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/into/websoso/WebsosoApp.kt b/app/src/main/java/com/into/websoso/WebsosoApp.kt index fb67179cf..a376f4375 100644 --- a/app/src/main/java/com/into/websoso/WebsosoApp.kt +++ b/app/src/main/java/com/into/websoso/WebsosoApp.kt @@ -5,7 +5,6 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.ProcessLifecycleOwner import com.into.websoso.BuildConfig.KAKAO_APP_KEY import com.into.websoso.core.auth.AuthSessionManager -import com.into.websoso.core.auth.SessionState import com.into.websoso.core.common.navigator.NavigatorProvider import com.into.websoso.core.common.util.collectWithLifecycle import com.kakao.sdk.common.KakaoSdk @@ -29,11 +28,8 @@ class WebsosoApp : Application() { } private fun subscribeSessionState() { - sessionManager.sessionState.collectWithLifecycle(ProcessLifecycleOwner.get()) { state -> - if (state is SessionState.Expired) { - sessionManager.clearSessionState() - navigatorProvider.navigateToLoginActivity() - } + sessionManager.sessionExpired.collectWithLifecycle(ProcessLifecycleOwner.get()) { + navigatorProvider.navigateToLoginActivity() } } } diff --git a/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt b/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt index 4ccf8df83..f6cad7ed8 100644 --- a/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt +++ b/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt @@ -1,31 +1,24 @@ package com.into.websoso.core.common.util.sessionManager import com.into.websoso.core.auth.AuthSessionManager -import com.into.websoso.core.auth.SessionState -import com.into.websoso.core.auth.SessionState.Idle import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow import javax.inject.Inject import javax.inject.Singleton internal class WebsosoAuthSessionManager @Inject constructor() : AuthSessionManager { - private val _sessionState = MutableStateFlow(Idle) - override val sessionState: StateFlow get() = _sessionState.asStateFlow() + private val _sessionExpired = MutableSharedFlow(extraBufferCapacity = 1) + override val sessionExpired: SharedFlow get() = _sessionExpired.asSharedFlow() - override fun clearSessionState() { - _sessionState.update { Idle } - } - - override suspend fun updateSessionState(sessionState: SessionState) { - _sessionState.update { sessionState } + override suspend fun onSessionExpired() { + _sessionExpired.emit(Unit) } } diff --git a/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt b/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt index 0ab0139f6..b988a3dba 100644 --- a/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt +++ b/app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt @@ -4,7 +4,6 @@ import android.Manifest import android.content.Intent import android.os.Build import android.os.Bundle -import android.util.Log import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts.RequestPermission @@ -151,7 +150,6 @@ class HomeFragment : BaseFragment(fragment_home) { !uiState.loading -> { binding.wllHome.setWebsosoLoadingVisibility(false) - Log.d("123123", uiState.popularNovels.toString()) popularNovelsAdapter.submitList(uiState.popularNovels) popularFeedsAdapter.submitList(uiState.popularFeeds) updateUserInterestFeedsVisibility(uiState.userInterestFeeds.isEmpty()) diff --git a/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt b/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt index d7029b558..79fbbb542 100644 --- a/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt +++ b/core/auth/src/main/java/com/into/websoso/core/auth/AuthSessionManager.kt @@ -1,17 +1,9 @@ package com.into.websoso.core.auth -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.SharedFlow interface AuthSessionManager { - val sessionState: StateFlow + val sessionExpired: SharedFlow - fun clearSessionState() - - suspend fun updateSessionState(sessionState: SessionState) -} - -sealed interface SessionState { - data object Expired : SessionState - - data object Idle : SessionState + suspend fun onSessionExpired() } diff --git a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt index 6aa358187..dcd699f65 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt @@ -1,9 +1,9 @@ package com.into.websoso.core.network.authenticator import com.into.websoso.core.auth.AuthSessionManager -import com.into.websoso.core.auth.SessionState.Expired import com.into.websoso.core.common.dispatchers.Dispatcher import com.into.websoso.core.common.dispatchers.WebsosoDispatchers +import com.into.websoso.core.common.extensions.ThrottleHelper import com.into.websoso.data.account.AccountRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.runBlocking @@ -23,6 +23,7 @@ internal class AuthorizationAuthenticator constructor( private val accountRepository: Provider, private val sessionManager: AuthSessionManager, + private val throttle: ThrottleHelper, @Dispatcher(WebsosoDispatchers.IO) private val dispatcher: CoroutineDispatcher, ) : Authenticator { private val mutex: Mutex = Mutex() @@ -36,12 +37,12 @@ internal class AuthorizationAuthenticator val renewedToken = runBlocking(dispatcher) { mutex.withLock { if (accountRepository.get().refreshToken().isBlank()) { - sessionManager.updateSessionState(Expired) + sessionManager.onSessionExpired() return@runBlocking null } if (response.isRefreshNeeded()) { - renewToken(response) + throttle { renewToken() } } else { return@withLock accountRepository.get().accessToken() } @@ -56,6 +57,15 @@ internal class AuthorizationAuthenticator } } + private fun shouldSkipCondition(response: Response): Boolean = + response.request.header("Authorization").isNullOrBlank() || + response.retryAttemptCount() >= MAX_ATTEMPT_COUNT + + private fun Response.retryAttemptCount(): Int = + generateSequence(this) { + it.priorResponse + }.count() + private suspend fun Response.isRefreshNeeded(): Boolean { val updatedAccessToken = accountRepository.get().accessToken() val oldAccessToken = request @@ -65,26 +75,17 @@ internal class AuthorizationAuthenticator return oldAccessToken == updatedAccessToken } - private suspend fun renewToken(response: Response): String? = + private suspend fun renewToken(): String? = runCatching { accountRepository.get().renewToken() }.fold( onSuccess = { updatedAccessToken -> updatedAccessToken }, onFailure = { - sessionManager.updateSessionState(Expired) + sessionManager.onSessionExpired() null }, ) - private fun shouldSkipCondition(response: Response): Boolean = - response.request.header("Authorization").isNullOrBlank() || - response.retryAttemptCount() >= MAX_ATTEMPT_COUNT - - private fun Response.retryAttemptCount(): Int = - generateSequence(this) { - it.priorResponse - }.count() - companion object { private const val MAX_ATTEMPT_COUNT = 2 } From 4a920ba48ee5a158ec635f6d4ac0af1962fe5103 Mon Sep 17 00:00:00 2001 From: s9hn Date: Tue, 13 May 2025 19:05:21 +0900 Subject: [PATCH 30/36] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/account/AccountModule.kt | 21 ------------------- .../account/DefaultAccountTokenProvider.kt | 15 ------------- .../data/account/AccountTokenProvider.kt | 5 ----- 3 files changed, 41 deletions(-) delete mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt delete mode 100644 core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt delete mode 100644 data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt deleted file mode 100644 index 66c432759..000000000 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/AccountModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.into.websoso.core.datastore.datasource.account - -import com.into.websoso.data.account.AccountTokenProvider -import com.into.websoso.data.account.datasource.AccountLocalDataSource -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal interface AccountModule { - @Binds - @Singleton - fun bindAccountLocalDataSource(defaultAccountDataSource: DefaultAccountDataSource): AccountLocalDataSource - - @Binds - @Singleton - fun bindAccountTokenProvider(defaultAccountTokenProvider: DefaultAccountTokenProvider): AccountTokenProvider -} diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt deleted file mode 100644 index e2b6de82d..000000000 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountTokenProvider.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.into.websoso.core.datastore.datasource.account - -import com.into.websoso.data.account.AccountTokenProvider -import com.into.websoso.data.account.datasource.AccountLocalDataSource -import javax.inject.Inject - -internal class DefaultAccountTokenProvider - @Inject - constructor( - private val accountDataStore: AccountLocalDataSource, - ) : AccountTokenProvider { - override suspend fun invoke() { - accountDataStore.accessToken() - } - } diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt deleted file mode 100644 index 5d418cdbf..000000000 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountTokenProvider.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.into.websoso.data.account - -fun interface AccountTokenProvider { - suspend operator fun invoke() -} From 4f828a437f4d4a977a2378c0d03727566a029a72 Mon Sep 17 00:00:00 2001 From: s9hn Date: Wed, 14 May 2025 16:08:53 +0900 Subject: [PATCH 31/36] =?UTF-8?q?refactor:=20=EB=88=84=EB=9D=BD=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80,=20DefaultAccountDataSo?= =?UTF-8?q?urce=20=EB=AA=A8=EB=93=88=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/account/DefaultAccountDataSource.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt index df722ee94..9d1c41c43 100644 --- a/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt +++ b/core/datastore/src/main/java/com/into/websoso/core/datastore/datasource/account/DefaultAccountDataSource.kt @@ -7,9 +7,14 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import com.into.websoso.core.datastore.di.AccountDataStore import com.into.websoso.data.account.datasource.AccountLocalDataSource +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import javax.inject.Inject +import javax.inject.Singleton internal class DefaultAccountDataSource @Inject @@ -58,3 +63,11 @@ internal class DefaultAccountDataSource private val IS_LOGIN = booleanPreferencesKey("IS_LOGIN") } } + +@Module +@InstallIn(SingletonComponent::class) +internal interface AccountDataSourceModule { + @Binds + @Singleton + fun bindAccountLocalDataSource(defaultAccountDataSource: DefaultAccountDataSource): AccountLocalDataSource +} From 82c36ca4bc92caf5a7ca21b5ea06f8f9919901a2 Mon Sep 17 00:00:00 2001 From: s9hn Date: Wed, 14 May 2025 16:30:06 +0900 Subject: [PATCH 32/36] =?UTF-8?q?refactor:=20AuthorizationInterceptor=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=EC=9D=B4=20=EB=B9=88=EA=B0=92=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EB=A6=AC=ED=84=B4=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/network/interceptor/AuthorizationInterceptor.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt index d84a7eb46..42eca870c 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/interceptor/AuthorizationInterceptor.kt @@ -25,6 +25,9 @@ internal class AuthorizationInterceptor if (shouldSkipCondition(request)) return chain.proceed(request) val token = runBlocking(dispatcher) { accountRepository.get().accessToken() } + + if (token.isBlank()) return chain.proceed(request) + val newRequest = request .newBuilder() .addHeader("Authorization", "Bearer $token") From 0daf45ebcf976e7c84849a487d777e8912cbfeb8 Mon Sep 17 00:00:00 2001 From: s9hn Date: Wed, 14 May 2025 16:47:48 +0900 Subject: [PATCH 33/36] =?UTF-8?q?refactor:=20AuthorizationAuthenticator=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EC=96=BC=EB=A6=AC=20?= =?UTF-8?q?=EB=A6=AC=ED=84=B4=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authenticator/AuthorizationAuthenticator.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt index dcd699f65..b225d3efb 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt @@ -47,14 +47,12 @@ internal class AuthorizationAuthenticator return@withLock accountRepository.get().accessToken() } } - } + } ?: return null - return renewedToken.let { token -> - response.request - .newBuilder() - .header("Authorization", "Bearer $token") - .build() - } + return response.request + .newBuilder() + .header("Authorization", "Bearer $renewedToken") + .build() } private fun shouldSkipCondition(response: Response): Boolean = From da360207c7f55742ae93d4475128692bb99cbc20 Mon Sep 17 00:00:00 2001 From: s9hn Date: Wed, 14 May 2025 18:27:43 +0900 Subject: [PATCH 34/36] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EB=B9=88=EA=B0=92=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20when=EB=AC=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authenticator/AuthorizationAuthenticator.kt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt index b225d3efb..9126db0f5 100644 --- a/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt +++ b/core/network/src/main/java/com/into/websoso/core/network/authenticator/AuthorizationAuthenticator.kt @@ -36,15 +36,9 @@ internal class AuthorizationAuthenticator val renewedToken = runBlocking(dispatcher) { mutex.withLock { - if (accountRepository.get().refreshToken().isBlank()) { - sessionManager.onSessionExpired() - return@runBlocking null - } - - if (response.isRefreshNeeded()) { - throttle { renewToken() } - } else { - return@withLock accountRepository.get().accessToken() + when (response.isRefreshNeeded()) { + true -> throttle { renewToken() } + false -> return@withLock accountRepository.get().accessToken() } } } ?: return null From 7dcfe828a672165b438d67564921542db428fe21 Mon Sep 17 00:00:00 2001 From: s9hn Date: Wed, 14 May 2025 18:28:07 +0900 Subject: [PATCH 35/36] =?UTF-8?q?refactor:=20WebsosoAuthSessionManager=20r?= =?UTF-8?q?eplay=201=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/util/sessionManager/WebsosoAuthSessionManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt b/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt index f6cad7ed8..a7b598203 100644 --- a/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt +++ b/app/src/main/java/com/into/websoso/core/common/util/sessionManager/WebsosoAuthSessionManager.kt @@ -14,7 +14,7 @@ import javax.inject.Singleton internal class WebsosoAuthSessionManager @Inject constructor() : AuthSessionManager { - private val _sessionExpired = MutableSharedFlow(extraBufferCapacity = 1) + private val _sessionExpired = MutableSharedFlow(replay = 1, extraBufferCapacity = 1) override val sessionExpired: SharedFlow get() = _sessionExpired.asSharedFlow() override suspend fun onSessionExpired() { From 462d3000850669051b4f2a0573593f2a5607ab91 Mon Sep 17 00:00:00 2001 From: s9hn Date: Thu, 15 May 2025 01:31:05 +0900 Subject: [PATCH 36/36] =?UTF-8?q?refactor:=20renewToken=20delay=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/into/websoso/data/account/AccountRepository.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt index ea3c667ac..9c8c35283 100644 --- a/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt +++ b/data/account/src/main/java/com/into/websoso/data/account/AccountRepository.kt @@ -4,7 +4,6 @@ import com.into.websoso.core.auth.AuthPlatform import com.into.websoso.core.auth.AuthToken import com.into.websoso.data.account.datasource.AccountLocalDataSource import com.into.websoso.data.account.datasource.AccountRemoteDataSource -import kotlinx.coroutines.delay import javax.inject.Inject import javax.inject.Singleton @@ -44,7 +43,7 @@ class AccountRepository accountLocalDataSource.saveAccessToken(tokens.accessToken) accountLocalDataSource.saveRefreshToken(tokens.refreshToken) - delay(100) + return tokens.accessToken } }