-
Notifications
You must be signed in to change notification settings - Fork 2
[feat/#135] google login 세팅 #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
8929711
27348a8
0ad351e
f71faa3
658f4c1
c2f6470
ac96d91
06e30d1
139c122
17259d5
fe270d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,10 @@ private val SELECTED_GU_NAME_KEY = stringPreferencesKey("selected_gu_name") | |
| private val SELECTED_DONG_NAME_KEY = stringPreferencesKey("selected_dong_name") | ||
| private val ACTIVE_REGION_KEY = stringPreferencesKey("active_region") | ||
|
|
||
| // 토큰 관리를 위한 키들 | ||
| private val ACCESS_TOKEN_KEY = stringPreferencesKey("access_token") | ||
| private val REFRESH_TOKEN_KEY = stringPreferencesKey("refresh_token") | ||
|
|
||
|
||
| private fun List<LatLng>.toPreferenceString(): String = | ||
| joinToString(";") { "${it.latitude},${it.longitude}" } | ||
|
|
||
|
|
@@ -46,7 +50,6 @@ object PreferenceDataStore { | |
| private val summaryStore | ||
| get() = appContext.summaryStore | ||
|
|
||
| // 기존 함수들... | ||
| suspend fun saveWalkSummary( | ||
| points: List<LatLng>, | ||
| totalDistance: Float, | ||
|
|
@@ -287,6 +290,63 @@ object PreferenceDataStore { | |
| } | ||
| } | ||
|
|
||
| // ===== 토큰 관리 관련 함수들 ===== | ||
|
|
||
| /** | ||
| * 액세스 토큰과 리프레시 토큰을 저장합니다 | ||
| */ | ||
| suspend fun saveTokens(accessToken: String, refreshToken: String) { | ||
| summaryStore.edit { preferences -> | ||
| preferences[ACCESS_TOKEN_KEY] = accessToken | ||
| preferences[REFRESH_TOKEN_KEY] = refreshToken | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 액세스 토큰을 조회합니다 | ||
| */ | ||
| fun getAccessToken(): Flow<String> = summaryStore.data.map { | ||
| it[ACCESS_TOKEN_KEY] ?: "" | ||
| } | ||
|
|
||
| /** | ||
| * 리프레시 토큰을 조회합니다 | ||
| */ | ||
| fun getRefreshToken(): Flow<String> = summaryStore.data.map { | ||
| it[REFRESH_TOKEN_KEY] ?: "" | ||
| } | ||
|
|
||
| /** | ||
| * 토큰 정보 데이터 클래스 | ||
| */ | ||
| data class TokenInfo( | ||
| val accessToken: String, | ||
| val refreshToken: String, | ||
| ) { | ||
| val isTokensAvailable: Boolean | ||
| get() = accessToken.isNotEmpty() && refreshToken.isNotEmpty() | ||
| } | ||
|
|
||
| /** | ||
| * 모든 토큰 정보를 한번에 조회합니다 | ||
| */ | ||
| fun getTokenInfo(): Flow<TokenInfo> = summaryStore.data.map { preferences -> | ||
| TokenInfo( | ||
| accessToken = preferences[ACCESS_TOKEN_KEY] ?: "", | ||
| refreshToken = preferences[REFRESH_TOKEN_KEY] ?: "" | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * 토큰 정보를 초기화합니다 | ||
| */ | ||
| suspend fun clearTokens() { | ||
| summaryStore.edit { preferences -> | ||
| preferences.remove(ACCESS_TOKEN_KEY) | ||
| preferences.remove(REFRESH_TOKEN_KEY) | ||
| } | ||
| } | ||
|
|
||
| suspend fun clearAllData() { | ||
| summaryStore.edit { it.clear() } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.paw.key.core.util | ||
|
|
||
| import kotlinx.coroutines.TimeoutCancellationException | ||
| import kotlinx.coroutines.ensureActive | ||
| import kotlin.coroutines.cancellation.CancellationException | ||
| import kotlin.coroutines.coroutineContext | ||
|
|
||
| suspend fun <R> suspendRunCatching(block: suspend () -> R): Result<R> { | ||
| return try { | ||
| Result.success(block()) | ||
| } catch (t: TimeoutCancellationException) { | ||
| Result.failure(t) | ||
| } catch (c: CancellationException) { | ||
| throw c | ||
| } catch (e: Throwable) { | ||
| coroutineContext.ensureActive() | ||
| Result.failure(e) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.paw.key.data | ||
|
|
||
| import android.content.Context | ||
| import androidx.credentials.CredentialManager | ||
| import androidx.credentials.GetCredentialRequest | ||
| import com.google.android.libraries.identity.googleid.GetGoogleIdOption | ||
| import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential | ||
| import com.paw.key.BuildConfig | ||
| import com.paw.key.core.util.suspendRunCatching | ||
| import com.paw.key.data.dto.request.LoginRequestDto | ||
| import com.paw.key.data.dto.response.BaseResponse | ||
| import com.paw.key.data.dto.response.LoginResponseDto | ||
| import com.paw.key.data.remote.datasource.login.AuthRemoteDataSource | ||
| import com.paw.key.data.remote.datasource.login.GoogleAuthDataSource | ||
| import com.paw.key.data.service.login.LoginService | ||
| import javax.inject.Inject | ||
|
|
||
| class GoogleAuthDataSourceImpl @Inject constructor( | ||
| private val credentialManager: CredentialManager, | ||
| ) : GoogleAuthDataSource { | ||
| override suspend fun signIn(context: Context): Result<GoogleIdTokenCredential> = | ||
| suspendRunCatching { | ||
| val googleIdOption = GetGoogleIdOption.Builder() | ||
| .setFilterByAuthorizedAccounts(false) | ||
| .setAutoSelectEnabled(false) | ||
| .setServerClientId(BuildConfig.GOOGLE_WEB_CLIENT_ID) | ||
| .build() | ||
|
|
||
| val request = GetCredentialRequest.Builder() | ||
| .addCredentialOption(googleIdOption) | ||
| .build() | ||
|
|
||
| val response = credentialManager.getCredential(context, request) | ||
| GoogleIdTokenCredential.createFrom(response.credential.data) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| class AuthRemoteDataSourceImpl @Inject constructor( | ||
|
||
| private val loginService: LoginService, | ||
| ) : AuthRemoteDataSource { | ||
| override suspend fun login( | ||
| providerToken: String, | ||
| provider: String, | ||
| ): BaseResponse<LoginResponseDto> = | ||
| loginService.login(providerToken, LoginRequestDto(provider)) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.paw.key.data.dto.request | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class LoginRequestDto ( | ||
| @SerialName("email") | ||
| val email: String, | ||
| ) | ||
| // 테스트용입니다 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.paw.key.data.dto.response | ||
|
|
||
| import kotlinx.serialization.SerialName | ||
| import kotlinx.serialization.Serializable | ||
|
|
||
| @Serializable | ||
| data class LoginResponseDto ( | ||
| @SerialName("AccessToken") | ||
| val AccessToken: String, | ||
| @SerialName("RefreshToken") | ||
| val RefreshToken: String | ||
| ) | ||
| // 테스트용입니다 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,12 @@ | ||
| package com.paw.key.data.remote.datasource.home | ||
|
|
||
| import com.paw.key.data.dto.request.home.HomeRegionRequest | ||
| import com.paw.key.data.service.home.HomeRegionService | ||
| import com.paw.key.data.service.home.RegionCurrentService | ||
| import javax.inject.Inject | ||
|
|
||
| class RegionCurrentDataSource @Inject constructor( | ||
| private val service: RegionCurrentService | ||
| private val service: HomeRegionService | ||
| ) { | ||
| suspend fun RegionCurrent(userId: Int) = | ||
| service.RegionCurrent(userId) | ||
| suspend fun regionCurrent(userId: Int) = | ||
| service.regionCurrent(userId) | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.paw.key.data.remote.datasource.login | ||
|
|
||
| import android.content.Context | ||
| import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential | ||
| import com.paw.key.data.dto.response.BaseResponse | ||
| import com.paw.key.data.dto.response.LoginResponseDto | ||
|
|
||
| interface GoogleAuthDataSource { | ||
| suspend fun signIn(context: Context): Result<GoogleIdTokenCredential> | ||
| } | ||
|
|
||
| interface AuthRemoteDataSource { | ||
| suspend fun login(providerToken: String, provider: String): BaseResponse<LoginResponseDto> | ||
| } | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,9 +9,9 @@ class RegionCurrentRepositoryImpl @Inject constructor( | |
| private val dataSource: RegionCurrentDataSource, | ||
| ) : RegionCurrentRepository { | ||
|
|
||
| override suspend fun RegionCurrent(userId: Int): Result<RegionCurrentDataEntity> { | ||
| override suspend fun regionCurrent(userId: Int): Result<RegionCurrentDataEntity> { | ||
| return runCatching { | ||
| val response = dataSource.RegionCurrent(userId) | ||
| val response = dataSource.regionCurrent(userId) | ||
| if (response.code == "S000") { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 어허 response.code 금지, 코드로 확인하지말고 result의 onSuccess나 onFailure로 판단합시다!
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 맞다 |
||
| // 올바른 타입 반환 (RegionCurrentDataEntity) | ||
| response.data.toEntity() // DTO에서 Entity로 변환 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.paw.key.data.repositoryimpl.login | ||
|
|
||
| import android.content.Context | ||
| import com.paw.key.core.util.PreferenceDataStore | ||
| import com.paw.key.core.util.suspendRunCatching | ||
| import com.paw.key.data.dto.response.LoginResponseDto | ||
| import com.paw.key.data.remote.datasource.login.AuthRemoteDataSource | ||
| import com.paw.key.data.remote.datasource.login.GoogleAuthDataSource | ||
| import com.paw.key.domain.repository.login.AuthRepository | ||
| import javax.inject.Inject | ||
|
|
||
| class AuthRepositoryImpl @Inject constructor( | ||
| private val authRemoteDataSource: AuthRemoteDataSource, | ||
| private val googleAuthDataSource: GoogleAuthDataSource, | ||
| ) : AuthRepository { | ||
| override suspend fun signInWithGoogle(context: Context): Result<String> = | ||
| googleAuthDataSource.signIn(context).map { it.idToken } | ||
|
|
||
| override suspend fun login(providerToken: String, provider: String): Result<LoginResponseDto> = | ||
| suspendRunCatching { | ||
| val loginResponse = authRemoteDataSource.login(providerToken, provider).data | ||
| PreferenceDataStore.saveTokens( | ||
| accessToken = loginResponse.AccessToken, | ||
| refreshToken = loginResponse.RefreshToken | ||
| ) | ||
| loginResponse | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍