[iOS] Firebase Auth를 활용한 Google 로그인 with Combine

Zerom·2024년 5월 14일
0

iOS 정리

목록 보기
5/6
post-thumbnail

Firebase Auth, Google 로그인, Combine을 활용해 회원가입을 할 수 있도록 하는 Firebase Auth에 대해 기록하려 한다.

Firebase를 처음 사용해본다면 다음 포스팅을 참고해서 프로젝트를 생성한 다음 실행하면 된다.
Firebase 프로젝트 생성하는 방법

Firebase Auth 활성화

Firebase Auth를 추가하고자 하는 콘솔창으로 들어간 다음 좌측 메뉴에 '빌드' 하위 항목인 Authentication에 들어간 후 시작하기를 눌러준다.

여기서 어떤 방법을 이용해 로그인 할 수 있는지 체크해줄 수 있는데, 이번엔 Google 로그인에 대해 알아보는 것이기 때문에 Google 버튼을 눌러주도록 한다.
그런 다음 사용 설정을 토글해주고, 프로젝트 지원 이메일을 입력해주면 저장하기 버튼이 활성화되고, 저장해주면 된다. 여기까지만 해주면 우선 Firebase 콘솔에서 설정하는 부분은 끝이다. 이제 Xcode로 넘어가서 Google 로그인을 구현해보도록 하자. 아래에는 Firebase에서 제공하는 공식 문서인데 여기 들어가보면 Google 로그인을 하는 방법에 대해 자세히 나와있으니 아래 문서를 참고해봐도 좋다.
애플 플랫폼에서 Google 로그인 하기 관련 문서 링크

Firebase Auth Google 로그인 흐름 이해하기

Firebase Auth를 통해 Google 로그인을 할 때는 아래와 같은 흐름으로 로그인이 진행된다.
1. Firebase의 클라이언트 ID로 Google configuration object 생성
2. 해당 object를 활용해 Google 로그인 요청
3. Google 로그인 완료 시 Access Token, ID Token이 발행이 되고, 해당 정보를 토대로 credential(사용자 인증 정보) 생성
4. credentiall로 Firebase 로그인

사용 방법

가장 먼저 Google 로그인을 하기 위해서는 패키지를 추가해줘야 한다. Pakage Manager를 통해 https://github.com/firebase/firebase-ios-sdk 해당 SDK를 추가해주도록 하자.

1. URL Schemes 설정하기

우선 App Target에 들어간 후 Info 탭에서 URL Types를 추가해줘야 한다. +버튼을 눌러 추가한 후 아래 박스에 URL Schemes를 넣어주면 되는데 해당 값은 앞서 Firebase 에서 다운 받았던 GoogleService-info.plist 파일의 REVERSED_CLIENT_ID의 값을 복사해서 넣어주면 된다.

2. App Delegate에 관련 메서드 정의

그런 후 AppDelegate에 아래의 메서드를 정의해준다. 이 때 Firebase Core, Firebase Auth, GoogleSignIn을 Import 해줘야 한다.

import FirebaseAuth
import FirebaseCore
import GoogleSignIn

class AppDelegate: NSObject, UIApplicationDelegate {
	func application(_ app: UIApplication,
                 	open url: URL,
                 	options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
  	return GIDSignIn.sharedInstance.handle(url)
	}
}

3. User 구조체 정의

그 다음 회원에 대한 정보를 담아서 사용하기 위해 필요한 정보들을 가지고 있는 User를 정의하면 된다.

struct User: Identifiable {
    var id: String
    var name: String
}

우선 간단하게 식별을 위한 id와 이름만 정의를 했다. 여기서 추가적으로 필요한 정보가 있다면 더 추가해주면 된다.

4. Google 로그인 메서드 정의

다음으로 Google 로그인에 대한 메서드를 정의해주면 되는데 Google 로그인은 Combine을 제공하지 않는다. 그렇기 때문에 Completion Handler를 만들어 처리하고 완료 되었을 때 Future를 활용해 Publisher를 생성해줘야 이 후 Combine을 이용해 처리해줄 수 있다.

가장 먼저 흐름의 1번과 2번을 진행해서 Access Token과 ID Token을 발행하는 메서드부터 아래 주석을 참고해 만들어주도록 하자.

private func signInWithGoogle(completion: @escaping (Result<User, Error>) -> Void) {
	// Firebase clientID 받아오기
	guard let clientID = FirebaseApp.app()?.options.clientID else {
    	completion(.failure(AuthenticationError.clientIDError))
        return
    }
    
    // clientID를 바탕으로 Google Configuration object 생성
    let config = GIDConfiguration(clientID: clientID)
    GIDSignIn.sharedInstance.configuration = config
    
    // Configuration object로 Google 로그인 요청
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
          let window = windowScene.windows.first,
          let rootViewController = window.rootViewController else { return }
        
    GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) { [weak self] result, error in
            
        if let error {
            completion(.failure(error))
            return
        }
            
        // 로그인 성공 시 result에서 user와 ID Token 추출
        guard let user = result?.user, let idToken = user.idToken?.tokenString else {
            completion(.failure(AuthenticationError.tokenError))
            return
        }
            
        // user에서 Access Token 추출
        let accessToken = user.accessToken.tokenString
        
        // Token을 토대로 Credential(사용자 인증 정보) 생성
        let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
            
        // 생성한 Credential을 Firebase Auth 로그인 메서드에 전달
        // 해당 메서드는 해당 포스팅 아래에 정의되어 있음
        self?.authenticateUserWithFirebase(credential: credential, completion: completion)
    }
}

5. Firebase Auth 로그인 메서드 정의

다음으로는 Credential을 받아서 Firebase Auth에 로그인 하는 메서드를 정의한 후 위 메서드의 가장 아래줄에 넣어주면 된다.

private func authenticateUserWithFirebase(credential: AuthCredential, completion: @escaping (Result<User, Error>) -> Void) {
	// 전달받은 Credential로 Firebase Auth에 로그인
    Auth.auth().signIn(with: credential) { result, error in
        if let error {
            completion(.failure(error))
            return
        }
            
        guard let result else {
            completion(.failure(AuthenticationError.invalidated))
            return
        }
        
        // 로그인에 성공 시 result에서 user를 받을 수 있는데 
        // 해당 정보를 토대로 위에서 정의한 User Model 생성
        let firebaseUser = result.user
        let user: User = .init(id: firebaseUser.uid,
                               name: firebaseUser.displayName ?? "")
            
        completion(.success(user))
    }
}

6. 위에서 정의한 Completion Handler를 활용해 Publisher를 생성하는 메서드 정의

마지막으로 로그인 ViewModel에서 가져다 쓸 로그인 메서드를 만들어주면 되는데 Future를 활용해 Publisher를 생성해주면 된다.

func signInWithGoogle() -> AnyPublisher<User, ServiceError> {
    Future { [weak self] promiss in
        // Service 내부에서 사용하는 Completion Handler 메서드 활용
        self?.signInWithGoogle { result in
            switch result {
            case let .success(user):
                promiss(.success(user))
            case let .failure(error):
                promiss(.failure(.error(error)))
            }
        }
    }.eraseToAnyPublisher()
}

여기까지 완료한다면 Google Login 연동은 완료된다.
전체 코드는 Messenger Demo App 해당 프로젝트의 AuthenticationService에서 확인할 수 있다.

profile
꼼꼼한 iOS 개발자 /
Apple Developer Academy @ POSTECH 2기 / 멋쟁이사자처럼 앱스쿨 1기

0개의 댓글