이번에는 Firebase에 카카오 로그인을 구현해보았어요.
Firebase에는 카카오로 로그인 기능이 없기 때문에 이메일 로그인으로 진행을 했어요.
email에 카카오 이메일, password에는 카카오에서 제공하는 고유한 유저 아이디가 입력되어요.
이번에는 RxSwift를 활용해서 조금더 단순하게 만들어보았어요.
1. 앱 등록 후 네이티브 키, 이메일 필수 동의 설정, KakaoSDK 설치
먼저 https://developers.kakao.com/ 에서 앱을 등록하고 네이티브 키를 얻어야 해요.
또한 이메일로 진행을 하기 위해서 카카오 계정 이메일을 필수 동의로 해주세요.
KakaoSDK도 설치해주어야해요.
이러한 과정은 다른 분의 블로그에서 잘 정리되어 있어서 여기서는 생략할게요.
2. AppDelegate, SceneDelegate 코드 추가
AppDelegate에 다음과 같이 추가해주세요.
APIKey.apikey는 앱을 등록하고 얻는 네이티브 키를 String 값으로 넣어주세요.
import KakaoSDKCommon
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
KakaoSDK.initSDK(appKey: APIKey.apiKey)
return true
}
SceneDelegate에 다음과 같이 추가해주세요.
import KakaoSDKAuth
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
if AuthApi.isKakaoTalkLoginUrl(url) {
_ = AuthController.handleOpenUrl(url: url)
}
}
}
3. ViewModel에 프로퍼티들 생성
코드 작성에 필요한 프로퍼티들을 생성해줄게요.
import KakaoSDKAuth
import KakaoSDKUser
import RxSwift
typealias FirebaseUser = FirebaseAuth.User
typealias KakaoUser = KakaoSDKUser.User
private var user: FirebaseUser?
let output = PublishSubject<Output>()
enum Output {
case didFirstSignIn
case didAlreadySignIn
case didFailToSignIn(error: Error)
}
4. ViewController에서 구독자 생성
ViewController에서 output을 구독하여 Sign In이 종료되면 발생하는 이벤트에 따라서 다르게 동작할 수 있는 코드를 작성해줄게요.
func bind() {
vm.output
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] event in
switch event {
case .didFirstSignIn:
print("처음 로그인했어요.")
case .didAlreadySignIn:
self?.delegate?.authenticationComlete()
self?.dismiss(animated: true, completion: nil)
case .didFailToSignIn(let error):
print("DEBUG: Failed sign in user \(error.localizedDescription)")
}
}).disposed(by: disposeBag)
}
5. Firebase에 Register 및 SignIn 도와주는 함수 생성
import Firebase
struct AuthService {
static func signinUser(withEmail email: String, password: String) async throws -> User {
do {
let result = try await Auth.auth().signIn(withEmail: email, password: password)
return result.user
} catch {
print("DEBUG: Failed to signinUser\(error.localizedDescription)")
throw error
}
}
static func registerUser(withEmail email: String, password: String) async throws -> User {
do {
let result = try await Auth.auth().createUser(withEmail: email, password: password)
return result.user
} catch {
print("DEBUG: Failed to registerUser\(error.localizedDescription)")
throw error
}
}
}
6. 카카오 로그인 버튼을 눌렀을 때 kakaoSignin() 함수 호출
필요한 준비를 마쳤으니 ViewController에서 ViewModel의 kakaoSignin() 함수를 호출해줄게요.
@objc func kakaoSignInButtonTapped() {
vm.kakaoSignin()
}
7. 카카오 로그인 코드
카카오 로그인은 다음과 같은 흐름으로 진행되어요.
순서대로 읽어가면 쉽게 흐름을 알 수 있으니 자세한 설명은 생략할게요.
func kakaoSignin() {
if UserApi.isKakaoTalkLoginAvailable() {
signInWithKakaoTalkApp()
} else {
signInWithKakaoWeb()
}
}
private func signInWithKakaoTalkApp() {
UserApi.shared.loginWithKakaoTalk { [weak self] _, error in
if let error = error {
self?.output.onNext(.didFailToSignIn(error: error))
}
self?.validateKakaoUserData()
}
}
private func signInWithKakaoWeb() {
UserApi.shared.loginWithKakaoAccount { [weak self] _, error in
if let error = error {
self?.output.onNext(.didFailToSignIn(error: error))
}
self?.validateKakaoUserData()
}
}
private func validateKakaoUserData() {
UserApi.shared.me { [weak self] kakaoUser, error in
if let error = error {
self?.output.onNext(.didFailToSignIn(error: error))
} else {
self?.registerKakaoUserToAuth(user: kakaoUser)
}
}
}
private func registerKakaoUserToAuth(user kakaoUser: KakaoUser?) {
Task {
do {
guard let email = kakaoUser?.kakaoAccount?.email,
let password = kakaoUser?.id else { return }
let result = try await AuthService.registerUser(withEmail: email, password: String(password))
validateKakaoUserInAuth(user: kakaoUser)
} catch let error as NSError {
if error.code == AuthErrorCode.emailAlreadyInUse.rawValue {
validateKakaoUserInAuth(user: kakaoUser)
} else {
output.onNext(.didFailToSignIn(error: error))
}
}
}
}
private func validateKakaoUserInAuth(user: KakaoUser?) {
Task {
do {
guard let email = user?.kakaoAccount?.email,
let password = (user?.id) else { return }
let user = try await AuthService.signinUser(withEmail: email, password: String(password))
self.user = user
await didUserAlreadyRegisterInFirestore()
} catch {
output.onNext(.didFailToSignIn(error: error))
}
}
}
private func didUserAlreadyRegisterInFirestore() async {
do {
guard let user = user else { return }
let status = try await FirebaseService.isUserAlreadyExisted(user: user)
output.onNext(status ? .didAlreadySignIn : .didFirstSignIn)
} catch {
output.onNext(.didFailToSignIn(error: error))
}
}
카카오 로그인 구현 완료!
깃허브나 다른 블로그를 보며 공부했던 코드들은 Combine, RxSwift 등으로 작성이 되어 있었어요.
개인적으로는 do/catch, async/await으로 작성했을 때가 더 가독성이 높은 것 같아서 새롭게 작성해주었어요.
제 마음데로 작성해보았는데, 혹시 더 좋은 의견이 있으시다면 댓글로 알려주세요!
전체코드:
'Project > UIKit 업비트' 카테고리의 다른 글
[UIKit] Firebase, 애플 로그인 구현하기 (0) | 2023.05.01 |
---|---|
[UIKit] UIMenu로 메뉴를 만들어보자 (0) | 2023.04.27 |
[UIKit] TableView에서 insetGrouped 스타일로 섹션을 나눠보자 (0) | 2023.04.22 |
[UIKit] TableView 위에 Header를 만들어보자 (0) | 2023.04.22 |
[UIKit] UISearchController, 검색 기능 구현하기 (1) | 2023.04.17 |
댓글