니델바, Firebase 써 봤어요?

 

음, 아니. 분명 대학다닐 시절 언젠가 DB서버로 한 번 활용한 적이 있었을 것이다. 구글 모바일 플랫폼으로 굉장히 자주 쓰인다 들어는 봤지만 이렇게 빨리 경험하게 될 줄은. 유니티의 파이어베이스 연동과 구글 로그인까지 구현하며 했던 삽질을 기록해 둔다.

 

사실 이 프로젝트에는 이미 로그인시스템이 구현되어 있었다. 여타 앱에서 사용하는 페북, 구글, 애플, 그리고 본사 계정 로그인 방식까지 지원하는. 다만 이번에 프로젝트를 파이어베이스로 이전하게 된 가장 큰 이유는 Unity WebView로 구현된 로그인창이 계속 Google Auth Token을 못 받아온다는 문제가 있어서인것 같았다. DontDestroyOnLoad에 걸어둔 Singleton의 Instance가 자꾸 초기화가 된다나 뭐라나. 겸사겸사 더 좋은 플랫폼으로 이사도 할 겸, 주니어인 나에게 한번 구현해보라 시킨 것. 실패해도 프로젝트 진행엔 별 타격이 없을테니 말이다.

 

사실 문제가 하나 더 있었다. 단순 유니티와 파이어베이스의 연동은 구글에게 물어봐도 대답을 잘 해준다. 어렵게 없을거라 생각했다. 위에서 말했던 Google Auth Token을 받아오는 그 과정이 문제였다. 이것을 무슨 수로 따냐 하니, 대부분의 사람들은 Google GamePlay Service를 이용했다. 아니, 나는 Google GamePlay가 아니라 단순 Google E-Mail 로그인을 원했다고! 그것마져 별도의 Activity를 사용하는 안드로이드와 다르게 IOS엔 적용조차 불가능했다. 그래서 했다. 삽질을..

 

다시 한 번 적지만 이 글은 Google Play Console을 사용하는것이 아닌, 단순 Google 계정을 통한 연동에 관한 글이다.

 

 

 

1. Unity Project에서 Keystore 생성, SHA-1 인증서 지문 추출

 

기존 프로젝트라면 이미 Keystore쯤은 가지고 있었을 것이다. 있다면 그것을 쓰면 되고, 나는 새 프로젝트의 테스트 용도차 처음부터 세팅을 해야하니 Keystore를 가져오기로 한다.

<Android Switch Platform>

 

Player Settings - Publishing Settings - Keystore 파트에서 Keystore를 생성한다.

keystore의 생성, 그리고 파일 저장 위치를 설정 후 Password를 설정한다.

Alias 부분은 이 키의 식별자인데 프로젝트 이름으로 해도 좋고 아무렇게나 적는다.

Certificate는 이 인증에 사용할 개인정보를 입력하는데, 이 정보는 앱에 당연히 표시되지 않지만 apk 파일 안엔 일부로 들어간다고 한다. 나의 경우는 생략했다.

keystore가 정상적으로 만들어줬다면, cmd창에서 keystore 파일이 있는 위치로 디렉터리를 이동 후

keytool -list -v -keystore [keystore이름] 을 입력한다. SHA1 그리고 SHA256 인증서 fingerprint를 확인할 수 있다.

이 인증서 지문은 파이어베이스 프로젝트 세팅이나 API등 여러 곳에서 이용되니, 보안을 지켜 적절한 곳에 메모해두도록 하자.

 

아 참, keytool 명령어를 찾을 수 없는 경우엔 '시스템 환경 변수'의 path에 사용중인 jdk/bin 폴더를 추가해주면 된다.

 

 

2. Firebase Console 프로젝트 설정 (https://console.firebase.google.com/)

 

파이어베이스 콘솔로 접속해, 새로운 프로젝트를 생성한다.

이 Cloud System에 적당한 이름을 짓고

Google Analytics를 이용할 것인지 묻는다. 나중 api 설정에서도 다시 바꿀 수 있으므로, 나는 사용하지 않고 다음.

이예에~!!

 

Firebase Console - Project Settings에서

앱을 추가! 내 경우는 Android를 먼저 세팅했다.

 

Android Package Name, 그리고 앱 이름을 입력한 후 등록

 

구성파일 다운로드는 현재 Console에서의 프로젝트 설정이나 api값이 바뀌면 다시 다운로드해서 넣어줘야하기에 지금은 패스한다. 나머지도 지금은 패스하고, 앱 설정을 완료한다.

 

앱 설정이 완료되면, 위와같은 창에서 아까 기록해둔 SHA 인증서 지문을 추가한다. 나는 SHA1만 써놨는데, SHA256도 필요할지는 잘 모르겠다. 여기까지 하면, Firebase Console의 세팅은 끝이 난다.

 

 

 

3. Google Cloud Playform API 세팅 (https://console.cloud.google.com/)

 

파이어베이스 프로젝트 세팅이 되면, 해당 프로젝트의 ID와 번호를 그대로 승계받아 Google Cloud Playform의 프로젝트에서 찾을 수 있다.

 

만약 프로젝트가 보이지 않는다면 아까 파이어베이스에 만들어둔 프로젝트 이름으로 검색해 직접 등록한다

해당 프로젝트의 API - 사용자 인증 정보(Credential) 파트에서 OAuth Web Client에 대한 정보를 찾을 수 있다. 클릭하여 클라이언트 ID를 메모해 두자.

 

 

 

 

4. Unity Setting

 

몇 가지 준비물이 필요하다.

 

google-signin-plugin-1.0.4.unitypackage
0.52MB

본디 인증 Token을 얻는 과정에서 따로 서버와 통신하는 Script를 짜거나, 별도의 Activity를 여는 과정에서 삽질이 많을텐데 역시나 누가 만들어둔 오픈소스가 존재했다. 받아서 Import 시켜주자

 

그리고 Firebase 페이지에서 SDK를 준비한다. 유니티 2017버전 이상이라면 dotnet4 폴더의 FirebaseAuth.unitypackage를 Import 시켜준다. 만약 인증이 아니라 다른 기능이 필요하다면, 동봉된 별도의 unitypackage를 차곡차곡 심어주면 된다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Firebase;
using Firebase.Auth;
using Google;
using UnityEngine;
using UnityEngine.UI;
 
public class GoogleSignInDemo : MonoBehaviour
{
    public Text infoText;
    public string webClientId = "<your client id here>";
 
    private FirebaseAuth auth;
    private GoogleSignInConfiguration configuration;
 
    private void Awake()
    {
        configuration = new GoogleSignInConfiguration { WebClientId = webClientId, RequestEmail = true, RequestIdToken = true };
        CheckFirebaseDependencies();
    }
 
    private void CheckFirebaseDependencies()
    {
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
        {
            if (task.IsCompleted)
            {
                if (task.Result == DependencyStatus.Available)
                    auth = FirebaseAuth.DefaultInstance;
                else
                    AddToInformation("Could not resolve all Firebase dependencies: " + task.Result.ToString());
            }
            else
            {
                AddToInformation("Dependency check was not completed. Error : " + task.Exception.Message);
            }
        });
    }
 
    public void SignInWithGoogle() { OnSignIn(); }
    public void SignOutFromGoogle() { OnSignOut(); }
 
    private void OnSignIn()
    {
        GoogleSignIn.Configuration = configuration;
        GoogleSignIn.Configuration.UseGameSignIn = false;
        GoogleSignIn.Configuration.RequestIdToken = true;
        AddToInformation("Calling SignIn");
 
        GoogleSignIn.DefaultInstance.SignIn().ContinueWith(OnAuthenticationFinished);
    }
 
    private void OnSignOut()
    {
        AddToInformation("Calling SignOut");
        GoogleSignIn.DefaultInstance.SignOut();
    }
 
    public void OnDisconnect()
    {
        AddToInformation("Calling Disconnect");
        GoogleSignIn.DefaultInstance.Disconnect();
    }
 
    internal void OnAuthenticationFinished(Task<GoogleSignInUser> task)
    {
        if (task.IsFaulted)
        {
            using (IEnumerator<Exception> enumerator = task.Exception.InnerExceptions.GetEnumerator())
            {
                if (enumerator.MoveNext())
                {
                    GoogleSignIn.SignInException error = (GoogleSignIn.SignInException)enumerator.Current;
                    AddToInformation("Got Error: " + error.Status + " " + error.Message);
                }
                else
                {
                    AddToInformation("Got Unexpected Exception?!?" + task.Exception);
                }
            }
        }
        else if (task.IsCanceled)
        {
            AddToInformation("Canceled");
        }
        else
        {
            AddToInformation("Welcome: " + task.Result.DisplayName + "!");
            AddToInformation("Email = " + task.Result.Email);
            AddToInformation("Google ID Token = " + task.Result.IdToken);
            AddToInformation("Email = " + task.Result.Email);
            SignInWithGoogleOnFirebase(task.Result.IdToken);
        }
    }
 
    private void SignInWithGoogleOnFirebase(string idToken)
    {
        Credential credential = GoogleAuthProvider.GetCredential(idToken, null);
 
        auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
        {
            AggregateException ex = task.Exception;
            if (ex != null)
            {
                if (ex.InnerExceptions[0is FirebaseException inner && (inner.ErrorCode != 0))
                    AddToInformation("\nError code = " + inner.ErrorCode + " Message = " + inner.Message);
            }
            else
            {
                AddToInformation("Sign In Successful.");
            }
        });
    }
 
    public void OnSignInSilently()
    {
        GoogleSignIn.Configuration = configuration;
        GoogleSignIn.Configuration.UseGameSignIn = false;
        GoogleSignIn.Configuration.RequestIdToken = true;
        AddToInformation("Calling SignIn Silently");
 
        GoogleSignIn.DefaultInstance.SignInSilently().ContinueWith(OnAuthenticationFinished);
    }
 
    public void OnGamesSignIn()
    {
        GoogleSignIn.Configuration = configuration;
        GoogleSignIn.Configuration.UseGameSignIn = true;
        GoogleSignIn.Configuration.RequestIdToken = false;
 
        AddToInformation("Calling Games SignIn");
 
        GoogleSignIn.DefaultInstance.SignIn().ContinueWith(OnAuthenticationFinished);
    }
 
    private void AddToInformation(string str) { infoText.text += "\n" + str; }
}
cs

 

DemoScript를 작성한다. 메소드별로 굵직굵직하게 OOP로 나뉘었으니 SignIn, Out, 그리고 Disconnect 정도를 할당해 쓰면 되게끔 만들었다. 그리고 추후 GooglePlay와의 연동 확장성을 생각해서 GoogleSignIn.Configuration.UseGameSignIn 변수를 사용해서도 하나 만들었으니 나중에 이용할 수 있도록 했다.

 

그렇게 대충 Canvas에 쭉쭉 긋고 프로토타입 빌드 후, 앱 내에서 테스트 진행.

 

구글 ID Token값을 정상적으로 받아오고

 

파이어베이스 Authentication에서도 정상적으로 로그인 기록이 남는걸 확인할 수 있다.

이렇게 구현 완료!!!!

 

 

 

-----추가

위에서 사용한 그 오픈소스의 경우, 별도의 구글 로그인창을 띄우기 위해 AndroidJavaObject의 Activity를 활용한다.

IOS의 경우는 그대로 zero를 return해버리는데, 이에 대한 대응이 되어있는지 몰라 직접 물어봤다. 지금은 재택근무 중이라 ios빌드테스트를 위한 환경도 셋팅되어있지 않아 답답한데.. ㅠㅠ

 

돌아온 개발자의 답변

답변을 받았다. '직접 테스트해보셈. 아마 될거임. 나 사과폰도 없어 ㅋ'

ios쪽에서 Activity를 별도로 만들수 있는 방법을 알아보고, 진전이 있다면 다음 포스팅에서.

 

----------------

참고

[Unity 이론] Google + Firebase 로그인 (https://inyongs.tistory.com/35)

Firebase Authentication in unity with Google Provider (https://youtu.be/pqJLHWFGhH4)

+ Recent posts