애플 계정 탈퇴 트러블 슈팅

김동연·2025년 6월 23일

개발기록일지(Flutter)

목록 보기
22/32

트러블슈팅

애플 계정 탈퇴 실패

문제: client_secret(JWT) 생성 단계에서 반복적으로 오류 발생


1. 구현 방향과 기본 개념

  • 목표:
    Apple 계정 탈퇴(연동 해제, revoke) API를 호출하려면
    반드시 JWT 기반의 client_secret을 생성해서 보내야 함.
    이 JWT의 서명에 **애플 개발자 사이트에서 받은 private key(PEM 포맷)**가 사용됨.

  • Flutter(dart)에서의 접근:
    서버가 아닌 클라이언트(Dart/Flutter)에서 직접 JWT를 만들어 dart_jsonwebtoken 라이브러리로 서명해서
    Apple 서버에 POST 요청을 보내는 구조로 시도.


2. 실제로 시도한 코드 구조

  • .env에서 PEM(개인키) 문자열 여러 줄로 분할해 불러와 조립

    final privateKey = [
      dotenv.env['APPLE_PRIVATE_KEY_LINE1']!,
      dotenv.env['APPLE_PRIVATE_KEY_LINE2']!,
      // ...
    ].join('\n');
  • JWT 객체 생성

    final jwt = JWT({
      'iss': teamId,
      'iat': ...,
      'exp': ...,
      'aud': 'https://appleid.apple.com',
      'sub': clientId,
    }, header: {
      'kid': keyId,
      'alg': 'ES256',
    });
  • JWT 서명

    final clientSecret = jwt.sign(
      privateKey, // <- PEM string 사용
      algorithm: JWTAlgorithm.ES256,
    );

3. 반복적으로 발생한 문제(오류 상황)

A. Argument Type 오류 (privateKey → JWTKey)

  • 오류 메시지:

    The argument type 'String' can't be assigned to the parameter type 'JWTKey'
  • 원인:

    • dart_jsonwebtoken 최신 버전에서는 sign() 함수가 PEM 문자열 직접 서명을 지원하지 않거나,
    • 또는 ECPrivateKey 타입만 허용하는 등 버전별 구현 차이 발생

B. PEM → ECPrivateKey 변환 시도 후 또 오류

  • 시도 코드:

    import 'package:basic_utils/basic_utils.dart';
    import 'package:pointycastle/ecc/api.dart' as pc;
    final pc.ECPrivateKey ecPrivateKey =
        CryptoUtils.ecPrivateKeyFromPem(privateKey);
    // ...
    final clientSecret = jwt.sign(
      ecPrivateKey,
      algorithm: JWTAlgorithm.ES256,
    );
  • 오류 메시지:

    The argument type 'ECPrivateKey' can't be assigned to the parameter type 'JWTKey'
  • 원인:

    • 라이브러리마다 기대하는 키 타입이 다름(서로 호환 안됨)
    • ECPrivateKey를 기대하는 게 아니라 자체 정의된 JWTKey만 지원

C. fromPem/parse 함수 등 별도 변환 함수 시도

  • 오류 메시지:

    The method 'fromPem' isn't defined for the type 'JWTKey'
  • 원인:

    • 공식 예시나 옛 버전과 다르게, 현 라이브러리에는 'fromPem' 등 변환 함수 없음

D. 버전 다운/업 시도

  • dart_jsonwebtoken을 1.0.1/2.7.1/3.1.1 등으로 다운그레이드/업그레이드

  • 여전히 같은 오류 반복

    • 어떤 버전에서는 아예 PEM 문자열 서명이 안 되고,
    • 어떤 버전에서는 키 객체 타입 불일치 오류만 남음
  • 결과:
    "PEM → 서명 키" 변환 방법 자체가 불안정하며, pure Dart로 client_secret(JWT) 생성이 사실상 불가능에 가까움


4. 추가적 참고 시도 및 결론

A. 구글링/깃허브 이슈/블로그 등

  • "Flutter에서 Apple client_secret 직접 서명" 관련 사례가 매우 적음
  • 서버(Node.js, Python 등)에서 JWT 서명해서 클라이언트로 내려주는 방식이 권장됨
  • 순수 Dart에서 EC키 파싱 및 서명은 지원이 불완전, 각종 라이브러리간 호환성 이슈 많음

B. 공식 문서 확인

  • Apple 공식 문서도 JWT 생성은 일반적으로 서버에서 진행하는 것을 가정

    • 비밀키 노출 위험, 환경차이(서명) 등 때문에
    • (Flutter 클라이언트만 사용하는 구조는 공식 가이드 아님)

5. 정리 (실패 원인 및 교훈)

  • privateKey(PEM)를 Flutter 클라이언트에서 직접 조립/사용해 client_secret(JWT) 생성 → 대부분 라이브러리 지원이 불완전, 타입 호환 문제 등으로 실패
  • 서버에서 JWT를 생성해서 client_secret을 클라이언트에 내려주는 구조가 확실
  • Flutter(Dart) 환경에서 PEM 파싱 및 JWT 서명은 버전에 따라 지원이 다르고, 공식적으로 보장되지 않음
  • 문제를 해결하기 위해 라이브러리 다운그레이드/업그레이드, 키 타입 변환, 코드 여러 방식 모두 시도했지만 일관되게 오류
  • 실제로 앱을 배포한다면 Apple privateKey는 절대 클라이언트에 노출하지 않고, 반드시 백엔드에서 JWT 생성/관리 권장

6. 결론

  • 실패:

    • "클라이언트(Flutter)에서 직접 Apple client_secret(JWT) 생성"은
      Dart 생태계 라이브러리/키 파싱/타입 이슈로 인해 실패했다.
  • 원인:

    • PEM(개인키) → JWT 서명 지원 미흡
    • 라이브러리마다 API 불일치, 타입 불일치
    • 보안상 애플도 공식적으로 서버에서만 하라고 안내
  • 교훈:

    • 클라이언트에서 민감키 서명 시도보단 서버 위임 구조 필수
    • 불가피하게 클라이언트에서 해야 한다면,
      최신 라이브러리 지원과 PEM 파싱, JWTKey 변환 등 다각적으로 미리 테스트 필요

0개의 댓글