Ios에서 로그인 콜백이 두 번 호출됩니다

안녕하세요 Kakao 로그인 기능을 연동하고자 하는 iOS 개발자입니다.
SDK는 2.14.0을 사용하고 있습니다.

아래와 같이 SDK를 이용하여 로그인 하는 부분을 async, await을 사용하기 위해 감싸서 사용하고 있는데,
Callback이 두번 호출되는 문제가 있습니다.

private func kakaologin() async throws {
    print("aaa \(#function)")
    return try await withCheckedThrowingContinuation { continuation in
        if UserApi.isKakaoTalkLoginAvailable() {
            UserApi.shared.loginWithKakaoTalk { token, error in
                print("aaa callback")
                if let error {
                    print("aaa error1 \(error.localizedDescription)")
                    continuation.resume(throwing: error)
                    return
                } else if token == nil {
                    print("aaa error2")
                    continuation.resume(throwing: NSError(
                        domain: "kakao auth token is nil", code: -1))
                    return
                } else {
                    print("aaa success")
                    continuation.resume(returning: ())
                    return
                }
            }
        } else {
            UserApi.shared.loginWithKakaoAccount { token, error in
                if let error = error {
                    continuation.resume(throwing: error)
                } else if token == nil {
                    continuation.resume(throwing: NSError(
                        domain: "kakao auth token is nil", code: -1))
                } else {
                    continuation.resume(returning: ())
                }
            }
        }
    }
}

로그를 찍어보면 아래와 같이 나옵니다.

aaa kakaologin()
aaa callback
aaa success
aaa callback
aaa error1

제가 어떤 부분에서 SDK를 잘못 사용하고 있는건지 문의드립니다…

안녕하세요.

로그인 중 로그도 전부 제공 가능하실까요?

aaa kakaologin()
aaa callback
aaa success
aaa callback
aaa error1 The operation couldn’t be completed. (KakaoSDKCommon.SdkError error 2.)
_Concurrency/CheckedContinuation.swift:187: Fatal error: SWIFT TASK CONTINUATION MISUSE: kakaologin() tried to resume its continuation more than once, throwing AuthFailed(reason: KakaoSDKCommon.AuthFailureReason.InvalidGrant, errorInfo: Optional(KakaoSDKCommon.AuthErrorInfo(error: KakaoSDKCommon.AuthFailureReason.InvalidGrant, errorDescription: Optional("authorization code not found for code=(보안상 삭제)"))))!
2023-05-17 12:16:34.646984+0900 Metacellar[1073:156657] _Concurrency/CheckedContinuation.swift:187: Fatal error: SWIFT TASK CONTINUATION MISUSE: kakaologin() tried to resume its continuation more than once, throwing AuthFailed(reason: KakaoSDKCommon.AuthFailureReason.InvalidGrant, errorInfo: Optional(KakaoSDKCommon.AuthErrorInfo(error: KakaoSDKCommon.AuthFailureReason.InvalidGrant, errorDescription: Optional("authorization code not found for code=(보안상 삭제)"))))!

에러 로그를 보고 싶으신 거죠? 위의 코드가 관련된 에러 로그입니다. InvalidGrant라네요…
아래는 callback이 2번와서 continuation이 두 번 호출되서 문제가 된 부분입니다.
로그 중 보안상 삭제라고 되어있는 곳은 중요정보같아서 삭제 했습니다. 이 부분도 필요하신 부분일까요?

제공해 주신 로그 전에 출력된 내용을 좀 더 부탁드립니다.

@woody.ho 도와주셔서 감사합니다 문제를 찾은거 같습니다.

문제는 URL을 받아 kakao 로그인을 처리하는 부분이 여러개였던게 문제였던 것으로 보입니다.

프로젝트를 UIKit에서 SwiftUI에서 전환하는 부분에서 실수가 있었던 것으로 보입니다.

수신을 여러 군데서 하니 콜백이 여러번 호출됬던 것으로 보입니다.

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    //카카오 로그인
    if let url = URLContexts.first?.url {
        if(AuthApi.isKakaoTalkLoginUrl(url)) {
            _ = AuthController.handleOpenUrl(url: url)
        } else {
            //네이버 로그인
            NaverThirdPartyLoginConnection.getSharedInstance().receiveAccessToken(URLContexts.first?.url)
        }
    }
    
}
func application(_ app: UIApplication, open url: URL,
                 options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool
{
    // 카카오 로그인
    if AuthApi.isKakaoTalkLoginUrl(url) {
        return AuthController.handleOpenUrl(url: url)
    }
    
    // 네이버 로그인
    NaverThirdPartyLoginConnection.getSharedInstance()?.application(
        app, open: url, options: options)
    
    return false
}
var body: some Scene {
    WindowGroup {
        ContentView()
            .onOpenURL {
                guard AuthApi.isKakaoTalkLoginUrl($0) == false else {
                    _ = AuthController.handleOpenUrl(url: $0)
                    return
                }
                
                appStatus.incomingURL = $0
            }
    }
}

네이버 로그인을 같이 사용하셨군요

네이버 로그인에서 카카오 로그인 url을 처리해서 발생하신것 같습니다.
AppDelegate.swift 에서 url에 kakao 가 포함되어 있는지 여부에 따라 분기 처리하시면 좋을것 같습니다.

예)

override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    var result = false

   if url.absoluteString.hasPrefix("kakao") {
       result = super.application(app, open: url, options: options)
   }

   if !result {
       result = NaverThirdPartyLoginConnection.getSharedInstance()?.application(app, open: url, options: options)
   }
   return result
}

@woody.ho 님 말씀을 100퍼센트 이해하지 못했습니다.
나머지 AppDelegate, SceneDelegate 로직들은 모두 삭제할 생각이고,
SwiftUI App Code만 남기려고 하는데 아래의 코드가 말씀하신 것과 같은 방법인가요?
SwiftUI에서 사용하는데 추가적으로 처리해야할 것이 있을끼요?

@main
struct MetacellarApp: App {
...

    init() {
        Kakao.shared.initSDK(key: Configuration.shared.environment.kakaoSDKKey)
        Naver.shared.initSDK(key: Configuration.shared.environment.naverConsumerKey,
                             secretKey: Configuration.shared.environment.naverConsumerSecret)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL {
                   // kakao login url 처리
                    if AuthApi.isKakaoTalkLoginUrl($0) {
                        _ = AuthController.handleOpenUrl(url: $0)
                        return
                    }
                    
                   // naver login url 처리
                   if $0.isNaverLoginUrl() {
                        NaverThirdPartyLoginConnection.getSharedInstance()?.application(
                            app, open: url, options: options)
                        return
                   }
                    
                    // firebase dynamic link 처리
                    appStatus.incomingURL = $0
                }
        }
    }
}

넵, 맞습니다.

AuthApi.isKakaoTalkLoginUrl 사용하시면 됩니다.

넵넵 감사합니다~