Hun-Bot

26년 반기를 돌아보면서

tag1 tag2

Google Sign-In 릴리스 설정 수정 PR

요약

이번 작업은 Flutter 클라이언트의 Android 릴리스 빌드에서 Google Sign-In이 PlatformException(sign_in_failed)로 실패하던 문제를 조사하고 수정한 내용입니다.

핵심 수정은 다음과 같습니다.

  • Dart 소스에 하드코딩되어 있던 Google OAuth serverClientId 제거
  • Android에서는 android/app/google-services.json에서 생성되는 default_web_client_id를 사용하도록 변경
  • Firebase 설정에 릴리스 서명 SHA-1 기반 Android OAuth 클라이언트 추가
  • Google Sign-In 실패 시 원인을 더 명확히 알 수 있도록 예외 메시지 개선
  • 재발 방지를 위한 테스트와 README 문서 추가

커밋:

2a2f7d7 Fix Google Sign-In release config

문제 상황

릴리스 실행 명령으로 앱을 실행했을 때 Google 로그인 단계에서 다음 문제가 발생했습니다.

PlatformException(sign_in_failed)

초기 상태에서 확인한 내용은 다음과 같습니다.

  • Firebase SHA-1 / SHA-256 등록은 이미 진행했다고 알려져 있었음
  • android/app/google-services.json은 Firebase에서 다시 다운로드해 교체한 상태였음
  • GrpcAuthRemoteDataSource 안에 Google OAuth client ID가 하드코딩되어 있었음
  • Android 릴리스 빌드가 실제로 어떤 서명 키로 설치되는지와 Firebase JSON 안의 Android OAuth client가 일치하는지 확인이 필요했음

조사한 항목

저장소 전체에서 다음 키워드와 설정을 확인했습니다.

  • serverClientId
  • clientId
  • GoogleSignIn
  • default_web_client_id
  • apps.googleusercontent.com
  • google-services.json
  • Android Gradle Google Services plugin 적용 여부
  • Android package name
  • Firebase project id / project number
  • iOS Google Sign-In plist 설정
  • 인증 gRPC 흐름
  • Google ID token 전달 경로
  • 로그와 예외 처리 방식

확인한 주요 파일:

flutter_client/lib/features/auth/data/auth_remote_data_source.dart
flutter_client/lib/features/auth/data/auth_repository.dart
flutter_client/lib/features/auth/providers/auth_repository_provider.dart
flutter_client/lib/main.dart
flutter_client/lib/features/auth/data/grpc_gen/auth/v1/auth.pb.dart
flutter_client/android/app/google-services.json
flutter_client/android/app/build.gradle.kts
flutter_client/android/settings.gradle.kts
flutter_client/pubspec.yaml
flutter_client/pubspec.lock
flutter_client/ios/Runner/Info.plist
flutter_client/ios/Runner/GoogleService-Info.plist
flutter_client/README.md

원인

하드코딩되어 있던 값 자체는 Android OAuth client가 아니라 Web OAuth client였습니다.

즉, 아래 값은 serverClientId로 쓰면 안 되는 Android client가 아니라, client_type: 3인 Web client였습니다.

44649239380-2pqbv44f0hb68vu90t7f7bl2vqltl5pq.apps.googleusercontent.com

하지만 Android에서는 이 값을 Dart에 직접 하드코딩할 필요가 없습니다. 현재 사용하는 google_sign_in Android 구현은 Google Services Gradle plugin이 생성하는 default_web_client_id를 자동으로 읽을 수 있습니다.

실제 로그인 실패의 더 중요한 원인은 google-services.json의 Android OAuth client가 릴리스 서명 키와 일치하지 않았던 점입니다.

초기 google-services.json에는 debug SHA-1 기반 Android OAuth client만 있었습니다.

debug SHA-1:
9E:8A:83:07:FD:6F:9E:D0:52:07:4D:B7:D1:5F:38:E7:6D:C6:42:81

하지만 flutter run --release는 릴리스 키로 앱을 설치합니다.

release SHA-1:
A4:AC:9F:60:F4:C4:7C:CF:13:B7:9C:61:91:FD:B2:B2:BE:C4:72:EC

따라서 Firebase / Google Cloud OAuth 설정에 릴리스 SHA-1 기반 Android OAuth client가 없으면 릴리스 빌드에서 Google Sign-In이 실패합니다.

수정 내용

1. Dart 하드코딩 제거

변경 파일:

flutter_client/lib/features/auth/data/auth_remote_data_source.dart

기존에는 다음처럼 Web OAuth client ID가 Dart 소스에 직접 들어가 있었습니다.

final googleSignIn = GoogleSignIn(serverClientId: serverClientId);

수정 후에는 플랫폼 설정을 사용하도록 변경했습니다.

static GoogleSignIn createGoogleSignIn() => GoogleSignIn();

Android에서는 google-services.json -> Google Services Gradle plugin -> default_web_client_id 흐름을 사용합니다.

2. Google Sign-In 예외 메시지 개선

Google Sign-In 실패가 단순한 Exception으로 처리되지 않도록 GoogleAuthException을 추가했습니다.

이제 다음 상황에서 더 명확한 메시지를 제공합니다.

  • 사용자가 계정 선택을 취소한 경우
  • Google Sign-In platform call 자체가 실패한 경우
  • ID token이 반환되지 않은 경우
  • Android package / SHA fingerprint / default_web_client_id 설정 문제가 의심되는 경우

토큰이나 전체 인증 정보는 로그에 출력하지 않습니다.

3. Android Firebase 설정 갱신

변경 파일:

flutter_client/android/app/google-services.json

새 파일에는 Android OAuth client가 두 개 있습니다.

debug Android OAuth client
SHA-1: 9e8a8307fd6f9ed052074db7d15f38e76dc64281

release Android OAuth client
client_id: 44649239380-e08vigdh9hig86v6bk4iip1lijtej6re.apps.googleusercontent.com
SHA-1: a4ac9f60f4c47ccf13b79c6191fdb2b2bec472ec

Web OAuth client도 유지되어 있습니다.

client_type: 3
client_id: 44649239380-2pqbv44f0hb68vu90t7f7bl2vqltl5pq.apps.googleusercontent.com

이 Web client는 Android에서 default_web_client_id로 생성됩니다.

4. 테스트 추가

변경 파일:

flutter_client/test/auth_remote_data_source_test.dart

추가한 테스트는 GrpcAuthRemoteDataSource가 Google Sign-In을 생성할 때 clientIdserverClientId를 직접 지정하지 않는지 확인합니다.

expect(googleSignIn.clientId, isNull);
expect(googleSignIn.serverClientId, isNull);

5. README 문서 추가

변경 파일:

flutter_client/README.md

추가한 내용:

  • Android Google Sign-In 설정의 source of truth는 android/app/google-services.json
  • Dart에 OAuth client ID를 하드코딩하지 않는 이유
  • default_web_client_id 생성 확인 명령
  • 릴리스 빌드에서는 signingReport와 Firebase JSON의 SHA-1 / SHA-256을 비교해야 한다는 점
  • 릴리스 실행 시 필요한 auth gRPC dart-define 예시

검증 결과

Google Services resource 생성

실행:

cd flutter_client/android
./gradlew :app:processReleaseGoogleServices

결과:

BUILD SUCCESSFUL

생성된 resource 확인:

grep -R "default_web_client_id" ../build/app/generated -n

결과:

../build/app/generated/res/processReleaseGoogleServices/values/values.xml:3:
<string name="default_web_client_id" translatable="false">44649239380-2pqbv44f0hb68vu90t7f7bl2vqltl5pq.apps.googleusercontent.com</string>

릴리스 서명 확인

실행:

cd flutter_client/android
./gradlew :app:signingReport

확인한 릴리스 서명:

Variant: release
Store: android/app/upload-keystore.jks
Alias: upload
SHA1: A4:AC:9F:60:F4:C4:7C:CF:13:B7:9C:61:91:FD:B2:B2:BE:C4:72:EC
SHA-256: 4E:DD:0A:0C:3A:86:70:11:91:9A:B8:0C:D7:9E:A5:CF:F4:BA:73:BC:86:4C:57:EF:69:99:9A:A6:B7:30:47:22

현재 google-services.json에는 위 릴리스 SHA-1에 대응하는 Android OAuth client가 포함되어 있습니다.

Flutter 검증

실행:

flutter clean
flutter pub get
flutter test test/auth_remote_data_source_test.dart
flutter test test/widget_test.dart

결과:

auth_remote_data_source_test.dart: passed
widget_test.dart: passed

flutter analyze도 실행했습니다.

결과는 인증 변경과 무관한 기존 info issue 2개가 남아 있습니다.

lib/features/profile/user_page_screen.dart:4:8
unnecessary_import

lib/features/profile/user_page_screen.dart:85:11
deprecated_member_use: cacheExtent

이번 PR에 포함된 파일

flutter_client/README.md
flutter_client/android/app/google-services.json
flutter_client/lib/features/auth/data/auth_remote_data_source.dart
flutter_client/test/auth_remote_data_source_test.dart

이번 PR에서 제외한 로컬 변경

다음 파일들은 현재 작업 트리에 남아 있지만 이번 Google Sign-In PR 커밋에는 포함하지 않았습니다.

flutter_client/android/app/build.gradle.kts
flutter_client/android/gradle.properties
flutter_client/ios/Runner.xcodeproj/project.pbxproj
flutter_client/pubspec.yaml
flutter_client/android/app/build.gradle.kts.debug-signing.backup
flutter_client/android/key.properties.example
flutter_client/assets/on-the-block-white Medium.jpeg
flutter_client/ios/Flutter/Profile.xcconfig

이유:

  • Android release signing Gradle 변경은 팀원 릴리스 빌드 방식에 영향을 줄 수 있음
  • key.properties.example은 별도 release signing 문서/정책으로 다루는 편이 안전함
  • iOS project / Profile.xcconfig 변경은 이번 Android Google Sign-In 수정과 직접 관련 없음
  • pubspec.yaml version bump는 인증 수정과 별도 릴리스 관리 이슈임
  • 이미지 asset 추가는 이번 인증 PR 범위 밖임

클라이언트 개발 진행 상황 메모

이번 작업 기준으로 클라이언트는 다음 방향으로 정리되고 있습니다.

  • 인증 흐름은 AuthRemoteDataSource -> gRPC AuthService로 연결되어 있음
  • Google Sign-In은 모바일 플랫폼 설정을 사용하고, 앱은 Google ID token만 backend authorization service로 전달함
  • backend authorization protocol은 변경하지 않았음
  • Android package name은 com.ontheblockand.app 그대로 유지함
  • Firebase project는 on-the-block-2026 그대로 유지함
  • gRPC endpoint 설정은 --dart-define 기반으로 유지함
  • gateway, chat, survey, board, map service endpoint는 실행 명령에서 환경별로 주입하는 구조를 유지함

릴리스 실행 예시

아래 명령은 현재 사용 중인 staging / Cloud Run endpoint 조합으로 릴리스 실행할 때 사용한 형태입니다.

flutter run --release \
  --dart-define=GATEWAY_BASE_URL=https://gateway-service-44649239380.asia-northeast3.run.app \
  --dart-define=APP_GATEWAY_GRPC_HOST=gateway-service-44649239380.asia-northeast3.run.app \
  --dart-define=APP_GATEWAY_GRPC_PORT=443 \
  --dart-define=APP_GATEWAY_GRPC_TLS=true \
  --dart-define=AUTH_GRPC_HOST=authorization-service-vcuepibcwq-du.a.run.app \
  --dart-define=AUTH_GRPC_PORT=443 \
  --dart-define=AUTH_GRPC_TLS=true \
  --dart-define=CHAT_GRPC_HOST=chat-service-staging-44649239380.asia-northeast3.run.app \
  --dart-define=CHAT_GRPC_PORT=443 \
  --dart-define=CHAT_GRPC_TLS=true \
  --dart-define=SURVEY_GRPC_HOST=survey-service-44649239380.asia-northeast3.run.app \
  --dart-define=SURVEY_GRPC_PORT=443 \
  --dart-define=SURVEY_GRPC_TLS=true \
  --dart-define=BOARD_GRPC_HOST=gateway-service-44649239380.asia-northeast3.run.app \
  --dart-define=BOARD_GRPC_PORT=443 \
  --dart-define=BOARD_GRPC_TLS=true \
  --dart-define=MAP_API_BASE_URL=https://map-service-44649239380.asia-northeast3.run.app/ \
  --dart-define=KAKAO_MAP_API_KEY=<local-key> \
  --dart-define=KAKAO_REST_API_KEY=<local-key>

주의:

  • Kakao key 값은 실제 값을 문서나 PR에 노출하지 않습니다.
  • Google OAuth client ID는 설정 파일에 포함되는 값이지만 auth token은 절대 로그나 문서에 남기지 않습니다.

리뷰어 확인 포인트

  • auth_remote_data_source.dart에서 OAuth client ID 하드코딩이 제거되었는지
  • google-services.json에 debug / release Android OAuth client가 모두 있는지
  • default_web_client_id가 Web OAuth client로 생성되는지
  • backend GoogleLoginRequest.idToken 프로토콜이 변경되지 않았는지
  • 이번 PR에 release signing Gradle 변경 같은 팀원 영향 큰 변경이 섞이지 않았는지

후속 작업

  • 실제 Android 기기에서 flutter run --release로 Google Sign-In 재확인
  • Google Play 배포를 시작할 경우 Play Console의 App Signing certificate SHA-1 / SHA-256도 Firebase에 등록
  • flutter analyze의 기존 profile 화면 info issue 정리
  • release signing Gradle 변경은 별도 PR로 분리 검토
series 이름 2 / 14

목차

댓글