From c773d6dbc9948cd6385eb744c9e8d40d8b88600f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=BC=ED=98=81?= Date: Sun, 27 Sep 2026 22:58:41 +0900 Subject: [PATCH] =?UTF-8?q?refactor(core,setting):=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EC=95=A1=EC=84=B8=EC=8A=A4=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=ED=97=AC=ED=8D=BC=EB=A5=BC=20core:common=20=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=98=AC=EB=A6=B0=EB=8B=A4=20(#2194)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 설정 KakaoLinkHelper.kt 의 requestKakaoAccessToken 을 core:common auth 패키지로 옮기고 동작·예외 문구·취소 판정(CoreAuthFailure.UserCancelledAuth)은 그대로 둔다. 연결된 계정 화면은 import 만 core 로 바꾸고, 설정 모듈에서 쓰지 않게 된 카카오 SDK 의존을 걷는다. --- core/common/build.gradle.kts | 3 +++ .../core/common/auth/KakaoAccessToken.kt | 22 +++++++++++++++++-- feature/setting/presentation/build.gradle.kts | 2 -- .../account/ConnectedAccountsScreen.kt | 2 +- 4 files changed, 24 insertions(+), 5 deletions(-) rename feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/social/KakaoLinkHelper.kt => core/common/src/main/kotlin/com/afternote/core/common/auth/KakaoAccessToken.kt (58%) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index a22788079..ac0f99183 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -10,7 +10,10 @@ android { } dependencies { + implementation(projects.core.domain) implementation(libs.androidx.work.runtime.ktx) + implementation(libs.kakao.sdk.auth) + implementation(libs.kakao.sdk.user) // BiometricCryptoGate 가 BiometricPrompt.CryptoObject 를 공개 시그니처로 돌려주므로 // 소비 모듈의 컴파일 클래스패스에도 올라가야 한다 — implementation 이면 반환 타입을 못 본다. diff --git a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/social/KakaoLinkHelper.kt b/core/common/src/main/kotlin/com/afternote/core/common/auth/KakaoAccessToken.kt similarity index 58% rename from feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/social/KakaoLinkHelper.kt rename to core/common/src/main/kotlin/com/afternote/core/common/auth/KakaoAccessToken.kt index 7a4335826..facdc58dc 100644 --- a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/social/KakaoLinkHelper.kt +++ b/core/common/src/main/kotlin/com/afternote/core/common/auth/KakaoAccessToken.kt @@ -1,4 +1,4 @@ -package com.afternote.feature.setting.presentation.account.social +package com.afternote.core.common.auth import android.app.Activity import com.afternote.core.domain.error.CoreAuthFailure @@ -9,8 +9,22 @@ import com.kakao.sdk.user.UserApiClient import kotlinx.coroutines.suspendCancellableCoroutine import kotlin.coroutines.resume -suspend fun requestKakaoAccessToken(activity: Activity): Result = +/** + * 카카오 SDK로부터 OAuth 액세스 토큰을 획득한다. + * + * 플랫폼 SDK가 [Activity] 컨텍스트를 요구하므로 이 함수는 UI 레이어(화면)에서 호출한다. + * Data 레이어(Repository)에는 획득한 액세스 토큰 문자열만 전달되어, + * Data 레이어가 UI 컴포넌트(Activity)에 의존하지 않도록 한다. + * 카카오 로그인(온보딩)과 카카오 계정 연결(설정)이 같은 흐름을 쓰므로 feature 가 아닌 core 에 한 벌을 둔다 (#2194). + * + * 디바이스에 카카오톡이 설치된 경우 앱 로그인을, 그 외에는 웹 계정 로그인을 수행하며, + * 앱 로그인이 비정상 종료된 경우에는 웹 계정 로그인으로 폴백한다. + * 사용자가 로그인을 취소하면 폴백 없이 [CoreAuthFailure.UserCancelledAuth] 로 실패한다. + * 콜백 기반 SDK API를 [suspendCancellableCoroutine]으로 감싸 코루틴 흐름으로 변환한다. + */ +public suspend fun requestKakaoAccessToken(activity: Activity): Result = suspendCancellableCoroutine { continuation -> + // 공통 콜백: 카카오톡 앱 로그인·웹 계정 로그인 결과를 모두 처리한다. val callback: (OAuthToken?, Throwable?) -> Unit = { token, error -> if (continuation.isActive) { when { @@ -36,20 +50,24 @@ suspend fun requestKakaoAccessToken(activity: Activity): Result = } if (UserApiClient.instance.isKakaoTalkLoginAvailable(activity)) { + // 카카오톡 앱이 설치된 경우: 앱 로그인 시도 UserApiClient.instance.loginWithKakaoTalk(activity) { token, error -> if (error != null) { + // 사용자가 의도적으로 취소한 경우에는 폴백 없이 즉시 실패 처리 if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { if (continuation.isActive) { continuation.resume(Result.failure(CoreAuthFailure.UserCancelledAuth())) } return@loginWithKakaoTalk } + // 그 외 에러(네트워크·앱 오류 등)는 웹 계정 로그인으로 폴백 UserApiClient.instance.loginWithKakaoAccount(activity, callback = callback) } else { callback(token, null) } } } else { + // 카카오톡 앱이 없는 경우: 곧바로 웹 계정 로그인 UserApiClient.instance.loginWithKakaoAccount(activity, callback = callback) } } diff --git a/feature/setting/presentation/build.gradle.kts b/feature/setting/presentation/build.gradle.kts index 4eb2c8004..abc770b98 100644 --- a/feature/setting/presentation/build.gradle.kts +++ b/feature/setting/presentation/build.gradle.kts @@ -33,8 +33,6 @@ dependencies { implementation(projects.core.ui) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.compose.runtime) - implementation(libs.kakao.sdk.auth) - implementation(libs.kakao.sdk.user) implementation(libs.androidx.biometric) implementation(libs.androidx.credentials) implementation(libs.androidx.credentials.play.services.auth) diff --git a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/ConnectedAccountsScreen.kt b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/ConnectedAccountsScreen.kt index 6e6d6295f..38eafcf3a 100644 --- a/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/ConnectedAccountsScreen.kt +++ b/feature/setting/presentation/src/main/kotlin/com/afternote/feature/setting/presentation/account/ConnectedAccountsScreen.kt @@ -12,13 +12,13 @@ import androidx.compose.ui.res.stringResource import androidx.credentials.CredentialManager import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.afternote.core.common.auth.requestKakaoAccessToken import com.afternote.core.domain.error.CoreAuthFailure import com.afternote.core.ui.findActivity import com.afternote.feature.setting.presentation.BuildConfig import com.afternote.feature.setting.presentation.R import com.afternote.feature.setting.presentation.account.social.KakaoAuthResult import com.afternote.feature.setting.presentation.account.social.requestGoogleIdToken -import com.afternote.feature.setting.presentation.account.social.requestKakaoAccessToken import com.afternote.feature.setting.presentation.account.social.toKakaoAuthResult @Composable