옛날에 시도를 했었지만 실패를 했었던 카카오톡 로그인을 구현하고자 한다.
카카오톡 로그인을 구현하기 위해서는 우선 Kakao Developers에 적용시키고 싶은 프로젝트를 등록해야 한다.
등록하는 방법은 다른 분이 너무 자세하게 잘 설명해주셔서 참고하면 좋을 것 같다.
Cocoapod를 이용한 방법과 SPM을 이용하는 방법이 있는데, 선호하는 방식으로 설치를 하면 될 것 같다. 지금은 SPM으로 진행을 해보겠다.
검색부분에 깃허브 주소를 넣으면 설치를 할 수 있다.
공식문서를 참고해도 좋다.
https://developers.kakao.com/docs/latest/ko/ios/getting-started
공식문서나 다른 블로그에서 LSApplicationQueriesSchemes
를 등록하라고 해서 등록을 했더니 Queried URL Schemes
로 자동으로 변해진다. Xcode 15.0 버전에서 부터 바뀌었는지, 잘 모르겠다.
info.plist에 등록을 하고, 아래쪽을 보면 URL Types 항목을 볼 수 있다. 여기에서 Native App Key를 등록해야 한다.
Native App Key는 Kakao Developers 페이지에서 확인 할 수 있다.
빨강색 부분에 있는 문자열이 키이다. 이 앱 키를 URL Types에서 + 버튼을 눌러서 나온 항목중에 URL Schemes에 넣으면 된다.
AppDelegate.swift 파일에서 Kakao SDK를 초기화하는 코드를 추가하자.
원래는 apiKey
에 앞에서 계속 사용했던 앱 키를 넣어야 한다.
KakaoSDK.initSDK(appKey: "123456")
123456자리에 앱 키를 넣어야 하는 것이다. 그렇지만 GitHub를 사용할 때, apiKey를 같이 push 하는 것은 상당히 위험하다. 따라서 apiKey라는 파일을 따로 만들어서 관리를 하는 것이 좋다. 물론 이 파일은 gitignore에 추가해야 한다.
이 부분은 iOS 버전이 13.0 이전이냐 이후냐에 따라 달라진다.
AppDelegate에 handelOpenUrl()
를 추가해주면 된다.
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
}
...
}
AppDelegate.swift 파일 대신 SceneDelegate.swift 파일에 handleOpenUrl()을 추가하면 된다.
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)
}
}
}
...
}
우선 로그인을 진행하기 위한 두가지 버튼을 추가했다. Kakao에서는 앱으로 로그인하는 방식을 추천하지만, 시뮬레이터에 카카오톡이 깔려있지 않기 때문에 Web으로 접근을 해서 계정으로 로그인하는 버튼도 추가를 했다.
사실 공식문서에서 제공하는 코드를 넣으면 끝이다.
// 카카오톡 실행 가능 여부 확인
if (UserApi.isKakaoTalkLoginAvailable()) {
UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in
if let error = error {
print(error)
}
else {
print("loginWithKakaoTalk() success.")
//do something
_ = oauthToken
}
}
}
UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in
if let error = error {
print(error)
}
else {
print("loginWithKakaoAccount() success.")
//do something
_ = oauthToken
}
}
import UIKit
import KakaoSDKAuth
import KakaoSDKUser
class LoginViewController: UIViewController {
private let loginView = LoginView()
override func loadView() {
view = loginView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
clickLoginButton()
}
}
private extension LoginViewController {
func clickLoginButton() {
loginView.kakaoLoginByAppButton.addTarget(self, action: #selector(kakaoLoginByApp), for: .touchUpInside)
loginView.kakaoLoginByWebButton.addTarget(self, action: #selector(kakaoLoginByWeb), for: .touchUpInside)
}
@objc func kakaoLoginByApp() {
if (UserApi.isKakaoTalkLoginAvailable()) {
UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in
if let error = error {
print(error)
}
else {
print("loginWithKakaoTalk() success.")
//do something
_ = oauthToken
let accessToken = oauthToken?.accessToken
self.setUserInfo()
}
}
}
}
@objc func kakaoLoginByWeb() {
UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in
if let error = error {
print(error)
}
else {
print("loginWithKakaoAccount() success.")
//do something
_ = oauthToken
let accessToken = oauthToken?.accessToken
self.setUserInfo()
}
}
}
func setUserInfo() {
UserApi.shared.me() {(user, error) in
if let error = error {
print(error)
}
else {
print("me() success.")
//do something
_ = user
self.loginView.userNameLabel.text = user?.kakaoAccount?.profile?.nickname
self.loginView.userIDLabel.text = String((user?.id)!)
}
}
}
}
user?
다음에 속성에 접근을 하면 다양한 속성을 이용할 수 있다.
import UIKit
import SnapKit
class LoginView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
addSubView()
autoLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// 카카오톡 로그인 버튼
private(set) lazy var kakaoLoginByAppButton: UIButton = {
let button = UIButton()
button.setTitle("카카오톡으로 로그인", for: .normal)
button.titleLabel?.textColor = UIColor.white
button.backgroundColor = UIColor.systemBlue
return button
}()
// 카카오 계정 로그인 버튼
private(set) lazy var kakaoLoginByWebButton: UIButton = {
let button = UIButton()
button.setTitle("카카오계정으로 로그인", for: .normal)
button.titleLabel?.textColor = UIColor.white
button.backgroundColor = UIColor.systemBlue
return button
}()
private(set) lazy var userNameLabel: UILabel = {
let label = UILabel()
label.text = "userName"
label.textColor = UIColor.black
label.font = UIFont.bodyFont(.medium, weight: .bold)
return label
}()
private(set) lazy var userIDLabel: UILabel = {
let label = UILabel()
label.text = "userID"
label.textColor = UIColor.black
label.font = UIFont.bodyFont(.medium, weight: .bold)
return label
}()
}
private extension LoginView {
// NoticeBoardView의 기본 UI 설정
func configureUI() {
backgroundColor = UIColor.white
}
// noticeBoardTableView를 SubView에 추가
func addSubView() {
addSubview(kakaoLoginByAppButton)
addSubview(kakaoLoginByWebButton)
addSubview(userNameLabel)
addSubview(userIDLabel)
}
// 오토레이아웃 설정
func autoLayout() {
kakaoLoginByAppButton.snp.makeConstraints { make in
make.center.equalToSuperview()
make.leading.equalTo(snp.leading).offset(Constant.margin3)
make.trailing.equalTo(snp.trailing).offset(-Constant.margin3)
}
kakaoLoginByWebButton.snp.makeConstraints { make in
make.top.equalTo(kakaoLoginByAppButton.snp.bottom).offset(Constant.margin3)
make.leading.equalTo(snp.leading).offset(Constant.margin3)
make.trailing.equalTo(snp.trailing).offset(-Constant.margin3)
}
userNameLabel.snp.makeConstraints { make in
make.top.equalTo(kakaoLoginByWebButton.snp.bottom).offset(Constant.margin3)
make.leading.equalTo(snp.leading).offset(Constant.margin3)
make.trailing.equalTo(snp.trailing).offset(-Constant.margin3)
}
userIDLabel.snp.makeConstraints { make in
make.top.equalTo(userNameLabel.snp.bottom).offset(Constant.margin3)
make.leading.equalTo(snp.leading).offset(Constant.margin3)
make.trailing.equalTo(snp.trailing).offset(-Constant.margin3)
}
}
}
버튼 이미지도 디자인 리소스에서 다운받아서 적용시켰다.