안드로이드 앱 프로젝트를 진행하며 로그인 및 회원가입 API를 개발하기 전에 클라이언트에서 받을 수 있는 반환값을 확인하기 위하여 Kotlin을 이용해 구글 로그인을 구현해보았다.
xml과 kotlin 코드가 분리되어있고, gradle 빌드를 하는 점에서 프론트엔드 프레임워크인 Vue.js나 React보다는 백엔드 프레임워크인 SpringBoot와 비슷하게 느껴졌다.


0. 사전 작업

1) Android 프로젝트에 Firebase 추가

  • 안드로이드 스튜디오에서 프로젝트 생성
  • Firebase Console에 안드로이드 앱 등록
    • SHA-1 등록 : 터미널에 ./gradlew signingReport
    • app 루트 폴더에 google-services.json 추가
    • Firebase SDK 추가
      • 프로젝트 수준의 build.gradle.kts에 Google 서비스 Gradle 플러그인 추가
        • Firebase SDK가 google-services.json 구성 파일의 값에 액세스할 수 있도록
          plugins {
              // ...
              id("com.google.gms.google-services") version "4.4.0" apply false
          		// ...
          }
        
      • 모듈(앱) 수준 build.gradle.kts에 google-services 플러그인과 앱에서 사용할 Firebase SDK 추가

          plugins {
            id("com.android.application")
                    
            // Add the Google services Gradle plugin
            id("com.google.gms.google-services")
            ...
          }
                    
          dependencies {
            // Import the Firebase BoM
            implementation(platform("com.google.firebase:firebase-bom:32.4.0"))
                    
            // TODO: Add the dependencies for Firebase products you want to use
            // When using the BoM, don't specify versions in Firebase dependencies
            implementation("com.google.firebase:firebase-analytics-ktx")
                    
            // Add the dependencies for any other desired Firebase products
            // https://firebase.google.com/docs/android/setup#available-libraries
          }
        


2) 모듈(앱 수준) Gradle 파일에 라이브러리 추가

  • 라이브러리 버전 관리 제어 : Firebase Android BoM
  • Firebase 인증을 설정하는 과정에서 앱에 Google Play 서비스 SDK를 추가 필요
dependencies {
    // Import the BoM for the Firebase platform
    implementation(platform("com.google.firebase:firebase-bom:32.3.1"))

    // Add the dependency for the Firebase Authentication library
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation("com.google.firebase:firebase-auth-ktx")

    // Also add the dependency for the Google Play services library and specify its version
    implementation("com.google.android.gms:play-services-auth:20.7.0")
}


3) SHA 디지털 지문 지정하지 않았다면 설정


4) Firebase Console에서 Google을 로그인 방법으로 사용 설정

  • Firebase Console에서 인증 섹션 접속
  • 로그인 방법 > Google 로그인 방법 사용 설정 > 저장
  • 업데이트된 Firebase 구성 파일(google-services.json)을 다운로드 > 안드로이드 스튜디오 프로젝트에서 구성 파일을 교체



1. Firebase에 인증

1) Firebase 이용하여 Google Login

  • 서버 클라이언트 ID
    • ‘서버’ 클라이언트 ID(your_web_client_id) 확인 방법
    • 안드로이드 스튜디오의 New String Value Resource 메뉴에서 지정
  • Firebase로 사용자 정보 확인하기
  • 구현 코드 (참고자료)
    • MainActivity.kt

        package com.zeomzzz.google_front_kotlin3
              
        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.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 com.zeomzzz.google_front_kotlin3.databinding.ActivityMainBinding
        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
            private var uid: String = ""
              
            override fun onCreate(savedInstanceState: Bundle?) {
              
                Log.e(TAG, "onCreate started")
              
                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
                                    Log.e(TAG, "tokenId : $tokenId")
                                    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()
                                                    uid = user.uid
              
                                                    Log.e(TAG, "email : $email")
                                                    Log.e(TAG, "uid : $uid")
                                                    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)
                        }
                        Log.e(TAG, "로그인 합니다.")
                    }
              
                    googleLogoutButton.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)
              
                            googleSignInClient.signOut()
                            Log.e(TAG, "로그아웃 합니다.")
                        }
                    }
                }
            }
        }
      
    • activity_main.xml

        <?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"
                    android:text="구글 로그인"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintVertical_bias="0.405" />
              
                <Button
                    android:id="@+id/google_logout_button"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="구글 로그아웃"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.498"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintVertical_bias="0.547" />
              
            </androidx.constraintlayout.widget.ConstraintLayout>
              
        </layout>
      


🚨 AndroidManifest.xml에서 MainActivity.kt 인식 못하는 오류

  • 앱을 실행하자 마자 종료됨
  • 해결 방법 : MainActivity.kt에 package 이름 추가



참고 문서


1) 공식 문서


2) 그 외



Leave a comment