관리 메뉴

나만을 위한 블로그

[Android] 코틀린으로 구글 로그인 구현하기 본문

Android

[Android] 코틀린으로 구글 로그인 구현하기

참깨빵위에참깨빵 2022. 6. 9. 02:28
728x90
반응형

※ 이 포스팅은 윈도우 기준으로 작성됐다.

 

안드로이드 스튜디오 버전은 이전 포스팅인 네이버 로그인 구현과 같다.

 

구글 로그인은 파이어베이스를 이용해서 진행한다. 그래서 사용해야 하는 사이트는 크게 2가지다.

 

  1. 파이어베이스 콘솔
  2. 구글 클라우드 플랫폼

 

구글 클라우드 플랫폼은 OAuth 설정을 위해서 사용한다. 그리고 주의할 것은 FCM까지 붙일 프로젝트라면 프로젝트 패키지명의 example을 다른 문자열로 바꾼다. 이 포스팅은 예제기 때문에 example인 상태로 진행한다.

먼저 파이어베이스 콘솔에 접속해서 프로젝트를 하나 판다.

 

https://console.firebase.google.com/u/0/?hl=ko 

 

로그인 - Google 계정

하나의 계정으로 모든 Google 서비스를 Google 계정으로 로그인

accounts.google.com

 

구글 로그인한 후 위 링크로 들어가면 파란색의 프로젝트 추가 글자가 보일 것이다. 이걸 클릭해서 적당한 이름의 프로젝트를 만들겠다.

 

 

테스트기 때문에 이렇게 해서 진행했다. 설정값은 자신에 맞게 알아서 바꾸자.

프로젝트 만들기를 클릭하면 잠깐의 로딩 후 프로젝트 생성이 완료된다.

 

 

계속을 누르면 자동으로 생성한 프로젝트의 개요 화면으로 이동한다.

 

 

이제 안드로이드 아이콘을 눌러서 앱 등록을 해야 한다. 난 이전 포스팅인 네이버 로그인 구현에서 만들어 사용했던 프로젝트를 그대로 사용할 것이다.

 

 

문제는 디버그용 SHA-1 키다. ArcticFox 이전 버전에서는 프로젝트 우측의 Gradle을 누르면 확인할 수 있었지만 이제는 확인할 수 없다.

그러나 방법이 없는 건 아니다. 아래 링크를 따라하면 된다.

 

https://onlyfor-me-blog.tistory.com/496

 

[Android] 안드로이드 스튜디오 범블비에서 SHA-1 키 빨리 확인하는 법

오른쪽 위에 보면 세로로 Gradle이라고 써진 탭이 있다. 이걸 클릭한다. 그리고 왼쪽 위의 코끼리 아이콘을 누른다. 그러면 아래와 비슷한 화면이 나올 것이다. 저 상태에서 signingReport라고 치면 로

onlyfor-me-blog.tistory.com

 

그러면 cmd에 "SHA1: 2D:01:D1..." 식으로 SHA-1 키가 나타난다. 이걸 복사해서 파이어베이스 콘솔로 돌아온 다음 붙여넣는다. 이후 앱 등록을 누르면 google-services.json 파일을 다운받으라고 한다. 다운받아서 넣으라는 곳에 넣어준다.

 

 

그리고 필요한 의존성들을 붙여넣으라고 한다. 역시 붙여넣으란 곳에 붙여넣는다.

여기서 문제는 프로젝트 수준 gradle 파일에 붙여넣어야 할 의존성이다.

 

 

이렇게 넣으라는데 범블비 이상 안드로이드 스튜디오라면 이렇게 나올 것이다.

 

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '7.1.3' apply false
    id 'com.android.library' version '7.1.3' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

 

그냥 이렇게 하면 된다.

 

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.8'
    }
}

plugins {
    id 'com.android.application' version '7.1.3' apply false
    id 'com.android.library' version '7.1.3' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

 

주의할 것은 위 사진에선 4.3.10 버전을 넣으라고 했지만 난 4.3.8을 넣었다.

왜냐면 구글 로그인 시 "R.string.default_web_client_id" 라는 문자열을 사용해야 하는데, 4.3.10 에선 이 문자열을 인식할 수 없는 에러가 발생할 수 있다. 4.3.8로 버전을 낮추면 정상 작동하니 4.3.8로 진행한다. 버전을 낮춘다고 FCM 관련 문제도 발생하지 않는다.

이후 앱 수준 gradle에 나머지 의존성을 복붙해준다. 반드시 코틀린을 선택한 후 나타나는 의존성들을 붙여넣기한다. 아래에서 2번째 줄까지는 내가 임의로 추가한 의존성이다.

 

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.google.gms.google-services' // <- 이 문장만 추가한다
}

.
.
.

implementation platform('com.google.firebase:firebase-bom:30.0.1')
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.android.gms:play-services-auth:20.2.0'
implementation 'com.google.firebase:firebase-auth-ktx:21.0.4'

 

이제 설정은 끝났다. 다음 버튼을 누르면 콘솔로 이동을 누르면 파이어베이스 콘솔 프로젝트 밑에 방금 등록한 앱 이름이 표시되는 게 보인다.

 

 

그리고 왼쪽의 메뉴 중 빌드의 "Authentication"을 클릭해서 시작하기를 누른다.

 

 

그러면 뭔가 선택할 수 있는 공간이 보인다.

 

 

구글 로그인을 구현해야 하니 구글을 선택한다.

 

 

사용 설정을 눌러서 설정하면 아래 화면이 나온다.

 

 

프로젝트 지원 이메일은 현재 로그인한 구글 계정의 이메일이 나오니 그걸 선택하면 된다. 저장을 누르면 구글이 정상적으로 추가된 걸 볼 수 있다.

 

 

이제 안드로이드 스튜디오 프로젝트로 돌아와서 Build를 눌러 Clean Project -> Rebuild project 순으로 눌러 프로젝트를 리빌드해준다. 왜냐면 방금 프로젝트에 추가한 google-services.json 파일을 프로젝트가 인식시키게 하기 위함이다. 이렇게 하지 않으면 default_web_client_id를 사용할 수 없으니 꼭 해준다.

 

그런데 이 때 계정 선택 후 팝업창이 사라지면서 RESULT_CANCELED가 나오고 이후 진행되지 않을 수도 있다.

왜 이런 에러가 발생하는지 찾아보니 스택오버플로우 등에서 말하는 해결법은 아래와 같았다.

 

  • 파이어베이스 콘솔에 SHA-1 키를 제대로 입력했는가?
  • 패키지명(com.example.xxx)을 제대로 입력했는가?
  • 올바른 클라이언트 ID를 썼는가?

 

나도 이 에러가 발생해서 확인해보니 SHA-1 키를 잘못 입력했었다. 그래서 위의 SHA-1 키 포스팅의 내용대로 키를 찾아서 파이어베이스 콘솔에 등록했다. 기존의 SHA-1 키를 삭제 후 재등록할 경우 변경완료까지 5분 좀 넘게 걸릴 수도 있으니 좀 기다려야 한다. 재등록했다고 바로 구글 로그인이 작동하진 않는다.

 

3번째 방법의 경우 구글 클라우드 플랫폼에서 해당 프로젝트로 들어가면 OAuth 2.0 설정을 한 경우 아래와 같이 나오는데, 이 중 OAuth 2.0 클라이언트 ID 밑의 "Web Client"를 클릭한다.

 

 

그러면 오른쪽 위에 클라이언트 ID와 그 옆의 문자열이 보일 것이다. 이 문자열을 복사해서 requestIdToken()의 매개변수로 넣으면 RESULT_CANCELED 에러가 해결될 수 있다. 아래 사진의 빨간 사각형 오른쪽에 있다.

 

 

그렇게 해서 완성한 메인 액티비티 코틀린 코드는 아래와 같다. 난 위에서 복사한 문자열을 requestIdToken()의 매개변수로 넣어도 작동하고 R.string.default_web_client_id를 넣어도 작동하는 걸 확인했다.

 

이제 액티비티 코드를 만들자. 화면엔 로그인을 진행해야 하니까 버튼 하나 만들었다.

특이한 거라면 데이터바인딩을 쓸 거라서 <layout>으로 감싼 것 정도다.

 

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/google_login_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:text="구글 로그인"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

 

그리고 코틀린 파일도 아래처럼 작성한다.

import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.googleloginpractice.databinding.ActivityMainBinding
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.firebase.auth.AuthCredential
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.auth.GoogleAuthProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val TAG = this.javaClass.simpleName

    private lateinit var launcher: ActivityResultLauncher<Intent>
    private lateinit var firebaseAuth: FirebaseAuth

    private var email: String = ""
    private var tokenId: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        firebaseAuth = FirebaseAuth.getInstance()
        launcher = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult(), ActivityResultCallback { result ->
                Log.e(TAG, "resultCode : ${result.resultCode}")
                Log.e(TAG, "result : $result")
                if (result.resultCode == RESULT_OK) {
                    val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
                    try {
                        task.getResult(ApiException::class.java)?.let { account ->
                            tokenId = account.idToken
                            if (tokenId != null && tokenId != "") {
                                val credential: AuthCredential = GoogleAuthProvider.getCredential(account.idToken, null)
                                firebaseAuth.signInWithCredential(credential)
                                    .addOnCompleteListener {
                                        if (firebaseAuth.currentUser != null) {
                                            val user: FirebaseUser = firebaseAuth.currentUser!!
                                            email = user.email.toString()
                                            Log.e(TAG, "email : $email")
                                            val googleSignInToken = account.idToken ?: ""
                                            if (googleSignInToken != "") {
                                                Log.e(TAG, "googleSignInToken : $googleSignInToken")
                                            } else {
                                                Log.e(TAG, "googleSignInToken이 null")
                                            }
                                        }
                                    }
                            }
                        } ?: throw Exception()
                    }   catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
            })

        binding.run {
            googleLoginButton.setOnClickListener {
                CoroutineScope(Dispatchers.IO).launch {
                    val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                        .requestIdToken(getString(R.string.default_web_client_id))
                        .requestEmail()
                        .build()
                    val googleSignInClient = GoogleSignIn.getClient(this@MainActivity, gso)
                    val signInIntent: Intent = googleSignInClient.signInIntent
                    launcher.launch(signInIntent)
                }
            }
        }
    }
}

 

이렇게 하고 앱을 빌드하면 로그인 성공 시 아래와 같은 로그가 찍힐 것이다. googleSignInToken 옆에도 긴 문자열이 찍혔지만 너무 길어서 그 밑의 줄에 찍혔기 때문에 생략했다.

 

 

이 로그가 찍혔다면 구글 로그인 구현에 성공한 것이다.

반응형
Comments