샘성의 iOS 개발 일지

[KakaoMap 클론] 4. Firebase에 카카오톡 로그인 유저 정보 저장하기 본문

iOS/UiKit

[KakaoMap 클론] 4. Firebase에 카카오톡 로그인 유저 정보 저장하기

SamusesApple 2023. 6. 9. 17:46
728x90

 

   로그인 한 유저의 검색 기록, 장소별 별점, 즐겨찾기 한 장소 쉽게 저장하고 꺼내볼 수 있도록 Firebase에 저장할 것이다. 

이를 위해 'Cocoa Pods'를 사용하여 Firebase를 세팅하고 카카오톡 로그인 유저 데이터를 저장해보자!

 

 

배경 : 카카오톡 로그인 구현 완료 된 상태

  카카오톡 로그인 구현이 안 된 상태라면, 하단의 게시글을 참고하시면 좋을 것 같습니다.

 

카카오톡 로그인 구현하기

국민 메신저인 카카오톡 로그인 구현은 필수일 것 같아 구현하면서 기록하려한다. 간단한 Auth만 구현하는 것이기 때문에 스토리보드로 해보려고 한다. 1. 종류 우선, 카카오에서 제공하는 로그

iossammy.tistory.com

 

 

1. Firebase 프로젝트 세팅하기

하단의 사이트에 접속 >> '시작하기' 버튼을 눌러 새로운 프로젝트를 생성한다.

 

 

Firebase

Firebase is an app development platform that helps you build and grow apps and games users love. Backed by Google and trusted by millions of businesses around the world.

firebase.google.com

 

   원하는 프로젝트명을 기입한 후, 구글 애널리스트 사용 여부 체크를 하면 상단의 이미지와 함께 새 프로젝트 생성이 완료된다.

 

 

  생성이 완료되면 상단과 같은 화면을 볼 수 있다. iOS 앱을 만드므로 'iOS+'라고 적혀있는 동그라미를 누른다.

 

 

  이제 각자 프로젝트의 번들 아이디를 사용하여 빈칸을 기입 후, '앱 등록' 버튼을 누른다.

 

 

그리고 'GoogleService-Info.plist 다운로드' 버튼을 누르면 하단과 같은 info.plist를 다운받게 된다.

 

 

  프로젝트에 다운받은 info.plist를 드래그-앤 드롭 하여 집어넣는다.

그리고 Firebase 프로젝트 화면의 '다음' 버튼을 누른다. 

주의: 이 때, 파일명이 'GoogleService-Info.plist'인지 잘 확인해야한다. 뒤에 숫자가 붙는다던지.. 다를 경우 실행이 안된다.

 

 

  이제 Firebase에 필요한 소스파일을 다운받아 프로젝트에 넣어야한다. 

SPM(Swift Package Manager)를 사용해도 되지만, 필자는 Cocoa Pods를 사용하여 해당 파일을 프로젝트에 install 할 것이다.

 

 

Apple 앱에 Firebase 설치  |  Apple 플랫폼용 Firebase

Google I/O 2023에서 Firebase의 주요 소식을 확인하세요. 자세히 알아보기 의견 보내기 Apple 앱에 Firebase 설치 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Swift P

firebase.google.com

 

pod 'FirebaseAuth'
pod 'FirebaseFirestore'

  pod file에 두가지 pod를 리스트에 추가 및 저장한다.

 

cd 프로젝트 경로  // 프로젝트 경로로 이동
pod install --repo-update  // update 된 pod file 다운로드

 

  여러개의 pod file들이 다운받아지는 것을 확인할 수 있다. (오래걸림..)

다 다운 받아졌으면, 프로젝트가 build되는지 확인한다. (오래걸림2222.... 약 4000개 이상의 파일이 빌드되는 것을 볼 수 있다...)

 

  잘 build 된다면, '다음' 버튼을 눌러서 초기화 코드를 추가할 것이다.

 

 

 

 

  Firebase에서 상단과 같은 코드를 추가하라고 제공했지만 @UIApplicationMain만 봐도 예전에 업로드 후 업데이트 되지 않았다는 것을 알 수 있다..^^.. 그래도 코드의 차이는 없기에..

 

 

  하단의 코드를 그대로 AppDelegate - didFinishLaunchingWithOptions에 넣으면 된다.

import UIKit
import FirebaseCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        return true
    }
    ...

 

 

 

 

 

2. Firebase Authentication 세팅하기

 좌측의 '빌드'  >>  'Authentication'을 클릭하여 유저 로그인 및 인증을 관리할 Authentication을 시작할 수 있다.

 

 

 

  Firebase에서 제공하는 로그인 제공업체에는 '카카오톡'이 없다.

그러므로 기본 제공업체의 '이메일/비밀번호'를 사용할 예정이다. (클릭하기)

이메일은 '카카오 아이디 이메일', 비밀번호는 '카카오 유저 고유 식별번호'를 사용할 것이다.

이것이 비밀번호 역할처럼 사용될 것..!

 

 

 

 

 

3. Firestore 세팅하기

 Authentication을 생성한것과 동일하게, 왼쪽에 있는 탭 리스트 중 'Firestore Database'를 눌러서 세팅한다.

 

 

  이 후, 생성한 Firestore의 규칙을 수정하지 않으면 콘솔창에 하단과 같은 에러를 받을 수 있으니.. 꼭 규칙을 수정해야한다. 

error: 'Permission denied: Missing or insufficient permissions.'

false로 되어있는 초록색 부분을 true로 변경하면 끝이다!

 

 

 

 

4. 로그인 및 회원가입 + Firestore에 유저 정보 저장하기 ( + 소스코드)

 

 Firebase로그인을 담당하는 AuthService라는 구조체를 하나 생성했다.

 

 로직은 간단하다.

 

  1. 유저가 로그인 버튼을 누른다.

  2. 카카오톡으로부터 해당 유저의 카카오톡 이메일, 카카오톡 닉네임, 카카오톡 고유 유저id를 받는다.
    (앱 로그인 id == 카카오톡 이메일   /   앱 로그인 비밀번호 == 카카오톡 고유 유저 id)
  3. 해당 유저가 이미 존재하는 유저인지 체크한다. (로그인 시도를 하면, 존재하지 않는 유저인 경우 에러가 발생한다. 이 때 회원가입 코드를 넣으면 된다)
    >>  이미 존재하는 유저인 경우: 로그인 진행   //   존재하지 않는 유저인 경우: 회원가입 진행

  4. 로그인 혹은 회원가입이 완료되면, UserDefaults에 '유저 이메일, 유저 닉네임, 유저 uid, 카카오 로그인 여부' 저장
  5. 모든 작업이 끝나면 completion 블럭에 이후 처리할 작업 정의하기
    (필자는 노티피케이션 센터에 로그인 된 것을 알리고, 해당 알림을 받는 객체들이 로그인 여부에 따라 다음 작업을 할 수 있도록 했다)
* Firebase 로그인을 담당하는 AuthService 구조체 *

import Foundation
import FirebaseAuth

// 필요한 유저 정보 캡슐화
struct AuthCredentials {
    let email: String
    let nickName: String
    let password: String
    let isKakaoLogin: Bool
}

typealias AuthDataResultCallback = (AuthDataResult?, Error?) -> Void
struct AuthService {

    /// 기존에 없는 새로운 유저 등록
    static func registerUser(userInfo credentials: AuthCredentials, completion: @escaping () -> Void) {
            Auth.auth().createUser(withEmail: credentials.email,
                                   password: credentials.password) { (result, error) in
                if let error = error {
                    print("AuthService ERROR : \(error.localizedDescription)")
                    return
                }
                guard let userUID = result?.user.uid else { return }
                
                // 유저 이메일에 해당되는 다큐먼트에 들어갈 컬렉션 형태의 데이터
                let data = ["email": credentials.email,
                            "nickName": credentials.nickName,
                            "isKakaoLogin": credentials.isKakaoLogin,
                            "uid": userUID] as [String : Any]
                
                // 'uses' 컬렉션에 유저 이메일을 id로 새로운 다큐먼트 생성 + 다큐먼트에 생성한 컬렉션 데이터 넣기
                Firestore.firestore().collection("users").document(credentials.email).setData(data) { error in
                    if let _ = error {
                        print("새로운 유저 생성하기 실해")
                        return
                    }
                    // UserDefaults에 로그인 된 유저 정보 저장
                    UserDefaultsManager.shared.setUserInfo(nickName: credentials.nickName,
                                                           email: credentials.email,
                                                           uid: userUID,
                                                           isKakaoLogin: credentials.isKakaoLogin)
                    completion()
                }
            }
        }
    
    /// 기존 유저 로그인
    static func logUserIn(withEmail email: String, password: String, completion: AuthDataResultCallback?) {
        Auth.auth().signIn(withEmail: email, password: password, completion: completion)
    }
    
}

 

* UserDefaults를 담당하는 구조체 *

struct UserDefaultsManager {
    
    static let shared = UserDefaultsManager()
    
    private init() { }
    
    func setUserInfo(nickName: String, email: String, uid: String, isKakaoLogin: Bool) {
        UserDefaults.standard.set(nickName, forKey: "name")
        UserDefaults.standard.set(email, forKey: "email")
        UserDefaults.standard.set(uid, forKey: "uid")
        UserDefaults.standard.set(isKakaoLogin, forKey: "isKakaoLogin")
    }
    
}

 

* 카카오톡 로그인 버튼에 연결된 target Action 함수 *

@objc private func kakaoLoginButtonTapped() {
        // 로그인 된 경우 -> 로그아웃
        // 로그인 안 된 경우 -> 로그인
        if UserApi.isKakaoTalkLoginAvailable() {
            UserApi.shared.loginWithKakaoTalk { [weak self] token, error in
                if let error = error {
                    print(error)
                    return
                }
                print("카카오 로그인 성공")
                _ = token
                // 로그인 된 카카오톡 정보 노티피케이션 센터에 등록 및 메뉴에 있는 프로필 세팅하기
                // firebase에 해당 카카오톡 아이디로 회원가입 유무 확인 후, 없으면 가입하고 있으면 로그인시키기
                self?.setFirebaseForKakaoTalkLogin()
            }
        }
        print("카카오 로그인 구현하기")
    }

 

  * 카카오톡 로그인 된 유저 정보로 Firebase에 로그인 및 회원가입 후, 완료되면 NotificationCenter에 알리는 메서드
     
    private func setFirebaseForKakaoTalkLogin() {
        UserApi.shared.me {[weak self] user, error in
            guard let user = user,
                  let email = user.kakaoAccount?.email,
                  let nickName = user.kakaoAccount?.profile?.nickname,
                  let password = user.id,
                  error == nil else {
                print(error!)
                return
            }
            
            let kakaoAuthCredentials = AuthCredentials(email: email,
                                                       nickName: nickName,
                                                       password: String(password),
                                                       isKakaoLogin: true)
            
            AuthService.logUserIn(withEmail: email, password: String(password)) { result, error in
            // 이미 존재하는 유저인지 확인 (로그인 실패: 유저 존재 X, 로그인 성공: 유저 존재 O)
                guard let result = result,
                      let email = result.user.email,
                      error == nil else { 
                      // 로그인 실패, 유저 존재 X
                    print("새로운 유저 회원가입 필요")
                    AuthService.registerUser(userInfo: kakaoAuthCredentials) {
                        
                        NotificationManager.postloginNotification(name: nickName,
                                                                  userEmail: email,
                                                                  profileImageURL: user.kakaoAccount?.profile?.profileImageUrl,
                                                                  isKakaoLogin: true)
                        self?.cancelButtonTapped()
                    }
                    return
                }
                // 로그인 성공, 유저 존재 O
                print("기존 존재하는 유저로 로그인하기")
                UserDefaultsManager.shared.setUserInfo(nickName: nickName,
                                                       email: email,
                                                       uid: result.user.uid,
                                                       isKakaoLogin: true)
                
                NotificationManager.postloginNotification(name: nickName,
                                                          userEmail: email,
                                                          profileImageURL: user.kakaoAccount?.profile?.profileImageUrl,
                                                          isKakaoLogin: true)
                self?.cancelButtonTapped()
            }
        }
    }

 

 

 

 

회원가입 된 유저들 목록

Authentication에 성공적으로 카카오톡 아이디를 이용해 로그인이 된 것을 확인할 수 있다.

 

 

 

회원가입 된 유저의 정보가 담긴 Cloud Firestore

추가적으로 필요한 유저의 정보들 또한 Firestore에 저장된것을 확인할 수 있다.

 

 

 

 

 

끝-

728x90