View
[Flutter] Apple 로그인 창 Modal 안뜨는 경우 / AuthorizationErrorCode.unknown error 1000.
sm_amoled 2024. 6. 14. 12:53ㅤ
이번에 개발을 진행하면서 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
- (요건 안해줘도 되는 것 같긴 하던데, 스택오버플로우에서 이거 하면 문제 해결될 수 있대서 했음. 근데 안됐음 ㅋㅋㅠ)
- App Identifier 등록
- 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 자원을 활용한다는 것도 이 에러 덕분에 배울 수 있었다. 후후
'Develop > Flutter 개발' 카테고리의 다른 글
[Flutter] 플러터 코드로만 Sign in with Apple 유저의 회원탈퇴 (Revoke) 기능 구현하기 (3) | 2024.06.26 |
---|---|
[Flutter] Image.file 은 File 데이터의 변경사항을 반영해주지 않는다. 대신 캐싱 관리가 쉬운 FileImage를 사용하자! (0) | 2024.06.19 |
[Flutter] Visibility 위젯의 maintainState 프로퍼티, Offstage 위젯 (0) | 2024.06.05 |
[Flutter] 자식 위젯에서 부모 위젯 setState 호출하기 (0) | 2024.05.23 |
[Flutter] Firebase에서 닉네임 문자열 필드에 대한 검색(인 척 하는) Query 기능 구현 (0) | 2024.05.22 |