[TIL] 10.17

Junyoung_Hong·2023년 10월 17일
0

TIL_10월

목록 보기
10/20
post-thumbnail

1. 카카오톡 로그인 기능 Firebase와 연결하기

이미 카카오톡 로그인기능을 구현했는데 Firebase와 연결을 해야하는 이유가 무엇일까? 로그인 기능을 구현한 프로젝트는 Firebase의 Realtime Database를 사용하고 있다. 이 Database를 사용하기 위해서는 FirebaseLogin이 되어있어야 한다. 그리고 사용자 정보의 데이터 모델을 설계했을때, Firebase에로그인한 사용자의 id가 필요했다.

1-1. 구현 설계

코드의 가장 큰 흐름은 카카오톡 로그인 → Firebase 로그인이다. Firebase의 로그인 방법에는 다양한 방법들이 있지만, 이메일/비밀번호 방법을 사용할 것이다.

Firebase에서는 이메일/비밀번호 로그인 방식에 규칙이 존재한다. 이메일은 @ 앞에 문자나 숫자가 있어야 하고, 뒤에는 .com이나 .kr 과 같은 식별자(?)가 있어야 하는 것이다. 비밀번호는 최소 6자리 이상의 숫자가 있어야 한다.

이메일과 비밀번호의 형식을 만족시키는 데이터가 카카오톡 정보를 매칭시켜보니, 다음과 같이 매칭이 되는 것이 가장 적절하다고 판단되었다.

Firebase 이메일 = 카카오톡 사용자 정보의 이메일
Firebase 비밀번호 = 카카오톡 사용자 id

카카오톡 사용자 id가 10자리 숫자로 이루어져 있기 때문이다.

1-2. Firebase 설정

Friebase에서 이메일/비밀번호 로그인 방식을 활성화 시켜주어야 한다.

이제 Firebase에 가입(create)을 하고, 로그인(login)을 하면 된다.

1-3. 카카오톡 로그인 유저 정보 가져오기

이제 로그인한 사용자의 계정 이메일id를 가져오자.

https://developers.kakao.com/docs/latest/ko/kakaologin/ios#req-user-info

var kakaoEmail: String = ""
var kakaoPassword: String = ""

private func getUserInfo() {
    UserApi.shared.me() {(user, error) in
        if let error = error {
            print(error)
        }
        else {
            print("me() success.")
                
            //do something
            _ = user
            if let email = user?.kakaoAccount?.email {
                self.kakaoEmail = email
            }
            if let id = user?.id {
                self.kakaoPassword = String(id)
            }
            self.regiSterFirebase(email: self.kakaoEmail, password: self.kakaoPassword)
        }
    }
}

1-4. 가입 함수 구현

Firebase입장에서는 새로운 유저가 생기는 것이기 때문에 회원가입을 먼저 해야한다.

private func regiSterFirebase(email: String, password: String) {
    Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
        // Error(등록 실패)
        if let e = error {
            print(e.localizedDescription)
        }
            
        // Success(등록 성공)
        else {
            // 로그인 함수 필요
        }
    }
}

createUser()로 간단하게 구현을 할 수 있고, 등록이 완료되면 로그인을 해주어야 한다.

1-5. 로그인 함수 구현

로그인은 signIn()으로 구현한다.

private func loginFirebase(email: String, password: String) {
    Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
        // Error(등록 실패)
        if let e = error {
            print(e.localizedDescription)
        }
        // Success(등록 성공)
        else {
            // 화면 전환 함수 필요
        }
    }
}

1-6. 전체 코드

regiSterFirebase() 에서 사용자 정보를 가져오고, loginFirebase에서 가입한 사용자의 이메일과 비밀번호를 가져오면 완성이다.

import UIKit
import KakaoSDKAuth
import KakaoSDKUser
import Firebase

class LoginViewController: UIViewController {
    
    private let loginView = LoginView()
    private let mainBarController = TabBarController()
    
    var kakaoEmail: String = ""
    var kakaoPassword: String = ""
    
    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.kakaoLoginButton.addTarget(self, action: #selector(kakaoLogin), for: .touchUpInside)
    }
    
    @objc func kakaoLogin() {
        // MARK: - 카카오톡 앱으로 로그인
        if UserApi.isKakaoTalkLoginAvailable() {
            UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in
                if let error = error {
                    print(error)
                }
                else {
                    print("loginWithKakaoTalk() success.")

                    //do something
                    _ = oauthToken
                    
                    self.getUserInfo()
                }
            }
        }
        
        // MARK: - 카카오톡 계정으로 로그인
        else {
            UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in
                if let error = error {
                    print(error)
                }
                else {
                    print("loginWithKakaoAccount() success.")
                    
                    //do something
                    _ = oauthToken
                    
                    self.getUserInfo()
                }
            }
        }
    }
    
    // MARK: - 카카오로 로그인한 사용자 정보 가져오기
    private func getUserInfo() {
        UserApi.shared.me() {(user, error) in
            if let error = error {
                print(error)
            }
            else {
                print("me() success.")
                
                //do something
                _ = user
                if let email = user?.kakaoAccount?.email {
                    self.kakaoEmail = email
                }
                if let id = user?.id {
                    self.kakaoPassword = String(id)
                }
                self.regiSterFirebase(email: self.kakaoEmail, password: self.kakaoPassword)
            }
        }
    }
    
    // MARK: - Firebase 등록
    private func regiSterFirebase(email: String, password: String) {
        Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
            // Error(등록 실패)
            if let e = error {
                print(e.localizedDescription)
            }
            
            // Success(등록 성공)
            else {
                self.loginFirebase(email: email, password: password)
            }
        }
    }
    
    // MARK: - Firebase 로그인
    private func loginFirebase(email: String, password: String) {
        Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
            // Error(등록 실패)
            if let e = error {
                print(e.localizedDescription)
            }
            // Success(등록 성공)
            else {
                // 화면 전환 함수 필요
            }
        }
    }
}

1-7. 결과

Firebase에 가보면 식별자에는 이메일이 들어가있고, 사용자 UID도 정상적으로 생성된 것을 확인할 수 있다.

2. 자동로그인 기능

보통 SNS로그인을 하면, 그 다음 앱을 사용할때는 자동적으로 로그인이 되서 바로 메인페이지로 이동을 한다. 이 기능을 토큰을 이용해서 구현해보고자 한다.

2-1. 토큰 이해하기

https://developers.kakao.com/docs/latest/ko/kakaologin/common#token

보통 로그인 기능을 구현하려고 여러 자료를 찾다보면 토큰이라는 용어를 많이 보게된다. 토큰은 일반적으로 인증 및 권한 부여에 사용되는 문자열 값이다. 이 토큰은 보통 사용자를 식별하거나 특정 작업을 수행할 수 있는 권한을 부여하는 데 사용된다. 토큰의 종류는 두 가지이다.

  1. 액세스 토큰 (Access Token): 액세스 토큰은 주로 사용자를 인증하고 API 엔드포인트에 액세스하는 데 사용된다. 예를 들어, 소셜 미디어 로그인 또는 OAuth 인증 후에 액세스 토큰이 발급되어 특정 리소스에 접근할 수 있도록 허용한다.

  2. 리프레시 토큰 (Refresh Token): 리프레시 토큰은 액세스 토큰의 유효성이 만료되었을 때 새로운 액세스 토큰을 발급받을 수 있게 해주는 토큰이다. 이 토큰은 보안 및 편리성을 위해 사용된다.

액세스 토큰은 사용자가 로그인하고 인증된 후에 발급된다. 일반적으로, 액세스 토큰은 API 호출에 함께 전송되어 특정 리소스에 액세스할 권한을 부여한다. 또한, 리프레시 토큰은 보통 액세스 토큰의 만료 시간을 관리하고, 만료되면 새로운 액세스 토큰을 발급받도록 한다.

카카오톡 문서를 읽어보면 동일한 내용이 적혀 있는 것을 확인 할 수 있다.

토큰 정보

토큰의 정보를 보기 위해서는 UserApiaccessTokenInfo() API로 토큰 정보를 조회할 수 있다.

https://developers.kakao.com/docs/latest/ko/kakaologin/ios#get-token-info

https://developers.kakao.com/sdk/reference/ios/release/KakaoSDKUser/documentation/kakaosdkuser/accesstokeninfo

2-2. SceneDelegate 수정하기

만약 사용자가 로그인을 했다면 토큰이 생겼을 것이다. 이 토큰의 여부를 활용하면 자동 로그인 기능을 구현할 수 있다. 그렇지만 토큰이 있는 부분에 위에서 진행했던 Firebase 로그인까지 진행해 주어야 한다.

토큰이 있다 → 카카오 사용자 정보가 있다 → Firebase로그인을 진행한다 → 메인 페이지를 바로 보여준다
토큰이 없다 → 로그인 되지 않았다 → 로그인 페이지를 보여준다

토큰의 여부는 AuthApihasToken()을 호출해서 알 수 있다.

https://developers.kakao.com/docs/latest/ko/kakaologin/ios#token-presence

토큰이 있을때와 없을때의 rootViewController를 설정해서 진행을 한다. 토큰이 있을때는 TabBarController()로, 토큰이 없을때는 LoginViewController()로 정하는 것이다.

import UIKit
import KakaoSDKAuth
import KakaoSDKUser
import KakaoSDKCommon

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
       
        guard let scene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: scene)
        window.backgroundColor = .white
        
        // 카카오 로그인 토큰이 있는지 여부 확인
        if AuthApi.hasToken() {
            UserApi.shared.accessTokenInfo { _, error in
                if let error = error {
                    if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true {
                        // 로그인 필요
                        let mainVC = LoginViewController()
                        window.rootViewController = mainVC
                    }
                    else {
                        // 기타 에러
                        print(error.localizedDescription)
                    }
                }
                else {
                    // 토큰 유효성 체크 성공(필요 시 토큰 갱신됨)
                    self.getUserInfo()
                    let mainVC = TabBarController()
                    window.rootViewController = mainVC
                }
            }
        }
        else {
            // 로그인 필요
            let mainVC = LoginViewController()
            window.rootViewController = mainVC
        }

        window.makeKeyAndVisible()
        self.window = window
    }
    
    // 카카오 사용자 정보 가져오기
    private func getUserInfo() {
        UserApi.shared.me() {(user, error) in
            if let error = error {
                print(error)
            }
            else {
                print("me() success.")
                
                //do something
                _ = user
                if let email = user?.kakaoAccount?.email,
                   let id = user?.id{
                    let password = String(id)
                    Auth.auth().signIn(withEmail: email, password: password)
                }
            }
        }
    }

    .
    .
    .
}

3. 로그인 시 화면 전환

보통 화면전환을 구현하게 되면 많은 화면들이 스택으로 쌓이게 된다. 화면들이 많이 쌓이게 되면 앱이 죽는 경우가 생길 수 있기 때문에, 로그인이 성공하고 나서 메인 페이지로 갈 때의 화면 전환은 rootViewController를 바꾸고자 한다.

3-1. 화면 전환 함수 만들기

우선 화면전환을 하기 위해서 함수를 만들어주자. rootVC를 바꾸기 때문에 SceneDelegate에 만들었다.

// rootVC를 바꾸기 위한 함수
func changeRootVC(_ vc: UIViewController, animated: Bool) {
    guard let window = self.window else { return }
    window.rootViewController = vc
        
    UIView.transition(with: window, duration: 0.2, options: [.transitionCrossDissolve], animations: nil, completion: nil)
}

3-2. 화면 전환 함수 호출하기

이제 함수를 호출해야 한다. 로그인이 성공했을 때, 호출을 하기 때문에 loginFirebase()에 넣어주자.

private let mainBarController = TabBarController()
.
.
.
private func loginFirebase(email: String, password: String) {
    Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
        // Error(등록 실패)
        if let e = error {
            print(e.localizedDescription)
        }
        
        // Success(등록 성공)
        else {        
            (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.changeRootVC(self.mainBarController, animated: true)
        }
    }
}

TabBarController()의 변수를 tabBarController라고 하면 Cannot override with a stored property 'tabBarController' 라는 오류가 생긴다. 이미 UIViewController안에 tabBarController라는 변수명이 있기 때문에 발생하는 오류이다.

profile
iOS 개발자를 향해 성장 중

0개의 댓글