기본 요건
- Android 4.4 이상을 실행하며 Google Play 스토어 또는 Android 4.2.2 이상을 기반으로 하는 Google API 플랫폼을 실행하고 Google Play 서비스 버전 15.0.0 이상을 실행하는 AVD가 있는 에뮬레이터가 포함된 호환 Android 기기.
- Android 4.4(KitKat) 이상에서 컴파일하도록 구성된 프로젝트
Google Play 서비스 추가
먼저 프로젝트 수준 build.gradle 파일에 Google의 Maven 저장소가 포함되어 있는지 확인한다.
buildscript {
repositories {
google()
mavenCentral()
}
}
다음으로, 앱 수준 build.gradle 파일에 Google Play 서비스 dependency를 추가한다.
// Google Play services
implementation 'com.google.gms:google-services:4.3.15'
implementation 'com.google.firebase:firebase-auth:22.0.0'
implementation 'com.google.firebase:firebase-bom:32.0.0'
implementation 'com.google.android.gms:play-services-auth:20.5.0'
Google Cloud Console에서 프로젝트 생성 및 OAuth 2.0 클라이언트 ID 생성
1. Google Cloud Console에서 콘솔 프로젝트 생성
아래와 같이 Google Cloud Console에 들어가서 프로젝트 선택을 누른 뒤
SHA-1 해시값은 오른쪽 Gradle를 클릭 합니다.

gradle signingReport 를 입력하면 하단 Run 부분에서 확인할 수 있다.

SHA-1 해시값을 확인 할 수 있습니다.

3. OAuth 2.0 클라이언트 ID 가져오기
Google API Console에서 OAuth 2.0 클라이언트 ID로 사용자 인증 정보를 새로 만들어준다.


그럼 아래와 같이 클라이언트 ID를 얻을 수 있게 된다.

여기서 얻은 ID는 GoogleSignInOptions 객체를 만들 때 requestIdToken 또는 requestServerAuthCode 메서드에 전달해야 하는 클라이언트 ID이므로, 일단 string.xml 파일에 저장해두도록 한다.
Google 로그인 연동
1. Google 로그인 및 GoogleSignInClient 객체 구성
이제 Google Cloud Console 에서의 준비가 모두 끝났다.
로그인이 이루어지는 activity의 onCreate 메서드(혹은 fragment의 onCreateView 메서드)에서 앱에 필요한 사용자 데이터를 요청하도록 코드를 작성해보자.
전반적인 로직은 아래와 같다.
- 사용자의 ID와 기본 프로필 정보를 요청하기 위해 DEFAULT_SIGN_IN 매개변수를 사용하여 GoogleSignInOptions 객체를 만든다.
val googleSignInOption = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
requestServerAuthCode를 사용하여 앞서 가져온 백엔드 서버의 OAuth 2.0 클라이언트 ID를 넘기고 요청한다.
.requestServerAuthCode(getString(R.string.google_login_client_id))
requestEmail을 사용하면 사용자의 이메일 주소를 요청할 수 있고, requestScopes을 사용하면 Google API에 액세스하기 위해 추가 범위를 요청할 수 있다.
.requestEmail()
위 과정에서 지정한 옵션들을 이용해 GoogleSignInClient 객체를 만든다. (GoogleSignInOptions 를 넘겨줌)
private fun getGoogleClient(): GoogleSignInClient {
val googleSignInOption = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Scope("https://www.googleapis.com/auth/pubsub"))
.requestServerAuthCode(getString(R.string.google_login_client_id)) // string 파일에 저장해둔 client id 를 이용해 server authcode를 요청한다.
.requestEmail() // 이메일도 요청할 수 있다.
.build()
return GoogleSignIn.getClient(requireActivity(), googleSignInOption)
}
2번에서 넘기는 클라이언트 ID로는 서버측에서 사용자의 이메일과 이름 등을 얻어올 수 있다. 만약 Google API에 액세스하기 위해 여기서 추가적인 정보가 더 필요하다면 accessToken을 넘겨주면 된다.
그러나 최상의 사용자 환경을 제공하려면 처음에 사용자를 로그인 처리할 때 가능한 한 적은 수의 범위를 요청해야 한다. 일반적으로는 GoogleSignInOptions.DEFAULT_SIGN_IN 구성만 있으면 로그인에 충분하기에, requestScopes의 사용은 꼭 필요한지를 잘 따져보고 진행하는 것이 좋을 것이다.
2. 로그인 인텐트를 만들어 구글 로그인 진행
각자의 로그인 화면에서 구글 로그인 브랜드 가이드라인을 이용하여 버튼을 만든다.
registerForActivityResult를 사용해 버튼이 클릭되면 내부에 로그인 인텐트를 만들어 로그인이 진행되도록 한다.
private fun addListener() {
binding.clGoogleLogin.setOnClickListener { // 버튼 역할을 하는 clGoogleLogin에 클릭리스너를 달아준다.
requestGoogleLogin()
}
}
private fun requestGoogleLogin() {
googleSignInClient.signOut()
val signInIntent = googleSignInClient.signInIntent
googleAuthLauncher.launch(signInIntent)
}
완성
로그인 버튼을 클릭하면 구글 로그인을 위한 페이지로 화면이 넘어가고, 모든 정보를 입력하고 동의 절차를 거치고 나면 해당 계정을 통한 로그인을 마칠 수 있다.
전체적인 코드는 아래와 같다. (fragment에서 작업하였다.)
class SignFragment : Fragment() {
private lateinit var binding: FragmentSignBinding
private val viewModel: SignViewModel by activityViewModels()
private val googleSignInClient: GoogleSignInClient by lazy { getGoogleClient() }
private val googleAuthLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
try {
val account = task.getResult(ApiException::class.java)
// 이름, 이메일 등이 필요하다면 아래와 같이 account를 통해 각 메소드를 불러올 수 있다.
val userName = account.givenName
val serverAuth = account.serverAuthCode
moveSignUpActivity()
} catch (e: ApiException) {
Log.e(SignFragment::class.java.simpleName, e.stackTraceToString())
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentSignBinding.inflate(inflater, container, false)
addListener()
return binding.root
}
private fun addListener() {
binding.clGoogleLogin.setOnClickListener {
requestGoogleLogin()
}
}
private fun requestGoogleLogin() {
googleSignInClient.signOut()
val signInIntent = googleSignInClient.signInIntent
googleAuthLauncher.launch(signInIntent)
}
private fun getGoogleClient(): GoogleSignInClient {
val googleSignInOption = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Scope("https://www.googleapis.com/auth/pubsub"))
.requestServerAuthCode(getString(R.string.google_login_client_id)) // string 파일에 저장해둔 client id 를 이용해 server authcode를 요청한다.
.requestEmail() // 이메일도 요청할 수 있다.
.build()
return GoogleSignIn.getClient(requireActivity(), googleSignInOption)
}
private fun moveSignUpActivity() {
requireActivity().run {
startActivity(Intent(requireContext(), SignUpActivity::class.java))
finish()
}
}
}