Flutter로 안드로이드 앱을 개발하면서 Google Sign-In과 Firebase Authentication을 함께 사용하는 경우가 많다. 나 역시 Windows 환경에서 Antigravity IDE를 사용하여 Flutter 앱을 개발하고 있었고, 개발 중에는 Google 로그인이 정상적으로 동작했다.
하지만 앱을 Google Play Console의 내부 테스트 트랙에 배포한 후 테스트를 진행하자 예상치 못한 문제가 발생했다.
Google Sign-in failed.
GoogleSignInException(
code: GoogleSignInExceptionCode.canceled,
activity is cancelled by the user.
)처음에는 정말 사용자가 로그인을 취소한 것처럼 보였다. 하지만 실제 원인은 전혀 다른 곳에 있었다.
Debug에서는 정상인데 Release에서는 왜 실패할까?
개발 중에는 Antigravity에서 Debug 모드로 실행했기 때문에 모든 것이 정상적으로 동작했다.
Google 계정을 선택하면 Firebase Authentication까지 문제없이 로그인되었다.
하지만 Release App Bundle(AAB)을 만들어 Google Play 내부 테스트에 업로드한 후 설치하면 Google 계정 선택 이후 바로 예외가 발생했다.
처음에는 Firebase 설정이나 코드의 문제라고 생각했지만, 실제 원인은 앱이 서명(Signing)되는 방식에 있었다.
Google Sign-In은 SHA-1을 이용해 앱을 인증한다
Google Sign-In은 단순히 패키지 이름만 확인하는 것이 아니다.
Android 앱은 다음 두 가지 정보를 이용해 인증된다.
- Package Name
- SHA-1 인증서 지문
Firebase Console에 Android 앱을 등록할 때 SHA-1을 입력하는 이유도 바로 이것이다.
Debug 환경에서는 Debug Keystore의 SHA-1이 사용된다.
Release APK를 직접 설치하면 Release Keystore의 SHA-1이 사용된다.
여기까지는 대부분의 개발자가 알고 있는 내용이다.
하지만 Google Play를 통하면 이야기가 달라진다.
Google Play는 앱을 다시 서명한다
이번 문제를 조사하면서 가장 크게 배운 부분이다.
App Bundle(AAB)을 Google Play에 업로드하면, 사용자가 설치하는 앱은 개발자가 만든 APK가 그대로 전달되는 것이 아니다.
Google Play App Signing 기능을 통해 Google이 다시 서명하여 사용자에게 배포한다.
즉 흐름은 다음과 같다.
개발자
↓
Release JKS로 AAB 생성
↓
Google Play 업로드
↓
Google Play App Signing
↓
Google이 새로운 인증서로 다시 서명
↓
사용자 설치따라서 실제 기기에 설치된 앱은 개발자의 Release JKS가 아니라 Google Play App Signing 인증서를 사용하게 된다.
이 SHA-1이 Firebase에 등록되어 있지 않으면 Google Sign-In 인증이 실패한다. 이 경우 사용자 취소처럼 보이는 GoogleSignInExceptionCode.canceled 예외가 발생할 수 있다.
serverClientId는 왜 Web Client ID일까?
이번에 가장 헷갈렸던 부분이 바로 이것이다.
Android 앱인데 왜 Android OAuth Client가 아니라 Web Client ID를 사용하는지 이해되지 않았다.
동작 구조를 이해하고 나니 이유가 명확했다.
Android App
│
│ Google Sign-In
▼
Google OAuth
│
│ ID Token 발급
▼
Firebase Authentication
│
▼
Firebase User 생성Android 앱이 Google 로그인 화면을 띄울 때는 Android OAuth Client가 사용된다.
이 과정은 SHA-1과 Package Name을 이용하여 자동으로 처리되므로 개발자가 별도로 Android Client ID를 코드에 입력하지 않는다.
반면 Firebase Authentication은 Google이 발급한 ID Token을 검증해야 한다.
이때 필요한 것이 바로 Web Client ID이다.
Flutter의 GoogleSignIn에서 사용하는 serverClientId는 바로 이 Web Client ID를 의미한다.
즉,
- Android Client → Google 계정 선택
- Web Client(serverClientId) → Firebase가 토큰 검증
이라는 역할 분담이 이루어진다.
결국 해결 방법은 무엇이었을까?
문제를 해결하기 위해 확인해야 할 것은 다음과 같다.
- Debug SHA-1 등록 여부
- Release SHA-1 등록 여부
- Google Play App Signing SHA-1 등록 여부
- 새로운 google-services.json 다운로드
- 앱 재빌드 후 다시 업로드
특히 내부 테스트나 프로덕션에서는 Google Play App Signing의 SHA-1 등록 여부를 가장 먼저 확인하는 것이 좋다.
이번 경험으로 이해한 Google Sign-In 구조
이번 문제를 해결하면서 단순히 오류 하나를 수정한 것이 아니라 Google Sign-In의 전체 구조를 이해하게 되었다.
처음에는 Google Sign-In과 Firebase Authentication이 하나의 기능처럼 느껴졌지만 실제로는 서로 다른 역할을 수행한다.
Google Sign-In은 사용자의 Google 계정을 인증하고,
Firebase Authentication은 그 결과로 전달받은 ID Token을 이용해 Firebase 사용자로 로그인시킨다.
그리고 그 사이에는 Android OAuth Client, Web Client ID, SHA-1 인증서, Google Play App Signing이라는 여러 요소가 함께 동작한다.
Debug에서는 정상인데 Google Play 내부 테스트에서만 로그인에 실패한다면, 단순히 Flutter 코드만 의심하기보다 앱 서명 방식과 Firebase에 등록된 SHA-1을 먼저 확인해 보길 추천한다.
이번 경험 덕분에 Google Sign-In의 동작 원리를 훨씬 깊이 이해할 수 있었고, 앞으로 비슷한 문제를 만나더라도 훨씬 빠르게 원인을 찾을 수 있을 것 같다.

댓글
댓글 쓰기