View

300x250

이번에 개발을 진행하면서 Sign In With Apple 기능을 붙이려고 했다. 그런데, 정말 예상치 못하는 상황에 Exception이 계속 발생했다. ㅜㅜㅜ 원인을 찾지 못해서 이걸로 2일 이상은 날린 것 같다.

간단하게 결론만 말하자면, iOS Configuration을 새로 만들자.


Firebase에서 Sign in With Apple을 적용하려고 했으며, Flutter로 개발을 해주고 있었다.

우선 설정은 다음 사항들을 진행했다. 다른 블로그 글들, 유튜브 튜토리얼에서 잘 설명되어있었다.

  • Firebase
    • Sign in with Apple 추가하기
  • Apple Developer
    • App Identifier 등록
      • Sign in with Apple 체크
    • Service Identifier 등록
      • Sign in with Apple Configuration 진행 (url, 도메인 넣기)
    • Sign in with Apple for Email Communication
      • (요건 안해줘도 되는 것 같긴 하던데, 스택오버플로우에서 이거 하면 문제 해결될 수 있대서 했음. 근데 안됐음 ㅋㅋㅠ)
  • Xcode
    • iOS 버전 13.0 이상 (나는 15.0으로 지정함)
    • app bundle ID 지정해주기
    • Sign in with Apple Capability 추가해주기

(더 있었던가..?)

애플로그인을 진행할 때, 아래 함수를 실행해주게 된다. 이 함수의 역할은 Sign In 모달을 띄워서 정보를 입력받고, 입력받고 처리한 정보를 반환해주는 함수이다. 그런데, 나의 경우에는 여기 부분에서 Exception이 발생하였다. 심지어 모달 창은 뜨지도 않았다.

final credential = await SignInWithApple.getAppleIDCredential(
  scopes: [
    AppleIDAuthorizationScopes.email,
    AppleIDAuthorizationScopes.fullName,
  ],
);

더 슬펐던 건, Exception의 이유가 콘솔에 나오지도 않았다는 것… 그냥 플러터도 “잘 모르겠어요… 응애…” 하고 나한테 넘겨버렸다. 이 AuthorizationError 1000 이라는 코드도 크게 의미 없는 정보같았다.

SignInWithAppleAuthorizationException
(AuthorizationErrorCode.unknown, The operation couldn’t be completed. 
(com.apple.AuthenticationServices.AuthorizationError error 1000.))

이 에러가 발생하면 다음 사항들을 고려해보라고 했는데, 나에게는 전혀 통하지 않았다. 혹시 아나? 이거 읽으러 온 사람들은 이걸로 해결이 될 지

  • Xcode Capability에서 Sign in with Apple 추가하기 (→ 이미 함)
  • Simulator 대신 실기기로 빌드하기 (→ 원래 실기기로 빌드함)
  • iOS 버전 13.0 부터 Sign in with Apple 지원함 (→ 나는 최소 버전을 15.0으로 맞춰둠)
  • 캐시 문제일 수 있음. 설치된 앱 완전히 삭제하고, 새로 빌드해서 실행하기 (→ 이것도 안됨)
  • iCloud 로그인 한 뒤에 앱 실행하기 (→ 내 실제 폰으로 빌드함)

여기에서 현타오고 방바닥에 한 20분 누워있었음 🛌🛌🛌 이런 에러 관련해서는 그냥 해결책을 알려주는 글도 별로 없었다. 구글 검색어에서 몇 페이지 넘어가니깐 일본어랑 중국어로 된 아티클들도 나오기 시작하더라…

그래서, 요 SignInWithApple.getAppleIDCredential 함수 라이브러리를 타고 들어가서 문제가 어디에서 발생하는지를 찾아보려고 했다.

// 호출하는 함수
class SignInWithApple {
	...
	static Future<AuthorizationCredentialAppleID> getAppleIDCredential({
    required List<AppleIDAuthorizationScopes> scopes,
    WebAuthenticationOptions? webAuthenticationOptions,
    String? nonce,
    String? state,
  }) async {
    return SignInWithApplePlatform.instance.getAppleIDCredential(
      scopes: scopes,
      webAuthenticationOptions: webAuthenticationOptions,
      nonce: nonce,
      state: state,
    );
  }
	 ...
}

 

// 타고 들어가면
class MethodChannelSignInWithApple extends SignInWithApplePlatform {

  @override
  Future<AuthorizationCredentialAppleID> getAppleIDCredential({
    required List<AppleIDAuthorizationScopes> scopes,
    WebAuthenticationOptions? webAuthenticationOptions,
    String? nonce,
    String? state,
  }) async {
			...

// 📌 여기에서 Exception이 발생한다!
      final response = await _channel.invokeMethod<Map<dynamic, dynamic>>(
        'performAuthorizationRequest',
        [
          AppleIDAuthorizationRequest(
            scopes: scopes,
            nonce: nonce,
            state: state,
          ).toJson(),
        ],
      );
			...
			
    } on PlatformException catch (exception) {
      throw SignInWithAppleException.fromPlatformException(exception);
    }
  }
}

 

 _channel.invokeMethod 의 역할은 NativeOS와 Flutter 사이의 Communication 을 하는 것이다. 즉, 플러터에서 OS에게 특정 작업을 요청하는 통로인데, 여기에서 뭔가 문제가 발생하고 있었다.

그리고, 이 _invokeMethod는 아래처럼 생겼다.

@optionalTypeArgs
Future<T?> _invokeMethod<T>(String method,
    {required bool missingOk, dynamic arguments}) async {
  final ByteData input = codec.encodeMethodCall(MethodCall(method, arguments));

  final ByteData? result = shouldProfilePlatformChannels
      ? await (binaryMessenger as _ProfiledBinaryMessenger)
          .sendWithPostfix(name, '#$method', input)
      : await binaryMessenger.send(name, input); // 이게 실행됨

// 📌 여기에서 result가 이상했다.
// 📌 정상경로에서는 send -> Sign in 모달이 올라오고 await이 걸려야 하는데
// 📌 await에서 걸리지 않고, result에 쓰레기 값이 들어간 채로 바로 넘어가버림

  if (result == null) {
    if (missingOk) {
      return null;
    }
    throw MissingPluginException(
        'No implementation found for method $method on channel $name');
  }
  return codec.decodeEnvelope(result) as T?;
}

나의 추측으로는, iOS 쪽으로 플러터가 메시지를 만들어 요청을 보냈는데 이를 iOS에서 제대로 캐치하지 못하는 것으로 보였다.

그래서 샘플 프로젝트를 하나 만들어서 Sign in with Apple 기능을 붙이고 두 프로젝트의 차이점을 찾아보고자 했다.

 

그런데 왠걸? message로 전달되는 인자값이 완전히 동일했다. 전혀 이상할 게 없었어… 플러터, 패키지, PodFile, Pubspec, 시뮬레이터 기기, Xcode 쪽 세팅, Apple Developer Certification도 동일하게 맞춰줬기 때문에, 차이가 발생한 곳은 전혀 없었는데도 새로 만든 프로젝트는 동작했다. ㅜㅜㅜ

그래서 그냥 iOS 폴더 자체를 지우고 새로 빌드하는 방법을 선택했다. 물론 권한문제 같은 부분들을 다시 복구해주는게 귀찮을 것 같기는 하지만, 일단 에러 잡는게 먼저니깐.

 

iOS 폴더를 삭제해준 뒤, 아래 코드 3줄을 터미널에 입력해주면 iOS 쪽 컨텐츠들을 새로 만들어준다.

flutter create .
flutter build ios
flutter run

ㅤㅤ

이후 다시 Xcode의 설정값들을 넣어주고 실행해봤다.

ㅤㅤ

그리고 동작함. 감동… 내가 뭔가 세팅을 잘못 넣었던 것 같지는 않은게, 샘플 프로젝트 생성했을 때에는 잘 동작했었다. 그래서 더 수상하다. 아마도 뭔가 내가 기존에 넣어뒀던 설정값이랑 새롭게 추가한 Sign in 관련 내용에서 충돌이 나서 정상적으로 실행이 되지 않은게 아닐까 싶다.

 

 

아직도 그 이유는 찾지 못했지만, 이런 경우에 문제를 해결할 수 있는 방법은 하나 터득한 것 같다. 🥲🥲🥲

또, Platform Channel을 통해 플러터가 Native Plugin과 통신하면서 필요한 OS 자원을 활용한다는 것도 이 에러 덕분에 배울 수 있었다. 후후

320x100
Share Link
reply
반응형
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31