본 포스팅은 이하 버전을 기준으로 작성되었습니다. · Swift 5.2.4 · Firebase 6.29.0

Intro

이번에는 Google 아이디로 로그인할 수 있도록 구현해볼거에요. Apple 로그인은 Xcode 에서 Native 로 지원이 되지만 Google 로그인은 그렇지 않기 때문에 Firebase 를 사용해야 해서 조금 더 복잡한 편입니다. 그래도 천천히 따라해보시면 문제없을 거에요!

CocoaPods 설치

만약에 여러분이 CocoaPods 를 설치한 적이 없다면 Xcode 프로젝트에 Firebase 를 연동하기 위해 CocoaPods 가 우선적으로 설치되어 있어야합니다. 설치 방법은 CocoaPods - 설치 및 라이브러리 적용하기 포스팅을 참고해주세요.

Importing Firebase

기초적인 Firebase 와 Xcode 의 연동방법은 Firebase - Xcode 프로젝트와 연동하기 포스팅을 통해 공부했었습니다. 그래서 이번 포스팅에서는 지난 글과 달라지는 부분에 대해서만 공부해보도록 할게요. Firebase 연동과정에서 Pod 를 설치하라는 메세지가 나오는 부분에까지 진행해주세요.


Pod Libraries 설치하기

Terminal 에서 Xcode 에 Firebase 파일 설치를 위해 터미널에서 내 프로젝트가 있는 폴더 내부로 이동해주세요. 이동을 완료했다면 pod init 을 진행해주세요. 그러면 새롭게 Podfile 이 생성되었을 거에요.

Terminal 에서 pod init

Podfile 을 열고 아래처럼 입력해주세요. 우리가 입력한 부분은 총 3가지에요.

  • pod ‘Firebase/Analytics’
  • pod ‘Firebase/Auth’
  • pod ‘GoogleSignIn’

이 중 첫번째 Firebase/Analytics 는 우리가 처음에 프로젝트 설정할 때 Analytics 를 등록했으니까 설치 해주는 것이고 Google 로그인에 필수 항목은 아닙니다. 혹시 체크 해제하고 진행하신 분들은 첫번째 항목은 설치할 필요가 없어요~

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'SignInWithGoogle_Example' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for SignInWithGoogle_Example
  pod 'Firebase/Analytics'
  pod 'Firebase/Auth'
  pod 'GoogleSignIn'

end

Podfile 수정 후 꼭 저장하고 다시 터미널로 돌아가서 pod install 을 진행해 주세요. 인스톨을 마치고 폴더를 열어보면 기존 Xcode 프로젝트 파일과는 조금 다른 흰색 .workspace 파일이 생성되어 있습니다. 앞으로 우리는 기존 파일이 아닌 이 파일에서 코드 작업을 진행할 거에요. 그럼 기존 파일을 종료하고 새로운 파일을 열어볼게요.

Finder 의 workspace 파일

파일을 실행하고 Firebase 홈페이지로 돌아가 Next 를 누르면 다음과 같은 화면을 볼 수 있습니다.

Firebase 홈페이지 AppDelegate 화면

먼저 AppDelegate 파일에서 FirebaseGoogleSignIn 을 import 해주도록 합시다.

import Firebase
import GoogleSignIn

그 다음에는 기본제공 함수 중 첫번째 내부에 FirebaseApp.configure() 함수를 입력해 주세요.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure()
    return true
}

마지막으로 ViewController 로 돌아가 다시 한번 FirebaseGoogleSignIn 을 import 해줍니다. 이제 몇 가지 설정만 더하면 됩니다 ㅎㅎ

다시 Firebase 로 돌아가 Next 를 누르면 마지막으로 아래와 같은 화면이 뜰텐데 Xcode 에서 시뮬레이터를 실행하고 잠시 기다리시면 Continue to console 버튼이 활성화 될거에요. 만약에 계속 활성화되지 않으면 Skip this step 버튼으로 그냥 넘어가주세요.

Firebase Verification

자 다들 이제 이렇게 생긴 Firebase 프로젝트 내의 메인화면으로 오셨죠?

Firebase 프로젝트 메인화면

여기서 좌측의 Authentication 버튼을 누르고 Sign-in method 탭을 클릭합니다. 리스트에서 Google 을 선택하고 Enabled 로 바꿔주세요. 참고로 상태를 바꾸려면 이메일을 반드시 선택해야 합니다.

Firebase Google 활성화


Google 로그인 프로세스 구현하기

이제 Firebase 홈페이지에서 세팅은 모두 마쳤습니다. 다음으로는 Xcode 로 다시 돌아와 GoogleService-Info.plist 파일의 REVERSED_CLIENT_ID 의 항목을 복사합니다.

Xcode GoogleService-Info.plist

복사가 되었으면 좌측의 Navigator 에서 프로젝트 파일을 클릭하고 Info 탭으로 들어가보면 하단에 URL Types 가 있습니다. 이 곳을 펼치고 + 버튼을 눌러 URL Schemes 칸에 복사한 REVERSED_CLIENT_ID 값을 붙여넣어 주세요.

Xcode Info Tab

다음으로는 이제 GIDSignInDelegateAppDelegate 로 돌아가 채택해주어야 합니다.

class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate

그리고 GIDSignInDelegate 의 필수구현 method 인 sign() 도 함께 구현해줍니다. 함수의 내부 코드는 다른 부분부터 먼저 끝내고 마지막에 입력해주도록 할게요.

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {

}

이제부터 우리는 GIDSignIn 이라는 인스턴스를 사용할 수 있게되는데 이것은 사용자가 구글 아이디로 로그인할 수 있도록 도와주는 인스턴스입니다.

GIDSignIn Summary

다음으로 기본제공 함수인 application:didFinishLaunchingWithOptions: 안에 우리가 조금 전 작성해놓은 FirebaseApp.configure() 코드 아래 다음 내용을 작성합니다. 두번째 줄은 우리가 프로토콜을 채택했으니 당연히 delegate 설정을 해주어야 하는 부분이고, 첫번째 줄은 sign-in method 가 동작하기 위해 반드시 필요한 코드라고하네요. 그냥 외워버리거나 자주 안쓴다면 필요할 때마다 찾아서 쓰는걸로…

GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self

추가적으로 application:openURL:options: 함수를 구현하고 내부에 handle() method 를 구현합니다. 이 method 는 GIDSignIn 인스턴스의 handleURL method 를 호출하여 인증 프로세스가 끝날 때 애플리케이션이 수신하는 URL을 적절히 처리해준다고 합니다. 인증 프로세스의 결과에 따라 다른 URL 을 받는건지 어쩐건지… 알 수 없지만 아무튼 해야한다니까 그냥 구현해줍니다.

@available(iOS 9.0, *)
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    return GIDSignIn.sharedInstance().handle(url)
}

위 함수는 대놓고 iOS 9.0 이상에서만 구현이 된다고 써있잖아요? 만약에 거의 쓸 일은 없겠지만 정말 만약에 iOS 8.0 이상부터 지원해야할 일이 있다면 다음 함수도 구현해주세요.

func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
    return GIDSignIn.sharedInstance().handle(url)
}

그리고 사용자가 앱에서 disconnect 되었을 때 호출되는 함수인 sign:didDisconnectWith 함수도 구현해 줍니다. 우리는 지금 Google 로그인을 구현하는 것이 목적이므로, 일단 함수를 구현만 하고 이곳에는 코드를 따로 작성하지 않겠습니다. 앱이 disconnect 되었을 때 추가적인 action 이 필요하다면 이곳에 작성해주세요.

func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
    // Perform any operations when the user disconnects from app here.
}

마지막으로 우리가 초반에 작성해 놓았던 필수구현 함수 sign() 의 내부 코드를 작성해볼게요. 에러가 날 경우 print 와 함께 함수가 종료되도록했고, 성공적으로 로그인 했을 경우에는 credential 이 print 될 수 있도록 작성했습니다. 여러분은 이곳에서 로그인 결과에 따라 원하는 ViewController 로 이동하거나 하는 등의 액션을 추가한다거나 하는 코드를 입력해봐도 좋을 것 같아요 ㅎㅎ

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
    if let error = error {
        print(error)
        return
    }

    guard let authentication = user.authentication else { return }
    let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                   accessToken: authentication.accessToken)
    print(credential.provider)
}

이제 거의 다 끝났으니까 조금만 더 힘내서 마무리 해볼게요!!


Google 로그인 버튼 생성하기

드디어 이것으로 AppDelegate 에서 작성할 코드는 모두 끝났고 이제 ViewController 로 넘어가서 나머지 작업과 버튼을 추가해서 실제로 Google 로그인이 동작하는지 확인해보도록 할게요. 거의 다 왔으니까 조금만 더 힘내봅시다!!

먼저 viewDidLoad() 에 다음 코드를 작성해볼게요. 첫번째 줄의 코드는 GIDSignIn 객체의 프레젠테이션 뷰 컨트롤러를 설정하고(Google 로그인 안내창을 어떤 뷰 컨트롤러에 띄울지 설정), 두번째 줄의 코드는 가능한 경우 자동으로 로그인을 할 수 있도록 도와주는 코드입니다. 두번째 줄의 코드는 자동 로그인이 필요하지 않다면 구현해주지 않아도 무방합니다.

GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()

다음으로 Google 로그인 버튼을 생성하고 View 의 가운데에 위치하도록 배치해 보겠습니다. 당연히 일반 버튼을 사용하면 안되고 GIDSignInButton() 을 상속받아 사용해야 합니다.

let googleLoginButton = GIDSignInButton()

ViewController 에 적힌 코드는 아래와 같습니다. 우리가 작업을 미리 다른 곳에서 많이 해둬서인지 Controller 에서 구현할 부분은 많지 않네요 ㅎㅎ

import UIKit
import Firebase
import GoogleSignIn

class ViewController: UIViewController {

    let googleLoginButton = GIDSignInButton()

    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
        GIDSignIn.sharedInstance()?.presentingViewController = self
    }

    private func configureUI() {
        view.addSubview(googleLoginButton)
        googleLoginButton.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            googleLoginButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            googleLoginButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }
}

이제 실행시켜보면 버튼이 보이고 눌러보면 구글 로그인하는 화면이 보일거에요~ 해냈다!!

Simulator GoogleSignIn


Firebase 에 로그인 데이터 저장하기

하지만 아직 끝이 아니에요. 사용자가 정상적으로 로그인 했을 때 그 정보를 우리가 저장해야하니까요. AppDelegate 로 돌아가 sign:didSignInFor 함수에 Auth.auth().signIn(with: credential) method 를 작성해야 합니다.

Auth 함수에서 else 쪽으로 빠지면 Firebase 에 로그인 데이터가 저장되게 됩니다. 가져올 수 있는 데이터는 사용자의 email 주소가 전부인 것 같네요.

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
    // ...
    if let error = error {
        print(error)
        return
    }

    guard let authentication = user.authentication else { return }
    let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                   accessToken: authentication.accessToken)
    Auth.auth().signIn(with: credential) { (authResult, error) in
        if let error = error {
            print(error.localizedDescription)
        } else {
            print("Login Successful")
        }
    }
}

마지막으로 로그아웃을 도와주는 method 입니다. 로그인을 했으니까 당연히 로그아웃이 있어야하잖아요? ㅎㅎ 근데 오늘은 구현하지 않을거에요. 그냥 버튼하나 만들어서 @objc method 로 넘겨주면 잘 작동될겁니다!

let firebaseAuth = Auth.auth()
do {
    try firebaseAuth.signOut()
} catch let signOutError as NSError {
    print ("Error signing out: %@", signOutError)
}

이번 포스팅은 내용이 정말 많이 길어졌네요. 여기까지 따라오신 분들 정말 수고많으셨습니다!! 그럼 이만~