Skip to content
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

refactor: 카카오 로그인 관련 로직 DataSource 리팩터링 #931

Merged
merged 12 commits into from
Jan 6, 2025
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.mulberry.ody.data.auth.repository

import android.content.Context
import com.mulberry.ody.data.auth.source.local.LocalAuthDataSource
import com.mulberry.ody.data.auth.source.remote.RemoteAuthDataSource
import com.mulberry.ody.domain.apiresult.ApiResult
import com.mulberry.ody.domain.apiresult.getOrNull
import com.mulberry.ody.domain.apiresult.suspendOnSuccess
import com.mulberry.ody.domain.model.AuthToken
import com.mulberry.ody.domain.repository.ody.AuthRepository
import javax.inject.Inject

class KakaoAuthRepository
@Inject
constructor(
private val localAuthDataSource: LocalAuthDataSource,
private val remoteAuthDataSource: RemoteAuthDataSource,
) : AuthRepository {
override suspend fun isLoggedIn(): Boolean {
if (!remoteAuthDataSource.isLoggedIn() || !localAuthDataSource.isLoggedIn()) {
return false
}
val authToken = remoteAuthDataSource.postAuthToken().getOrNull() ?: return false
localAuthDataSource.postAuthToken(authToken)
return true
}

override suspend fun login(context: Context): ApiResult<AuthToken> {
val fcmToken = localAuthDataSource.fetchFCMToken().getOrNull() ?: return ApiResult.Unexpected(Exception("FCM 토큰이 존재하지 않습니다."))
return remoteAuthDataSource.login(fcmToken, context).suspendOnSuccess {
localAuthDataSource.postAuthToken(it)
}
}

override suspend fun logout(): ApiResult<Unit> {
localAuthDataSource.removeAuthToken()
return remoteAuthDataSource.logout()
}

override suspend fun withdrawAccount(): ApiResult<Unit> {
return remoteAuthDataSource.withdraw().suspendOnSuccess {
localAuthDataSource.removeAuthToken()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mulberry.ody.data.auth.source.local

import com.mulberry.ody.data.local.db.OdyDatastore
import com.mulberry.ody.domain.model.AuthToken
import kotlinx.coroutines.flow.first
import javax.inject.Inject

class KakaoLocalAuthDataSource
@Inject
constructor(
private val odyDatastore: OdyDatastore,
) : LocalAuthDataSource {
override suspend fun isLoggedIn(): Boolean {
return odyDatastore.getAuthToken().first().isSuccess
}

override suspend fun removeAuthToken() {
odyDatastore.removeAuthToken()
}

override suspend fun postAuthToken(authToken: AuthToken) {
odyDatastore.setAuthToken(authToken)
}

override suspend fun fetchFCMToken(): Result<String> {
return odyDatastore.getFCMToken().first()
}

override suspend fun postFCMToken(fcmToken: String) = odyDatastore.setFCMToken(fcmToken)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.mulberry.ody.data.auth.source.local

import com.mulberry.ody.domain.model.AuthToken

interface LocalAuthDataSource {
suspend fun isLoggedIn(): Boolean

suspend fun removeAuthToken()

suspend fun postAuthToken(authToken: AuthToken)

suspend fun fetchFCMToken(): Result<String>

suspend fun postFCMToken(fcmToken: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.mulberry.ody.data.auth.source.remote

import android.content.Context
import com.mulberry.ody.data.remote.core.entity.login.mapper.toAuthToken
import com.mulberry.ody.data.remote.core.entity.login.request.LoginRequest
import com.mulberry.ody.data.remote.core.service.AuthService
import com.mulberry.ody.data.remote.core.service.RefreshTokenService
import com.mulberry.ody.data.remote.thirdparty.login.kakao.KakaoOAuthLoginService
import com.mulberry.ody.domain.apiresult.ApiResult
import com.mulberry.ody.domain.apiresult.flatMap
import com.mulberry.ody.domain.apiresult.map
import com.mulberry.ody.domain.apiresult.toApiResult
import com.mulberry.ody.domain.model.AuthToken
import javax.inject.Inject

class KakaoRemoteAuthDataSource
@Inject
constructor(
private val authService: AuthService,
private val refreshTokenService: RefreshTokenService,
private val kakaoOAuthLoginService: KakaoOAuthLoginService,
) : RemoteAuthDataSource {
override suspend fun isLoggedIn(): Boolean {
return kakaoOAuthLoginService.isLoggedIn()
}

override suspend fun postAuthToken(): ApiResult<AuthToken> {
return refreshTokenService.postRefreshToken().map { it.toAuthToken() }
}

override suspend fun login(
fcmToken: String,
context: Context,
): ApiResult<AuthToken> {
val loginResult =
kakaoOAuthLoginService.login(context).toApiResult()
.map { LoginRequest(fcmToken, it.providerId, it.nickname, it.imageUrl) }

return loginResult.flatMap { loginRequest ->
authService.postLoginWithKakao(loginRequest).map { it.toAuthToken() }
}
}

override suspend fun logout(): ApiResult<Unit> {
val kakaoLogoutResult = kakaoOAuthLoginService.logout()
val odyLogoutResult = authService.postLogout()

val exception = kakaoLogoutResult.exceptionOrNull()
if (exception != null) {
return ApiResult.Unexpected(exception)
}

return odyLogoutResult
}

override suspend fun withdraw(): ApiResult<Unit> {
return authService.deleteMember()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.mulberry.ody.data.auth.source.remote

import android.content.Context
import com.mulberry.ody.domain.apiresult.ApiResult
import com.mulberry.ody.domain.model.AuthToken

interface RemoteAuthDataSource {
suspend fun isLoggedIn(): Boolean

suspend fun postAuthToken(): ApiResult<AuthToken>

suspend fun login(
fcmToken: String,
context: Context,
): ApiResult<AuthToken>

suspend fun logout(): ApiResult<Unit>

suspend fun withdraw(): ApiResult<Unit>
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ import com.mulberry.ody.data.remote.core.entity.login.request.LoginRequest
import com.mulberry.ody.data.remote.core.entity.login.response.LoginResponse
import com.mulberry.ody.domain.apiresult.ApiResult
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.POST

interface LoginService {
interface AuthService {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무 좋다고 생각합니다.

@POST("/v1/auth/kakao")
suspend fun postLoginWithKakao(
@Body loginRequest: LoginRequest,
): ApiResult<LoginResponse>

@POST("/v1/auth/logout")
suspend fun postLogout(): ApiResult<Unit>

@DELETE("/members")
suspend fun deleteMember(): ApiResult<Unit>
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.mulberry.ody.data.remote.thirdparty.fcm.service

import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.mulberry.ody.data.local.db.OdyDatastore
import com.mulberry.ody.domain.model.NotificationType
import com.mulberry.ody.domain.repository.ody.FCMTokenRepository
import com.mulberry.ody.presentation.notification.FCMNotification
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.runBlocking
Expand All @@ -12,7 +12,7 @@ import javax.inject.Inject
@AndroidEntryPoint
class FCMService : FirebaseMessagingService() {
@Inject
lateinit var fcmTokenRepository: FCMTokenRepository
lateinit var odyDatastore: OdyDatastore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

자연스러워졌네요!


@Inject
lateinit var fcmNotification: FCMNotification
Expand All @@ -28,7 +28,7 @@ class FCMService : FirebaseMessagingService() {

override fun onNewToken(token: String) {
runBlocking {
fcmTokenRepository.postFCMToken(token)
odyDatastore.setFCMToken(token)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class KakaoOAuthLoginService {
}
}

fun checkIfLoggedIn(): Boolean = TokenManagerProvider.instance.manager.getToken() != null
fun isLoggedIn(): Boolean = TokenManagerProvider.instance.manager.getToken() != null

private suspend fun loginWithKakao(context: Context): Result<AuthToken> =
loginWithKakaoTalk(context).onFailure { error ->
Expand Down
Loading
Loading