카테고리 없음

[flutter] 소셜로그인 - 구글로그인 에러(안드로이드 sha-1 이슈)

STUFIT 2024. 5. 24. 11:30
반응형

현재 개발중인 앱에서는 구글로그인, 카카오 로그인, 네이버로그인 3개의 소셜로그인을 구현해놓았다. 그런데 IOS에서만 테스트하다가 안드로이드를 켜서 구글 로그인을 하는데, 구글로그인을 누른 후 해당 아이디를 클릭하면 아예 안드로이드가 꺼져버리는 이슈가 발생했다.

분명히 나는 구글 GCP에도 안드로이드랑 IOS를 OAuth 인증을 해놓았는데 왜 이런 문제가 발생할까 도저히 모르다가 어느 해외 유튜버를 따라하면서 해당 이슈를 해결할 수 있었다.

먼제 문제의 코드이다.

import 'dart:convert';

import 'package:dio/dio.dart';
import 'dart:io' show Platform;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_naver_login/flutter_naver_login.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:kakao_flutter_sdk/kakao_flutter_sdk.dart';
import 'package:wallet_worrior/config/base_url.dart';
import 'package:wallet_worrior/enum/login_platform.dart';
import 'package:wallet_worrior/src/auth/AuthDio.dart';
import 'package:wallet_worrior/src/auth/token.dart';
import 'package:wallet_worrior/src/service/user/user_service.dart';

class SocialLoginService {
  GoogleLSignIn _googleSignIn;
  UserService userService = UserService();
  final _dio = AuthenticatedDio().dio;

  String get _nestUrl => '${BaseUrlConfig.baseUrl}';

  // SocialLoginService() {
  //   _initializeSignInMethod();
  // }
  //
  // void _initializeSignInMethod() {
  //   if (Platform.isAndroid) {
  //     _googleSignIn =
  //         // GoogleSignIn(serverClientId: dotenv.get('GOOGLE_CLIENT_ID_ANDROID'));
  //     GoogleSignIn();
  //   } else if (Platform.isIOS) {
  //     _googleSignIn = GoogleSignIn(clientId: dotenv.get('GOOGLE_CLIENT_ID'));
  //   } else {
  //     _googleSignIn = GoogleSignIn();
  //   }
  // }

  Future<bool> signIn(LoginPlatform platform) async {
    try {
      switch (platform) {
        case LoginPlatform.kakao:
          return await _signInWithKakao();
        case LoginPlatform.google:
          return await _signInWithGoogle();
        case LoginPlatform.naver:
          return await _signInWithNaver();
        default:
          print('지원하지 않는 로그인 플랫폼 입니다.');
          return false;
      }
    } catch (e) {
      print('로그인 중 에러가 발생했습니다 : $platform: $e');
      return false;
    }
  }

  Future<bool> _signInWithKakao() async {
    bool isInstalled = await isKakaoTalkInstalled();
    OAuthToken token = isInstalled
        ? await UserApi.instance.loginWithKakaoTalk()
        : await UserApi.instance.loginWithKakaoAccount();
    return await _sendTokenToBackend(token.accessToken, 'kakao');
  }

// 구글 로그인
  Future<bool> _signInWithGoogle() async {
    try {
      final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
      if (googleUser != null) {
        final GoogleSignInAuthentication googleAuth =
            await googleUser.authentication;
        if (googleAuth.accessToken != null) {
          return await _sendTokenToBackend(googleAuth.accessToken!, 'google');
        } else {
          return false;
        }
      }
      return false;
    } catch (e) {
      print('구글에러:${e}');
      return false;
    }
  }

  // 구글 로그아웃 메서드
  Future<bool> googleSignOut() async {
    try {
      await _dio.patch('$_nestUrl/user/logout');
      await _googleSignIn.signOut();
      await TokenStorage.clearTokens(); // 토큰 저장소에서 토큰 삭제
      return true;
    } catch (e) {
      print('구글 로그아웃 에러: $e');
      return false;
    }
  }

}

위의 코드에서 주석을 쳐놓은 곳은 내가 이전에 핸드폰이 안드로이드일 경우와 IOS일 경우로 나누어서 clientId를 제공하였다.

그런데 사실 위의 코드는 헛짓이라는 것을 깨달았다. 

먼저, 안드로이드에서는 google-services.json 을 android/app에 넣기 때문에 굳이 클라이언트 아이디를 저렇게 박아줄 필요는 없다.

IOS도 마찬가지이다.

그래서 나는 코드를 주석된 곳을 지우고 static final _googleSignIn = GoogleSignIn(); 을 class SocialLoginService아래에 추가해주었다.

근데 문제는 이렇게 바꾸어도 안드로이드에서 계속 문제가 발생하는 것이다.

그 이유를 알고 보니 바로 내 GCP가 문제였다.

GCP에서는 SHA-1 를 입력하는게 있는데, 내가 debug용, release용, 앱 서명 키용을 만들지 않았던 것이다.

그래서 위의 3가지를 만들기 위해 다음과 같은 작업을 진행하였다.

먼저 제일 root로 가서 .android 폴더로 진입한 후 아래의 키 생성 명령어를 넣어보자. 나는 맥을 사용해서 다음과 같이 들어갔다.

cd /Users/[계정명]/.android

1. debug key 생성

keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -storepass android -keypass android -keyalg RSA -keysize 2048 -validity 999999 -dname "CN=Android Debug,O=Android,C=US"

2. release key 생성

keytool -genkey -v -keystore release.keystore -alias androidreleasekey -storepass android -keypass android -keyalg RSA -keysize 2048 -validity 999999 -dname "CN=Android Debug,O=Android,C=US"

3. 앱 서명 key 생성

keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

이 때 앱 서명 key를 복붙해서 엔터를 누르면 몇가지 문답이 나오는데 해당 문답을 완료하면 된다.

그리고 키 저장소 비밀번호는 잘 기억해두도록 하자.

여기서 생성된 key.jsk 파일은 플러터의 android/app 폴더에 넣어주자.

그리고 app에 있는 build.gradle은 다음과 같이 작성해주자.

plugins {
    id "com.android.application"
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
}

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

android {
    namespace "com.example.walletWorrior"
    compileSdk flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.walletWorrior"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
        minSdkVersion 23
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    signingConfigs{
        debug{
            keyAlias 'key'
            keyPassword 'android'
            storePassword 'android'
            storeFile file('key.jks')
        }
    }

    buildTypes {
        debug {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {}

원래는 signingConfigs가 비어있었는데 이렇게 debug를 추가해주었다. 그리고 밑에 buildTypes에는 위에 작성한 debug를 참조해주도록 하자.

다시 돌아와서, /Users/[계정명]/.android 에서 아래의 명렁어를 치면 존재하는 key를 확인할 수 있다.

DEBUG KEY 확인
keytool -list -v -keystore debug.keystore -alias androiddebugkey -storepass android -keypass android

RELEASE KEY 확인
keytool -list -v -keystore release.keystore -alias androidreleasekey -storepass android -keypass android

앱 서명 KEY 확인
keytool -list -v -keystore key.jks

이렇게 완성된 키는 GCP에서 등록해주면 된다.

 

이렇게 하면 안드로이드가 꺼지는 이슈는 해결되었다.

그런데 한가지 더 문제점이 남아있다. 바로 IOS 부분이였다.

아까 위의 코드에서 IOS일 때에도 분기처리를 통해 clientId를 직접 env 에서 받아와서 보내주다가, GoogleSignIn() 메서드만 사용하니 안드로이드와 똑같이 아예 앱이 꺼져버리는 현상이 일어났다.

이것은 아마 90% 이상의 높은 확률로 Info.plist 에 GIDClientID를 빼먹었을 가능성이 크다.

먼저, GoogleService-Info.plist 에서 REVERSED_CLIENT_ID 복사한 후에 Info.plist에 CFBundleURLSchemes 아래에 넣어주자.

<key>CFBundleURLSchemes</key>
<array>
    <string>여기에 넣어주자</string>
</array>

그 다음, GoogleService-Info.plist의 Client_id를 복사한 후에는 GIDClientID에 복붙해주자.

이때 GIDClientID는 맨 마지막에 있는 dict에 넣어줘야 한다!!

<key>GIDClientID</key>
    <string>클라이언트아이디</string>

총 코드를 보자면

<?xml version="1.0" encoding="UTF-8"?>
http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CADisableMinimumFrameDurationOnPhone</key>
    <true/>
    <key>CFBundleDevelopmentRegion</key>
    <string>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleDisplayName</key>
    <string>WalletWorrior</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>wallet_worrior</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>$(FLUTTER_BUILD_NAME)</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleURLTypes</key>
    <array>
       <dict>
          <key>CFBundleTypeRole</key>
          <string>Editor</string>
          <key>CFBundleURLSchemes</key>
          <array>
             <string>kakao7b3e4fe8aab03e83422ab6e3c87db9a7</string>
             <string>walletWorriorScheme</string>
             <string>리버스 클라이언트 아이디</string>
          </array>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>리버스 클라이언트 아이디</string>
            </array>
       </dict>
    </array>
    <key>CFBundleVersion</key>
    <string>$(FLUTTER_BUILD_NUMBER)</string>
    <key>LSApplicationQueriesSchemes</key>
    <array>
       <string>kakaolink</string>
       <string>kakaompassauth</string>
       <string>kakaoplus</string>
       <string>naversearchapp</string>
       <string>naversearchthirdlogin</string>
    </array>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>NSAppTransportSecurity</key>
    <dict>
       <key>NSALLowsLocalNetworking</key>
       <true/>
       <key>NSAllowsArbitraryLoads</key>
       <true/>
       <key>NSAllowsArbitraryLoadsInWebContent</key>
       <true/>
       <key>NSExceptionDomains</key>
       <dict>
          <key>naver.com</key>
          <dict>
             <key>NSExceptionRequiresForwardSecrecy</key>
             <false/>
             <key>NSIncludesSubdomains</key>
             <true/>
          </dict>
          <key>naver.net</key>
          <dict>
             <key>NSExceptionRequiresForwardSecrecy</key>
             <false/>
             <key>NSIncludesSubdomains</key>
             <true/>
          </dict>
       </dict>
    </dict>
    <key>NSCameraUsageDescription</key>
    <string>카메라 권한을 허용 하시겠습니까?</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>마이크 권한을 허용 하시겠습니까?</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>사진첩 권한을 허용 하시겠습니까?</string>
    <key>UIApplicationSupportsIndirectInputEvents</key>
    <true/>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>UISupportedInterfaceOrientations</key>
    <array>
       <string>UIInterfaceOrientationPortrait</string>
       <string>UIInterfaceOrientationLandscapeLeft</string>
       <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
       <string>UIInterfaceOrientationPortrait</string>
       <string>UIInterfaceOrientationPortraitUpsideDown</string>
       <string>UIInterfaceOrientationLandscapeLeft</string>
       <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>naverConsumerKey</key>
    <string>rrgoC_hcKaTGs2oBbF9m</string>
    <key>naverConsumerSecret</key>
    <string>H_xEMNyqIs</string>
    <key>naverServiceAppName</key>
    <string>walletWorrior</string>
    <key>naverServiceAppUrlScheme</key>
    <string>walletWorriorScheme</string>
    <key>GIDClientID</key>
        <string>클라이언트아이디</string>
</dict>
</plist>

이렇게 하면 구글로그인 오류를 해결할 수 있다.!!

무려 4시간동안 삽질한 나의 버그 해결기 끝!

참고한 유튜브를 아래에 링크를 달도록 하겠다.

구글 로그인(안드로이드) 오류 해결 참고 유튜브: https://www.youtube.com/watch?v=E5WgU6ERZzA

반응형