Suyeon9911 / TIL

매일 오전에 적는 미라클 TIL 🥣
10 stars 0 forks source link

[iOS] 카카오 로그인 & 애플 로그인 #50

Open Suyeon9911 opened 2 years ago

Suyeon9911 commented 2 years ago

소셜로그인 플로우 정리

로그인 API 명세

=> 일단 서버랑 연결을 시켜놓고 값들을 어떻게 처리를 할지.. 생각을 해보자..

회원가입 API 명세

전체적인 플로우 - 일단 카카오로그인 먼저.. 카카오 로그인 버튼 -> 클라 내에서 카카오SDK 로 카카오로그인을 띄운다 -> 앱이 있으면 카톡으로 없으면 웹으로 연결시킨다 -> 성공 시 카카오에서 발급하는 토큰 저장 -> 저장한 토큰으로 서버에 Post 요청을 한다 -> 기존 유저일 경우 : 로그인이 된다 !

신규 유저일 경우? 카카오에서 발급한 토큰 + 카카오에서 가져온 정보 + 프로필 설정 뷰에서 가져온 정보로 회원가입 POST 연결 ㅋㅋ -> 로그인 된다

Suyeon9911 commented 2 years ago

소셜로그인의 의미

카카오와 애플 등의 소셜플랫폼에 이미 가입된 사용자의 계정들이 존재 이 계정들에는 고유한 userID가 있는데 소셜 api와의 연동을 통해서 이 userID를 받아오는 것이다. -> 이렇게 받아온 userID로 앱의 회원을 판별하는 고유한 값으로 사용 !

소셜에서 받아온 userID를 이용해서 Authentication 시스템을 구축하기 위해서 서버에서 제공해야할 API는 2가지 !

Suyeon9911 commented 2 years ago

스플래시에서 자동로그인 판별해서 화면 띄워주기

class SplashViewController: UIViewController {
    private weak var appDelegate = UIApplication.shared.delegate as? Appdelegate

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            if self.appDelegate?.isLogin {
                self.presentToMain()
            } else {
                self.presentToLogin 
            }
        }
    }
}

스플래시에서 자동로그인을 판별해서 로그인 된 상태이면 메인으로 안되어있으면 로그인 화면으로 간다. -> 앱딜리게이트에서 자동로그인 구현해주자 !!!

Suyeon9911 commented 2 years ago

LoginViewController 구현

AuthenticationServices랑 kakaoSDKUser 모듈을 import 해주고 버튼을 눌렀을 때 로직을 연결시킨다 !

private func signupWithKakao() {
    if UserApi.isKakaoTalkLoginAvailable() {
        loginWithKakaoApp()
    } else {
        loginWithWeb()
    }
}
private func loginWithKakaoApp() {
    UserApi.shared.loginWithKakaoTalk { _, error in
        if let error = error {
            print(error)
        } else {
            print("loginWithKakaoApp() success.")

            self.getUserID()
        }
    }
}

private func loginWithWeb() {

    UserApi.shared.loginWithKakaoAccount { _, error in
        if let error = error {
            print(error)
        } else {
            print("loginWithKakaoAccount() success.")

            self.getUserID()
        }
    }
}

여기서 에러가 없을 경우 getUserID라는 함수를 호출한다 !

private func getUserID() {
    UserApi.shared.me {(user, error) in
        if let error = error {
            print(error)
        } else {
            if let userID = user?.id {
                UserDefaults.standard.set(String(userID), forKey: Const.UserDefaultsKey.userID)
                UserDefaults.standard.set(false, forKey: Const.UserDefaultsKey.isAppleLogin)

                self.loginWithAPI(userID: String("Kakao@\(userID)"))
            }
        }
    }
}

이 함수는 카카오의 UserAPI에 접근하여 Userid를 불러오고, userdefault에 저장한다. 동시에 애플로그인을 막기 위해 isAppleLogin을 false로 설정 !

이후 loginWithAPI를 호출하는데 여기서 서버통신이 시작되는 것 ! 카카오와 애플의 유저아이디가 중복될 수 있기 때문에 구분하기 위해서 Kakao@(userID) 이렇게 처리해준다.

Suyeon9911 commented 2 years ago

애플로그인 !

애플로그인을 찾아봤을때 거의 요런 방식들이 나오는데 해빗의 경우 파이어베이스를 이용하는 것 같다. request로 파베 uid를 보내줘야하는데 요걸 연결하는 방법은 더 찾아봐야할 것 같다

private func signupWithApple() {
    let appleIDProvider = ASAuthorizationAppleIDProvider()
    let request = appleIDProvider.createRequest()
    request.requestedScopes = [.fullName, .email]

    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    authorizationController.delegate = self
    authorizationController.presentationContextProvider = self
    authorizationController.performRequests()
}

일단 바로 애플아이디 로그인을 하려면 IDProvider 객체를 생성하고, request를 만들고, request에서 받을 정보의 범위를 설정해준다. authorizationController 객체를 생성하는데, 위에서 만들어준 request를 주입하여 생성한다 !! 이후 컨트롤러 객체에서 performRequests를 수행하면 애플과의 서버통신을 시작한다

애플로그인은 한가지 더 프로토콜을 채택하여 메서드를 구현해주어야한다.

// MARK: - AppleSignIn

extension LoginVC: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return self.view.window!
    }

    // Apple ID 연동 성공 시
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        switch authorization.credential {
        case let appleIDCredential as ASAuthorizationAppleIDCredential:
            let userIdentifier = appleIDCredential.user

            UserDefaults.standard.set(userIdentifier, forKey: Const.UserDefaultsKey.userID)
            UserDefaults.standard.set(true, forKey: Const.UserDefaultsKey.isAppleLogin)

            loginWithAPI(userID: "Apple@\(userIdentifier)")
        default:
            break
        }
    }

    // Apple ID 연동 실패 시
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
    }
}
Suyeon9911 commented 2 years ago

로그인 API 호출

extension LoginVC {
    private func loginWithAPI(userID: String) {
        AuthAPI.shared.login(socialID: userID, fcmToken: UserDefaults.standard.string(forKey: Const.UserDefaultsKey.fcmToken) ?? "") { response in
            switch response {
            case .success(let data):
                if let data = data as? Login {
                    if data.isNew {
                        // 회원가입을 하지 않은 사용자입니다.
                        guard let nextVC = UIStoryboard(name: Const.Storyboard.Name.profileSetting, bundle: nil).instantiateViewController(withIdentifier: Const.ViewController.Identifier.profileSetting) as? ProfileSettingVC else { return }

                        nextVC.modalPresentationStyle = .fullScreen

                        self.present(nextVC, animated: true, completion: nil)
                    } else {
                        // 회원 정보를 불러왔습니다.
                        UserDefaults.standard.set(data.accesstoken, forKey: Const.UserDefaultsKey.accessToken)

                        self.presentToMainTBC()
                    }
                }
            case .requestErr(let message):
                print("loginWithAPI - requestErr: \(message)")
            case .pathErr:
                print("loginWithAPI - pathErr")
            case .serverErr:
                print("loginWithAPI - serverErr")
            case .networkFail:
                print("loginWithAPI - networkFail")
            }
        }
    }
}

이제 프로젝트 서버와의 통신을 하는데 여기서는 파라미터로 유저아이디랑 fcmToken을 보내주고 있다 !

로그인 통신을 하게 되면 기존유저인지 신규유저인지 판별이 가능하다 !

  1. 신규 유저인 경우 - 프로필설정 화면으로 넘어가는것을 볼수 있다.
  2. 기존 유저인 경우 - 회원정보를 불러와서 엑세스토큰을 유저디폴트에 저장하고 메인으로 이동한다.
Suyeon9911 commented 2 years ago

회원가입 API 호출

회원가입 API와 로그인 API의 차이점

로그인은 이 사람이 기존에 가입한 사람이고, 서버의 DB에 저장된 사람인지를 판별하여 앱의 메인 플로우로 넘어가게 해주는 기능으로, 서버에 존재하는 데이터를 변동시키지 않는다.

반면 회원가입 API는 새로운 userID를 서버의 DB에 저장하는 POST 메서드이다. 이를 통해 다음 로그인 API 호출 시 자동으로 넘어가게 되는 것이다.

회원가입은 로그인 하면서 받아온 유저아이디와 프로필 설정 내용을 바탕으로 post 요청을 한다
성공하면 로그인과 동일하게 token을 보내주고 이를 userDefalut에 저장한다.

Suyeon9911 commented 2 years ago

AppDelegate

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var isLogin = false

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        KakaoSDK.initSDK(appKey: "d51e83bca123750446afc70ab65225b9")

        let accessToken = UserDefaults.standard.string(forKey: Const.UserDefaultsKey.accessToken)

        if accessToken != nil {
            if UserDefaults.standard.bool(forKey: Const.UserDefaultsKey.isAppleLogin) {
                // 애플 로그인으로 연동되어 있을 때, -> 애플 ID와의 연동상태 확인 로직
                let appleIDProvider = ASAuthorizationAppleIDProvider()
                appleIDProvider.getCredentialState(forUserID: UserDefaults.standard.string(forKey: Const.UserDefaultsKey.userID) ?? "") { credentialState, _ in
                    switch credentialState {
                    case .authorized:
                        print("해당 ID는 연동되어있습니다.")
                        self.isLogin = true
                    case .revoked:
                        print("해당 ID는 연동되어있지않습니다.")
                        self.isLogin = false
                    case .notFound:
                        print("해당 ID를 찾을 수 없습니다.")
                        self.isLogin = false
                    default:
                        break
                    }
                }
            } else {
                if AuthApi.hasToken() {
                    UserApi.shared.accessTokenInfo { _, error in
                        if let error = error {
                            if let sdkError = error as? SdkError, sdkError.isInvalidTokenError() == true {
                                self.isLogin = false
                            }
                        } else {
                            // 토큰 유효성 확인한 경우.
                            self.isLogin = true
                        }
                    }
                } else {
                    // 유효한 토큰이 없는 경우.
                    self.isLogin = false
                }
            }
        } else {
            // access token 이 없는 경우.
            self.isLogin = false
        }

                return true
    }
}

isLogin이라는 Bool 값으로 자동로그인 기능을 만들자 ! 카카오 SDK에 대한 설정도 해주고, 유저디폴트의 엑세스 토큰을 받아오는데 엑세스 토큰이 없을 경우 -> 애플로그인이면 애플 ID와의 연동상태를 확인한다

카카오 로그인의 경우에도 유효성 검사를 해준다 유효하다면 앞서 구현해둔 스플래시 화면에서 자동으로 메인으로 넘어간다 !

Suyeon9911 commented 2 years ago

https://seungchan.tistory.com/entry/Swift-%EC%B9%B4%EC%B9%B4%EC%98%A4-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8

Suyeon9911 commented 2 years ago

https://mini-min-dev.tistory.com/38

Suyeon9911 commented 2 years ago

https://sujinnaljin.medium.com/ios-%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%86%A1-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-58a525e6f219

Suyeon9911 commented 2 years ago

해빗 프로젝트의 경우 FirebaseAuth 클래스에서 애플로그인을 구현해주는 것 같다...

Suyeon9911 commented 2 years ago

파베로 보내고... 파베에서.. 난 왤케 감자일까....? 눈무리 난다..