카카오톡앱 로그인 -> 뒤로가기 오류

문의 시 사용하시는 SDK 버전 정보를 알려주세요.

sdk 버전 : implementation “com.kakao.sdk:v2-user:2.12.0” // 카카오 로그인

카카오톡으로 로그인을 선택하고 하단 소프트키로 뒤로가기를 했을 때
TalkAuthCodeActivity가 finish 되고 키 이벤트가 소비되지 않고 fragment 까지 전달되서
fragment에 정의된 뒤로가기가 수행되는 상황입니다.

  • back event 한번 → )TalkAuthCodeActivity finish → fragment back
  • 카카오톡으로 로그인 화면에 "취소"를 누르면 정상동작

카카오톡 로그인을 아래와 같이 사용중입니다.
sdk를 잘못사용하고 있는것인지 sdk 오류 인지 문의드립니다.

코드

    private suspend fun handleKakaoLogin(): Boolean =
        suspendCoroutine<Boolean> { continuation ->
            val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
                if (error != null) {
                    Timber.e("카카오계정으로 로그인 실패 $error")

                    if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
                        //test
                        continuation.resume(false)
                    } else {
                        continuation.resume(false)
                    }
                } else if (token != null) {
                    Timber.i("카카오계정으로 로그인 성공 ${token.accessToken}")
                    UserApiClient.instance.me { user, error ->
                        if (error != null) {
                            Timber.e("사용자 정보 요청 실패 $error")
                            continuation.resume(false)
                        } else if (user != null) {
                            Timber.e("사용자 정보 요청 성공 : $user")
                            viewModel.postSnsLogin("KK", user.id.toString())
                            continuation.resume(true)
                        }
                    }
                }
            }

            UserApiClient.instance.let { kakao ->
                kakao.me { user, error ->
                    if (error != null) {
                        Timber.e("사용자 정보 요청 실패 $error")
                        if (kakao.isKakaoTalkLoginAvailable(requireActivity())) {
                            **kakao.loginWithKakaoTalk(requireActivity(), callback = callback)**
                        } else {
                            kakao.loginWithKakaoAccount(requireActivity(), callback = callback)
                        }
                    } else {
                        user?.let {
                            viewModel.postSnsLogin("KK", it.id.toString())
                            continuation.resume(true)
                        }
                    }
                }
            }
        }

로그

2477-6019 InputManager-JNI pid-2477 W Input channel object ‘cb2a454 com.kyobo.nalcee.dev/com.kakao.sdk.auth.TalkAuthCodeActivity (client)’ was disposed without first being removed with the input manager!
2477-2636 WindowManager pid-2477 I Reparenting to original parent: Surface(name=Task=12708)/@0x62258c9, destroy=true, surface=Surface(name=ActivityRecord{efa29cb u0 com.kakao.talk/com.kakao.sdk.CapriLoggedInActivity})/@0xa0e31bd
2477-2636 WindowManager pid-2477 V Setting visibility of Window{88cdae4 u0 com.kakao.talk/com.kakao.sdk.CapriLoggedInActivity}: false, caller=com.android.server.wm.WindowContainer.sendAppVisibilityToClients:1236 com.android.server.wm.WindowToken.setClientVisible:431 com.android.server.wm.ActivityRecord.setClientVisible:7587 com.android.server.wm.ActivityRecord.onAnimationFinished:8445 com.android.server.wm.WindowContainer$$ExternalSyntheticLambda4.onAnimationFinished:2
2477-2636 WindowManager pid-2477 I Reparenting to original parent: Surface(name=Task=12708)/@0x62258c9, destroy=true, surface=Surface(name=ActivityRecord{dce6a24 u0 com.kyobo.nalcee.dev/com.kakao.sdk.auth.TalkAuthCodeActivity})/@0xce8c367
2477-2636 WindowManager pid-2477 V Setting visibility of Window{cb2a454 u0 com.kyobo.nalcee.dev/com.kakao.sdk.auth.TalkAuthCodeActivity EXITING}: false, caller=com.android.server.wm.WindowContainer.sendAppVisibilityToClients:1236 com.android.server.wm.WindowToken.setClientVisible:431 com.android.server.wm.ActivityRecord.setClientVisible:7587 com.android.server.wm.ActivityRecord.onAnimationFinished:8445 com.android.server.wm.WindowContainer$$ExternalSyntheticLambda4.onAnimationFinished:2
2477-2636 WindowManager pid-2477 E win=Window{cb2a454 u0 com.kyobo.nalcee.dev/com.kakao.sdk.auth.TalkAuthCodeActivity EXITING} destroySurfaces: appStopped=false cleanupOnResume=false win.mWindowRemovalAllowed=true win.mRemoveOnExit=true win.mViewVisibility=0 caller=com.android.server.wm.ActivityRecord.destroySurfaces:6443 com.android.server.wm.ActivityRecord.destroySurfaces:6424 com.android.server.wm.WindowState.onExitAnimationDone:5915 com.android.server.wm.ActivityRecord$$ExternalSyntheticLambda11.accept:2 java.util.ArrayList.forEach:1262 com.android.server.wm.ActivityRecord.onAnimationFinished:8462 com.android.server.wm.WindowContainer$$ExternalSyntheticLambda4.onAnimationFinished:2
2477-2636 WindowManager pid-2477 I Destroying surface Surface(name=com.kyobo.nalcee.dev/com.kakao.sdk.auth.TalkAuthCodeActivity$_28751)/@0xffa50b6 called by com.android.server.wm.WindowStateAnimator.destroySurface:907 com.android.server.wm.WindowStateAnimator.destroySurfaceLocked:519 com.android.server.wm.WindowState.destroySurfaceUnchecked:4253 com.android.server.wm.WindowState.destroySurface:4227 com.android.server.wm.ActivityRecord.destroySurfaces:6443 com.android.server.wm.ActivityRecord.destroySurfaces:6424 com.android.server.wm.WindowState.onExitAnimationDone:5915 com.android.server.wm.ActivityRecord$$ExternalSyntheticLambda11.accept:2
1808-1808 Layer pid-1808 I id=208451 removedFromDrawingState com.kyobo.nalcee.dev/com.kakao.sdk.auth.TalkAuthCodeActivity$_28751#208451 (122)

안녕하세요.

코루틴을 사용하며 발생하는 사이드 이팩트 같은데요
com.kakao.sdk.auth.AuthCodeHandlerActivity 가 선언된 manifest 내용을 보여주실수 있을까요?

<application
    android:name=".temp"
    android:allowBackup="true"
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:enableOnBackInvokedCallback="true"
    android:fullBackupContent="@xml/backup_rules"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Aos">
    <activity
        android:name=".ui.MainActivity"
        android:exported="true"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        android:theme="@style/AppTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <meta-data
            android:name="android.app.lib_name"
            android:value="" />

        <!-- This is added to handle a deep link -->
        <nav-graph android:value="@navigation/nav_graph" />
    </activity>


    <activity
        android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />

            <!-- Redirect URI: "kakao${NATIVE_APP_KEY}://oauth" -->
            <data android:host="oauth"
                android:scheme="@string/kakao_oauth_host" />
        </intent-filter>
    </activity>

입니다.

AuthCodeHandlerActivity 에 android:launchMode=“singleTask” 추가 설정해도 동일하실까요?

네 동일합니다.
launchMode 추가 여부와 상관없이 100% 오류가 발생하는 것이 아닌, 간헐적으로 뒤로가기가 정상동작 (AuthCodeHandlerActivity만 닫히는) 하는 상황입니다.

안녕하세요

테스트해봤을 때는 말씀해주신 이슈가 재현되지 않아서 정확하게 파악하지 못한 상황입니다

첨부해주신 handleKakaoLogin()를 어떻게 호출하는지 호출부 코드와 앱 id (디벨로퍼스에서 확인할 수 있는 숫자 값)도 같이 첨부 부탁드립니다

그리고 카카오톡 로그인을 선택하고 뒤로가기를 한다고 말씀하신 내용을 제대로 이해하지 못했습니다. 최초 가입 시 표시되는 동의창 화면에서 뒤로가기를 했다는건지, 아니면 카카오톡으로 로그인하기를 누르고 잠깐의 딜레이가 있을 때 뒤로가기를 했다는건지 정확하게 이해하기가 어렵네요. 가능하시다면 이슈 재현 동영상도 첨부해주시면 파악하는데 큰 도움이 될 것 같습니다.

카카오로그인.mp4.zip (976.0 KB)

Fragment에서 compose를 활용해 ui를 구성하고 있습니다.
카카오 로그인 버튼을 누르면 KakaoLoginManager 에서 로그인을 처리하도록 구현했습니다.
구글/네이버/애플/카카오 로그인을 제공하고 있으며 카카오 로그인 시에만
바텀 네비게이션 버튼으로 뒤로가기를 할때 이슈가 있는 상태입니다.
앱 아이디 : ID 654379

재현 동영상을 zip 파일로 압축해 첨부드립니다.

@AndroidEntryPoint
class LoginFragment : BaseFragment<FragmentLoginBinding>(R.layout.fragment_login) {

....
    override fun init(view: View) {
        binding.composeView.apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                LoginPage(
                    viewModel,
                    navigatePop = { navigatePop() },
                    requestSnsLogin = { requestSnsLogin(it) })
            }
        }
    }
    private fun requestSnsLogin(joinPath: String) {
        CoroutineScope(Dispatchers.Main).launch {
            when (joinPath) {
                "NV" -> {
                    naverLoginManager.login { id: String ->
                        viewModel.postSnsLogin(joinPath, id)
                    }
                }
                "KK" -> {
                    kakaoLoginManager.login { id: String ->
                        viewModel.postSnsLogin(joinPath, id)
                    }
                }
                "AP" -> {
                    appleLoginManager.login { id: String ->
                        viewModel.postSnsLogin(joinPath, id)
                    }
                }
                "GG" -> {
                    handleGoogleLogin()
                }
            }
        }
    }
class KakaoLoginManager @Inject constructor(
    @ActivityContext private val context : Context
) : OauthLogin {

    override fun login(postSnsLogin : (String) -> Unit) {
        val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
            if (error != null) {
                Timber.e("카카오계정으로 로그인 실패 $error")
            } else if (token != null) {
                Timber.i("카카오계정으로 로그인 성공 ${token.accessToken}")
                UserApiClient.instance.me { user, error ->
                    if (error != null) {
                        Timber.e("사용자 정보 요청 실패 $error")
                    } else if (user != null) {
                        Timber.e("사용자 정보 요청 성공 : $user")
                        postSnsLogin(user.id.toString())
                    }
                }
            }
        }

        UserApiClient.instance.let { kakao ->
            kakao.me { user, error ->
                if (error != null) {
                    Timber.e("사용자 정보 요청 실패 $error")
                    if (kakao.isKakaoTalkLoginAvailable(context)) {
                        kakao.loginWithKakaoTalk(context, callback = callback)
                    } else {
                        kakao.loginWithKakaoAccount(context, callback = callback)
                    }
                } else {
                    user?.let {
                        postSnsLogin(user.id.toString())
                    }
                }
            }
        }
    }

첨부해주신 코드와 같이 부분적으로 compose를 도입하는 등 최대한 비슷한 환경을 구성해봐도 이슈가 재현이 되지 않아서 원인 파악이 되지 않고 있습니다.

구현하고 계신 프로젝트의 정확한 구현 상황을 알지 못해서 확인하는데 한계가 있네요ㅠㅠ

조금 번거로우시겠지만 간단한 샘플 프로젝트를 새로 만들고 로그인 부분을 비슷하게 구현해서 확인해주실 수 있을까요?

샘플 프로젝트에서도 이슈가 재현된다면 저에게 개인메시지로 프로젝트 전달해주시면 정말 감사하겠습니다