컴포즈로 구글 로그인 진행하기 (with. Dagger Hilt)

GongBaek·2024년 7월 19일

구글 소셜 로그인

요즘 사용하는 안드로이드 애플리케이션에서는 소셜 로그인을 통한 간편한 사용자 인증이 UX 개선에 빼놓을 수 없는 요소입니다. 😥
이번 앱잼을 진행하면서 Jetpack Compose를 사용하여 안드로이드 애플리케이션에서 구글 소셜 로그인을 구현하는 과정에서 생겼던 문제와 과정을 풀어보고자 합니다.

우선 build-logic에서 의존성을 추가하는 과정부터 시작해야겠죠? 👍

소소하고 당연한 준비물

  • 최신 버전의 Android Studio
  • Google Cloud Platform에 등록된 프로젝트
  • Jetpack Compose 기본 지식

클라이언트 ID 생성

구글 소셜 로그인을 사용하기 위해서는 먼저 Google Cloud Platform에서 OAuth 2.0 클라이언트 ID를 생성해야 합니다.

  1. Google Cloud Console에 접속하여 프로젝트를 생성합니다.
  2. API 및 서비스 > 사용자 인증 정보로 이동하여 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 선택합니다.
  3. 애플리케이션 유형을 웹 애플리케이션으로 선택하고 추가합니다.
  4. 생성된 클라이언트 ID를 야무지게 앱애 숨겨줍니다.

의존성 추가

// GoogleAuthModule.kt
google-play-services-auth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "googlePlayService" }

위와 같은 의존성을 야무지게 추가해줍니다.
해당 게시글에서는 21.1.0 버전을 사용했습니다.

버전이 다르면 호환성에 문제가 생길 수도 있으니 참고해 주세요!

제 경우 같은 경우에는
위와 같이 Deprecated라고 떠서 정말 슬펐습니다 🥲

공식문서에 가면 구글 소셜로그인을 위한 다양한 방법을 찾아볼 수 있었는데요,
당장 여유가 없어서 흔하게 널린 play services를 사용하는 방식을 도입했습니다.

저는 기존의 방식에 조금 변주를 주어서 Hilt를 사용하면서 Data 로직을 분리하는 과정을 di에 다음과 같이 넣었습니다. 😎

GCP에서 받은 클라이언트 ID를 보안을 위해 BuildConfig에 넣어줍니다.
Android 유형이나 웹 애플리케이션 유형을 사용할 수 있는데, 저희는 팀 서버가 존재하니 웹 애플리케이션을 거쳐서 로그인하는 방식을 택했습니다.

구글 클라이언트 ID를 local.properties에서 받아오고,
해당 서비스를 SingletonComponent를 통해 주입하는 과정을 가졌습니다.

.requestServerAuthCode 가 구글 로그인 코드를 가져오는 옵션이고,
BuildConfig.GOOGLE_CLIENT_ID 가 클라이언트 아이디를 가져왔습니다.

힐트 모듈 생성

@Module
@InstallIn(SingletonComponent::class)
object GoogleAuthModule {

    @Provides
    @Singleton
    fun provideGoogleSignInOptions(): GoogleSignInOptions {
        return GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestServerAuthCode(BuildConfig.GOOGLE_CLIENT_ID)
            .requestEmail()
            .build()
    }

    @Provides
    @Singleton
    fun provideGoogleSignInClient(@ApplicationContext context: Context, options: GoogleSignInOptions): GoogleSignInClient {
        return GoogleSignIn.getClient(context, options)
    }
}

Screen 코드

로그인을 사용하는 Screen에서 구글 로그인 창을 키기 위한 런처를 선언해줍니다.

    val signInLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
            viewModel.performGoogleSignIn(task)
        }
    }

viewModel 연결

위와 같이 선언해주고 viewModel과 연결합니다.

    fun performGoogleSignIn(result: Task<GoogleSignInAccount>) {
        viewModelScope.launch {
            runCatching {
                result.getResult(ApiException::class.java)
            }.onSuccess { account ->
                signIn(
                    SocialSignInPlatform.GOOGLE.name,
                    null,
                    account.serverAuthCode.toString(),
                )
            }.onFailure {
                Log.e("123123", it.message.toString())
            }
        }
    }

로그인을 진행하는 과정은 UI가 알아서는 안된다고 생각해
viewModel과 연결해두었습니다.
그리고 해당 함수를 버튼을 누르면 실행해주면 됩니다.

다시 Screen

onClick = {
                    signInLauncher.launch(viewModel.googleSignInClient.signInIntent)
                },

그러면 정상적으로 로그인이 진행되는 모습을 볼 수 있습니다.

이렇게 로그인 코드를 받고 팀 서버로 보낸 다음,
정상적인 응답이 오면 그때부터 JWT 토큰을 관리합니다.

이 부분은 DataStore로 진행했는데, 이 부분에 대해서도 추후 아티클로 써보겠습니다 😜

트러블슈팅

사실 Screen에서 진행하는 과정에 굉장히 많은 트러블이 있었는데요,
자꾸 로그인이 완료되지 않고 계속 Cancel되었다는 Result가 나오는 것이었습니다.

여기에는 몇 가지 이유를 살펴볼 수 있습니다.

  • GCP의 설정이 잘 되었는지 확인한다.
    - 웹 애플리케이션이라 안되는줄 알았지만, 웹을 거쳐 가는 과정도 앱에서 가능하더라구요? 앞서 나왔다시피 저희 팀의 서버를 사용하는 방식이라 크게 문제되지 않았습니다.
  • 구글에서 가져오는 옵션이 정확한지 확인한다.
    - 필요한 내용이 아니라면 없애주고, 필요한 내용을 추가합니다. 저는 필요한 옵션이 정확히 기입되지 않아서 오류가 발생했습니다.

사실 그 밖에도 여러 이유가 있었을 것 같은데,
지금도 써보면서 안정성이 높지는 않습니다.
그래서 Deprecated되었나 싶기도 하구요 😢

추후 계획

아직 개선해야 할 부분은 Data 레이어에서 Presentation 레이어로 직접 주입해준다는 아키텍처에서의 아쉬움이 있고,
왜 Cancel이 되었는지에 대한 레퍼런스가 많지 않아 어려움이 있었습니다.

추후 리팩토링을 진행하면서 Data와 관심사를 분리하고 원인을 찾아보는 과정을 거쳐봐야겠습니다 ㅎㅎ...

최신 레퍼런스도 많이 없고,
Jetpack Compose에 더불어 Hilt와 함께 사용하는 방식도 보이지 않는 것 같아서 아티클로 남겨봅니다.
추후 구글 로그인 때문에 삽질하게될 사람들을 위해... 파이팅입니다 🎉

profile
Junior Android Developer

0개의 댓글