목차
티스토리 뷰
안녕하세요🐾
iOS 카카오톡 로그인 SDK 적용 과정을 정리하고자 글을 남깁니다.
다른 SNS 로그인 관련 SDK 적용은 아래의 글을 참고해 주세요!
2023.12.26 - [iOS/Swift] - [iOS] 구글 로그인 SDK 연동하기
2023.12.22 - [iOS/Swift] - [iOS] 페이스북 로그인 SDK 적용
2023.06.21 - [iOS/Swift] - [iOS] 네이버 로그인 SDK 연동하기
카카오톡 SDK는 개발자 페이지가 굉장히 잘 정리되어있고, API에 대한 상세 설명이 관련 문서와 코드 내 주석으로 잘 정리되어 있어 개발자 페이지만 보아도 큰 무리 없이 구현 가능할 정도라는 느낌을 받았고, 실제로 그랬습니다.
최근 사내 배포용을 위한 코코아팟 프레임워크를 만들고 API 문서 페이지를 구현했던 경험이 있던터라, 개발자가 쉽게 이해할 수 있도록 SDK를 제공하려는 개발자 친화적인 요소들에 많은 자극을 받으면서 아직 갈 길이 멀다는 것을 느낍니다. ㅠ_^ 그럼 시작해볼까유
※ 23년 6월 SDK 기준으로 작성된 글 입니다.
1. 설치
카카오톡 로그인 SDK는 라이브러리 의존성이 있어 카카오톡 SDK를 설치하면 아래의 라이브러리가 같이 설치됩니다.
- iOS SDK: Alamofire
- ReactiveX iOS SDK: RxSwift, RxCocoa, RxAlamofire
저는 Cocoapods을 이용하여 설치를 진행하였습니다.
먼저 podfile에 들어갈 아래의 코드를 보실까요?
# 전체 추가
pod 'KakaoSDK'
# or
# 필요한 모듈 추가
pod 'KakaoSDKCommon' # 필수 요소를 담은 공통 모듈
pod 'RxKakaoSDKCommon'
pod 'KakaoSDKAuth' # 사용자 인증
pod 'RxKakaoSDKAuth'
pod 'KakaoSDKUser' # 카카오 로그인, 사용자 관리
pod 'RxKakaoSDKUser'
pod 'KakaoSDKTalk' # 친구, 메시지(카카오톡)
pod 'KakaoSDKStory' # 카카오스토리
pod 'KakaoSDKShare' # 메시지(카카오톡 공유)
pod 'KakaoSDKTemplate' # 메시지 템플릿
pod 'KakaoSDKNavi' # 카카오내비
pod 'KakaoSDKFriend' # 카카오톡 소셜 피커, 리소스 번들 파일 포함
KakaoSDK에서 제공하는 모든 기능을 사용하려면 최상단의 pod KakaoSDK
만 설치하면 되지만,
필요한 모듈만 골라 설치할 경우 하단에 나열된 pod 항목 중 필요한 것만 남기고 나머지는 삭제하시면 됩니다.
저는 이번 글에서 로그인 SDK만 사용할 것이고, RxSwift를 사용 할 때와, Swift만을 사용할 때, 두 가지의 케이스에 대해 모두 테스트 해보기 위해 아래와 같이 필요한 pod만 구성하였습니다.
# 카카오톡 로그인 관련
pod 'KakaoSDKCommon' # 필수 요소를 담은 공통 모듈
pod 'RxKakaoSDKCommon'
pod 'KakaoSDKAuth' # 사용자 인증
pod 'RxKakaoSDKAuth'
pod 'KakaoSDKUser' # 카카오 로그인, 사용자 관리
pod 'RxKakaoSDKUser'
개발자 페이지에 나와있는 모듈간의 의존성은 아래 이미지와 같습니다.
로그인 SDK를 연동하기 위해서는 pod KakaoSDK{Common, Auth, User}
3개의 pod가 필요하다는 것을 알 수 있네요!
마지막으로 pod install
을 통해 설치를 진행해 주세요!
설치가 완료되면 .xworkspace를 오픈합니다.
2. 설정
2.1 애플리케이션 추가
먼저 kakao devlopers에서 로그인 연동에 사용할 애플리케이션을 아래와 같이 추가합니다.
추가된 앱을 선택하면 설정 화면이 나오게 됩니다.
카카오 로그인에서 활성화 설정을 ON으로 바꿔주세요.
OIDC(OpneID Connect)를 사용하려면 이 옵션 또한 활성화 시켜야 합니다.
카카오 로그인 시 얻어올 사용자의 정보에 대한 동의 항목도 아래와 같이 설정해 주세요!
이렇게 설정한 항목들은 아래와 같이 로그인 요청시 동의 요구 항목으로 나타납니다.
2.2 앱 허용
목록 설정
내 앱에서 카카오톡 앱을 실행시키기 위해 info.plist
에 허용 목록을 작성해야 합니다.
아래와 같이 프로퍼티 리스트에서 직접 추가하시거나,
코드로 추가하고 싶다면 info.plist 마우스 우클릭
- open source code
후 아래의 코드를 입력
<key>LSApplicationQueriesSchemes</key>
<array>
<!-- 카카오톡으로 로그인 -->
<string>kakaokompassauth</string>
<!-- 카카오톡 공유 -->
<string>kakaolink</string>
</array>
2.3 URL Schemes 설정
다음으로 URL Schemes에 카카오 로그인을 위한 네이티브 앱 키를 입력해야 합니다.
네이티브 앱 키는 아래와 같이 kakao developers에서 생성한 애플리케이션의 요약 정보 화면에서 확인 할 수 있습니다.
Xcode로 돌아와, 프로젝트 설정 - Info - URL Types - URL Schemes에서 kakao${네이티브앱키}
형식으로 URL Schemes를 넣어줍니다. (ex. 네이티브 앱 키가 1234이면, kakao1234)
그리고 또 하나의 URL Types을 만들고 kakao${네이티브앱키}://oauth
를 URL Schemes에 추가해 주어야 합니다.
(ex. 네이티브 앱 키가 1234이면, kakao1234:ouath)
개발자 문서에 의하면,
첫번째는 URL Schemes는 카카오톡을 실행할 때
두번째는 URL Schemes는 카카오톡 로그인 후 서비스 앱으로 돌아올 때 필요하다고 하네요 :)
2.4 초기화 코드 및 Open URL설정
먼저 KakaoSDK 초기화 코드 추가가 필요합니다.
AppDelegate.swift
에서 아래의 코드를 추가합니다.
주석에 표시한 것 처럼 네이티브 앱 키를 확인하여 넣어주세요!
Swift code:
import KakaoSDKCommon
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
KakaoSDK.initSDK(appKey: "${NATIVE_APP_KEY}")
// NATIVE_APP_KEY가 1234라면,
//KakaoSDK.initSDK(appKey: "1234")
...
}
RxSwift code:
import RxKakaoSDKCommon
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
RxKakaoSDK.initSDK(appKey: "${NATIVE_APP_KEY}")
// NATIVE_APP_KEY가 1234라면,
//RxKakaoSDK.initSDK(appKey: "1234")
...
}
마지막으로 카카오톡에서 프로젝트 앱으로 돌아왔을때 로그인 처리를 완료하기 위해 appDelegate.swift
에서 아래의 코드도 추가합니다.
Swift Code:
import KakaoSDKAuth
...
class AppDelegate: UIResponder, UIApplicationDelegate {
...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
return AuthController.handleOpenUrl(url: url)
}
return false
}
...
}
RxSwift Code:
import RxKakaoSDKAuth
import KakaoSDKAuth
...
class AppDelegate: UIResponder, UIApplicationDelegate {
...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
return AuthController.rx.handleOpenUrl(url: url)
}
return false
}
...
}
만약 Deployment Target이 iOS 13.0 이상 버전이신가요?
iOS 13.0 이후부터 앱의 생명주기 관리는 AppDelegate가, UI의 생명주기는 SceneDelegate에서 관리하도록 변경되었습니다.
따라서 배포 타겟이 13.0 이상인 프로젝트라면 UISceneDelegate.swift가 기본으로 실행되도록 설정되기 때문에 SceneDelegate.swift에 아래의 코드도 같이 입력합니다.
AppDelegate만 사용하도록 SceneDelegate를 제거하였거나, 프로젝트 배포 타겟이 iOS 13.0 이하라면, 아래의 코드는 생략하여도 됩니다.
Swift Code:
import KakaoSDKAuth
...
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
_ = AuthController.handleOpenUrl(url: url)
}
}
}
...
}
RxSwift Code:
import RxKakaoSDKAuth
import KakaoSDKAuth
...
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
if (AuthApi.isKakaoTalkLoginUrl(url)) {
_ = AuthController.rx.handleOpenUrl(url: url)
}
}
}
...
}
3. 카카오톡 로그인
설정이 끝났다면 이제 로그인을 할 일만 남았네요 😎
3.1 로그인 요청
로그인 화면은 위와 같이 간단하게 만들어 보았습니다.
카카오 로그인 버튼을 누르면 로그인 요청을 진행하는 func
블록 내에 아래의 코드를 넣어줍니다.
Swift Code:
// 카카오톡 로그인 버튼을 누르면 실행되는 코드
// 카카오톡 실행 가능 여부 확인
if (UserApi.isKakaoTalkLoginAvailable()) {
UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in
if let error = error {
print(error)
}
else {
print("loginWithKakaoTalk() success.")
//let idToken = oAuthToken.idToken ?? ""
//let accessToken = oAuthToken.accessToken
self.kakaoGetUserInfo()
}
}
}
RxSwift Code:
// Class member property
let disposeBag = DisposeBag()
// 카카오톡 실행 가능 여부 확인
if (UserApi.isKakaoTalkLoginAvailable()) {
UserApi.shared.rx.loginWithKakaoTalk()
.subscribe(onNext:{ (oauthToken) in
print("loginWithKakaoTalk() success.")
//let idToken = oAuthToken.idToken ?? ""
//let accessToken = oAuthToken.accessToken
self.kakaoGetUserInfo()
}, onError: {error in
print(error)
})
.disposed(by: disposeBag)
}
3.2 로그인 정보 가져오기
3.1에서 호출하는 kakaoGetUserInfo()
는 사용자의 정보를 가져오는 코드입니다.
Swift Code:
/// 사용자 정보 가져오기
private func kakaoGetUserInfo() {
UserApi.shared.me() { (user, error) in
if let error = error {
print(error)
}
let userName = user.kakaoAccount?.name
let userEmail = user.kakaoAccount?.email
let userGender = user.kakaoAccount?.gender
let userProfile = user.kakaoAccount?.profile?.profileImageUrl
let userBirthYear = user.kakaoAccount?.birthyear
let contentText =
"user name : \(userName)\n userEmail : \(userEmail)\n userGender : \(userGender), userBirthYear : \(userBirthYear)\n userProfile : \(userProfile)"
print("user - \(user)")
if userEmail == nil {
self.kakaoRequestAgreement()
return
}
self.textField.text = contentText
}
RxSwift Code:
/// 사용자 정보 가져오기
private func kakaoGetUserInfo() {
UserApi.shared.rx.me()
.subscribe (onSuccess:{ user in
print("me() success.")
//do something
let userName = user.kakaoAccount?.name
let userEmail = user.kakaoAccount?.email
let userGender = user.kakaoAccount?.gender
let userProfile = user.kakaoAccount?.profile?.profileImageUrl
let userBirthYear = user.kakaoAccount?.birthyear
let contentText =
"user name : \(userName)\n userEmail : \(userEmail)\n userGender : \(userGender), userBirthYear : \(userBirthYear)\n userProfile : \(userProfile)"
print("user - \(user)")
if userEmail == nil {
self.kakaoRequestAgreement()
return
}
self.textField.text = contentText
}, onFailure: {error in
print(error)
})
.disposed(by: disposeBag)
}
사용자 정보를 가져와 표시하였습니다. 만약 사용자 정보 제공 동의를 하나도 하지 않았다면,
위 코드 중 if userEmail == nil 과 같은 조건문에 걸리게 되겠죠?
해당 function에서 추가 동의를 요구하는 코드가 필요합니다.
3.3 추가 동의 요청하기
동의하지 않은 항목에 대해 추가 동의 요청 항목 화면을 안내하는 코드는 아래와 같습니다.
Swift Code:
/// 추가 항목 동의받기
private func kakaoRequestAgreemenft() {
// 추가 항목 동의 받기(사용자가 동의하지않은 항목에 대한 추가 동의 요청
UserApi.shared.me() { (user, error) in
if let error = error {
print(error)
}
else {
guard let user = user else { return }
var scopes = [String]()
if (user.kakaoAccount?.profileNeedsAgreement == true) { scopes.append("profile") }
if (user.kakaoAccount?.emailNeedsAgreement == true) { scopes.append("account_email") }
if (user.kakaoAccount?.birthdayNeedsAgreement == true) { scopes.append("birthday") }
if (user.kakaoAccount?.birthyearNeedsAgreement == true) { scopes.append("birthyear") }
if (user.kakaoAccount?.genderNeedsAgreement == true) { scopes.append("gender") }
if (user.kakaoAccount?.phoneNumberNeedsAgreement == true) { scopes.append("phone_number") }
if (user.kakaoAccount?.ageRangeNeedsAgreement == true) { scopes.append("age_range") }
if (user.kakaoAccount?.ciNeedsAgreement == true) { scopes.append("account_ci") }
if scopes.count > 0 {
print("사용자에게 추가 동의를 받아야 합니다.")
// OpenID Connect 사용 시
// scope 목록에 "openid" 문자열을 추가하고 요청해야 함
// 해당 문자열을 포함하지 않은 경우, ID 토큰이 재발급되지 않음
// scopes.append("openid")
//scope 목록을 전달하여 카카오 로그인 요청
UserApi.shared.loginWithKakaoAccount(scopes: scopes) { (_, error) in
if let error = error {
print(error)
}
else {
UserApi.shared.me() { (user, error) in
if let error = error {
print(error)
}
else {
print("me() success.")
guard let user = user else { return }
//do something
let userName = user.kakaoAccount?.name
let userEmail = user.kakaoAccount?.email
let userGender = user.kakaoAccount?.gender
let userProfile = user.kakaoAccount?.profile?.profileImageUrl
let userBirthYear = user.kakaoAccount?.birthyear
let contentText =
"user name : \(userName)\n userEmail : \(userEmail)\n userGender : \(userGender), userBirthYear : \(userBirthYear)\n userProfile : \(userProfile)"
self.textField.text = contentText
}
}
}
}
}
else {
print("사용자의 추가 동의가 필요하지 않습니다.")
}
}
}
}
RxSwift Code:
/// 추가 항목 동의받기
private func kakaoRequestAgreement() {
// 추가 항목 동의 받기(사용자가 동의하지않은 항목에 대한 추가 동의 요청
UserApi.shared.rx.me()
.map({ (user) -> User in
//필요한 scope을 아래의 예제코드를 참고해서 추가한다.
//아래 예제는 모든 스콥을 나열한것.
var scopes = [String]()
if (user.kakaoAccount?.profileNeedsAgreement == true) { scopes.append("profile") }
if (user.kakaoAccount?.emailNeedsAgreement == true) { scopes.append("account_email") }
if (user.kakaoAccount?.birthdayNeedsAgreement == true) { scopes.append("birthday") }
if (user.kakaoAccount?.birthyearNeedsAgreement == true) { scopes.append("birthyear") }
if (user.kakaoAccount?.genderNeedsAgreement == true) { scopes.append("gender") }
//if (user.kakaoAccount?.phoneNumberNeedsAgreement == true) { scopes.append("phone_number") }
if (user.kakaoAccount?.ageRangeNeedsAgreement == true) { scopes.append("age_range") }
if (user.kakaoAccount?.ciNeedsAgreement == true) { scopes.append("account_ci") }
if (scopes.count > 0) {
print("사용자에게 추가 동의를 받아야 합니다.")
self.textField.text = "사용자에게 추가 동의를 받아야 합니다."
// OpenID Connect 사용 시
// scope 목록에 "openid" 문자열을 추가하고 요청해야 함
// 해당 문자열을 포함하지 않은 경우, ID 토큰이 재발급되지 않음
// scopes.append("openid")
// scope 목록을 전달하여 SdkError 처리
throw SdkError(scopes: scopes)
}
else {
print("사용자의 추가 동의가 필요하지 않습니다.")
self.textField.text = "사용자의 추가 동의가 필요하지 않습니다."
return user
}
})
.retry(when: Auth.shared.rx.incrementalAuthorizationRequired())
.subscribe(onSuccess:{ ( user ) in
print("me() success.")
//do something
let userName = user.kakaoAccount?.name
let userEmail = user.kakaoAccount?.email
let userGender = user.kakaoAccount?.gender
let userProfile = user.kakaoAccount?.profile?.profileImageUrl
let userBirthYear = user.kakaoAccount?.birthyear
let contentText =
"user name : \(userName)\n userEmail : \(userEmail)\n userGender : \(userGender), userBirthYear : \(userBirthYear)\n userProfile : \(userProfile)"
self.textField.text = contentText
}, onFailure: {error in
print(error)
})
.disposed(by: disposeBag)
}
4. 카카오톡 로그아웃
로그아웃은 Logout
, Unlink
두 개의 API가 지원됩니다.
4.1 로그아웃
로그아웃은 사용자 액세스 토큰과 리프레시 토큰을 모두 만료시켜 사용자 정보로 API를 호출할 수 없도록하고, 해당 API를 호출하면 요청 성공 여부와 관계없이 토큰을 삭제 처리합니다.
Swift Code:
/// 로그아웃
private func kakaoLogout() {
// 사용자 액세스 토큰과 리프레시 토큰을 모두 만료시켜, 더 이상 해당 사용자 정보로 카카오 API를 호출할 수 없도록 합니다.
UserApi.shared.logout {(error) in
if let error = error {
print(error)
}
else {
print("logout() success.")
}
}
}
RxSwift Code:
/// 로그아웃
private func kakaoLogout() {
// 사용자 액세스 토큰과 리프레시 토큰을 모두 만료시켜, 더 이상 해당 사용자 정보로 카카오 API를 호출할 수 없도록 합니다.
UserApi.shared.rx.logout()
.subscribe(onCompleted:{
print("logout() success.")
self.textField.text = "logout() success."
}, onError: {error in
print(error)
})
.disposed(by: disposeBag)
}
4.2 언링크
언링크는 연결 끊기 요청 성공시, 로그아웃 처리가 함께 이뤄져 토큰이 삭제됩니다.
연결 해제 요청이 성공될때만 로그아웃이 수행되는 API입니다.
Swift Code:
/// 연결 끊기
private func kakaoUnlink() {
// 연결 끊기 요청 성공 시 로그아웃 처리가 함께 이뤄져 토큰이 삭제됩니다.
UserApi.shared.unlink {(error) in
if let error = error {
print(error)
}
else {
print("unlink() success.")
}
}
}
RxSwift Code:
/// 연결 끊기
private func kakaoUnlink() {
// 연결 끊기 요청 성공 시 로그아웃 처리가 함께 이뤄져 토큰이 삭제됩니다.
UserApi.shared.rx.unlink()
.subscribe(onCompleted:{
print("unlink() success.")
self.textField.text = "unlink() success."
}, onError: {error in
print(error)
})
.disposed(by: disposeBag)
}
지금까지 KakaoSDK를 이용한 로그인 API를 알아보았습니다.
혹시나 틀린 부분이 있다면 알려주세요 감사합니다 :)
5. 참고
'iOS > Swift' 카테고리의 다른 글
[iOS] 페이스북 로그인 SDK 적용 (0) | 2023.12.22 |
---|---|
[iOS] 네이버 로그인 SDK 연동하기 (0) | 2023.06.21 |
[iOS] Code Snippet(코드 스니펫), 코드 즐겨찾기 (0) | 2023.06.14 |
[iOS] SwiftLint 적용하기 (0) | 2023.06.05 |
[iOS] Moya 적용하기 (0) | 2023.06.01 |
- Total
- Today
- Yesterday
- nimble
- ios google
- Quick
- swift google signin
- XCTest
- iOS 테스트 코드
- ios 구글 로그인 sdk
- Framework
- iOS Framework
- iOS Nimble
- swift 구글 sdk
- ios mvvm
- iOS 유닛테스트
- swift google login sdk
- swift google sdk
- swift quick
- XCFramework
- iOS Unit Tes
- ios xcframework
- swift reactorkit
- Firebase Distribution
- swift 구글 로그인
- ios google signin
- iOS 단위테스트
- swift framework
- swift xctest
- ios reactorkit
- iOS Quick
- swift nimble
- swift google login
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |