문서 목록
ENGINEERING

KakaoTalk 로그인 ⋅ 실패 처리 코드의 수정 전후

기본 login()이 KakaoTalk 경로를 선택했을 때 오류가 즉시 거절되던 코드와 계정 로그인으로 이어지도록 고친 Android·iOS 코드를 비교한다.

·5 min read
수정 전후의 KakaoTalk 오류 처리와 계정 로그인 재시도 분기를 설명한다. 음성: Gemini 3.8 Flash Lite TTS, Kore · 2026-10-04.

as-is · 수정 전: KakaoTalk 오류가 곧바로 거절됐다

isKakaoTalkLoginAvailable()은 앱 사용 가능 여부를 확인하며 KakaoTalk 계정의 로그인 상태를 보장하지 않는다. KakaoTalk이 설치됐지만 로그아웃된 기기에서 기본 login()이 KakaoTalk 경로를 선택한 뒤 오류로 끝난 사례가 이슈 #77에 보고됐다.

Android · 수정 전. 여기서 공통 callback은 로그인 결과를 받아 login()의 Promise를 완료하는 함수다. 수정 전에는 KakaoTalk SDK에도 이 함수를 직접 전달했다. 오류가 오면 promise.rejectWith(error)를 호출했다.

RNCKakaoUserModule.ktKotlin
val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->  if (error != null) {    promise.rejectWith(error)  }  // Other result handling omitted.}UserApiClient.instance.loginWithKakaoTalk(  context,  nonce = nonce,  serviceTerms = serviceTerms?.filterIsInstance<String>()?.ifEmpty { null },  callback = callback,)

수정 전 Android 공통 콜백과 KakaoTalk 호출에서 연결 관계를 확인할 수 있다.

iOS · 수정 전. iOS도 KakaoTalk SDK에 Promise를 완료하는 callback을 직접 건넸다. 오류가 오면 RNCKakaoUtil.reject(reject, error)가 바로 실행됐다.

RNCKakaoUserManager.swiftSwift
let callback = { (token: OAuthToken?, error: Error?) in  if let error {    RNCKakaoUtil.reject(reject, error)  }  // Other result handling omitted.}UserApi.shared  .loginWithKakaoTalk(    serviceTerms: emptyArrayToNil(serviceTerms),    nonce: nonce,    completion: callback  )

수정 전 iOS 공통 콜백과 KakaoTalk 호출을 대조하면 계정 로그인으로 넘어갈 분기가 없었다.

to-be · 수정 후: KakaoTalk 실패를 분류해 계정 로그인으로 넘긴다

Android · 수정 후. KakaoTalk 전용 talkCallback이 SDK 결과를 먼저 받는다. ClientErrorCause.Cancelled이면 공통 콜백으로 오류를 보내 Promise를 거절한다. 다른 오류나 토큰 부재는accountLogin()을 호출한다. 성공한 KakaoTalk 토큰만 공통 콜백으로 보낸다.

accountLogin은 처음 받은 nonce, 변환된 serviceTerms와 prompts를 계정 로그인에 전달한다.

RNCKakaoUserModule.ktKotlin
val accountLogin = {  UserApiClient.instance.loginWithKakaoAccount(    context,    prompts = requestedPrompts,    nonce = nonce,    serviceTerms = requestedServiceTerms,    callback = callback,  )}
RNCKakaoUserModule.ktKotlin
val talkCallback: (OAuthToken?, Throwable?) -> Unit = { token, error ->  if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {    callback(token, error)  } else if (error != null || token == null) {    accountLogin()  } else {    callback(token, error)  }}UserApiClient.instance.loginWithKakaoTalk(  context,  nonce = nonce,  serviceTerms = requestedServiceTerms,  callback = talkCallback,)

계정 로그인 호출을 정의한 뒤 KakaoTalk 분기에서 callback = talkCallback을 넘긴다.

iOS · 수정 후. iOS도 KakaoTalk 전용 콜백이 SDK 결과를 먼저 받는다. 오류가 .ClientFailed(.Cancelled) 또는 .AuthFailed(.AccessDenied)이면 계정 화면을 열지 않고 거절한다. 구현은 AccessDenied전체를 이 경로로 분류한다. 카카오 오류 코드 문서는 access_denied가 동의 취소 외에 연령 제한이나 보호자 동의 실패에도 쓰인다고 설명한다. 이 코드는 실제 사용자 의도를 따로 판별하지 않는다. 나머지 SDK 오류와 토큰 부재는 계정 로그인을 시도한다.

iOS 계정 로그인도 원래 nonce, serviceTerms와 변환된 prompts를 사용한다.

RNCKakaoUserManager.swiftSwift
let accountLogin = {  var _prompts: [Prompt] = []  for p in prompts {    if p == "Login" {      _prompts.append(.Login)    }    if p == "Cert" {      _prompts.append(.Cert)    }    if p == "Create" {      _prompts.append(.Create)    }    if p == "SelectAccount" {      _prompts.append(.SelectAccount)    }  }  UserApi.shared.loginWithKakaoAccount(    prompts: self.emptyArrayToNil(_prompts),    serviceTerms: self.emptyArrayToNil(serviceTerms),    nonce: nonce,    completion: callback  )}
RNCKakaoUserManager.swiftSwift
let talkCallback = { (token: OAuthToken?, error: Error?) in  if let sdkError = error as? SdkError {    switch sdkError {    case .ClientFailed(.Cancelled, _),         .AuthFailed(.AccessDenied, _):      callback(token, error)    default:      accountLogin()    }  } else if error != nil || token == nil {    accountLogin()  } else {    callback(token, error)  }}UserApi.shared.loginWithKakaoTalk(  serviceTerms: emptyArrayToNil(serviceTerms),  nonce: nonce,  completion: talkCallback)

계정 로그인 호출을 정의한 뒤 KakaoTalk 분기에서 completion: talkCallback을 넘긴다.

기본 KakaoTalk 경로에서는 공개 API가 비어 있지 않은 prompts를 사전에 거절한다. 직접 계정 로그인에서는 이 옵션을 사용한다.

즉시 거절 대상으로 분류한 오류 이외의 KakaoTalk 실패에서는 공통 콜백이 아직 실행되지 않는다. 이후 계정 로그인의 콜백이 토큰을 받으면 Promise를 완료하고, 계정 오류를 받으면처음 KakaoTalk 오류 대신 계정 오류로 거절한다. 계정 로그인에서 오류와 토큰이 모두 없으면 토큰 부재 오류로 거절한다.

useKakaoAccountLogin: true와 KakaoTalk 미설치 경로는 처음부터 기존 계정 로그인을 사용한다. 스코프를 지정한 직접 계정 로그인은 별도 경로에 남아 있다. 기본 login()에서 비어 있지 않은 prompts나 scopes는 공개 API의 입력 검사가 네이티브 호출 전에 거절한다.

기본 login()의 KakaoTalk 로그인에서 성공은 토큰 반환, Android Cancelled와 iOS Cancelled·AccessDenied는 계정 로그인 없이 거절, 그 밖의 실패는 원래 nonce와 serviceTerms를 사용한 Kakao Account 로그인으로 이어진다.
변경 후의 분기. 즉시 거절 대상으로 분류한 오류 이외의 실패는 계정 로그인 결과가 올 때까지 Promise를 끝내지 않는다.

병합된 PR #119는 이 콜백 분기를 Android와 iOS에 추가했다fix(user): fall back to account login after KakaoTalk failureMJ Studiomerged생성 수정 병합 KakaoTalk 전용 콜백이 즉시 거절 대상으로 분류한 오류와 나머지 실패를 분리해 계정 로그인을 재시도한다.Android·iOS 모두 같은 nonce와 serviceTerms를 계정 로그인에 전달한다. PR은 병합됐다.. 공개 TypeScript API에는 새 옵션을 추가하지 않았다.

PR #119의 검증 기록fix(user): fall back to account login after KakaoTalk failureMJ Studiomerged생성 수정 병합 에 따르면 yarn lint, yarn typecheck, yarn test와 Android·iOS New Architecture 예제 빌드가 통과했다. 로그인 fallback 전용 합성 E2E 코드는 최종 커밋에서 삭제됐다.

미확인KakaoTalk이 설치됐지만 로그아웃된 실제 기기에서 계정 화면이 열리는지, Kakao 서버가 발급한 OIDC ID 토큰에 원래 nonce가 들어 있는지는 확인하지 않았다.