← 아티클 목록
패키지 분석laravel패키지apple

Apple 패키지 분석 — 한국 Laravel 개발자 가이드

작성: 라라벨 코리아 (초안)

발행: 2026년 7월 12일

Laravel Socialite용 Apple OAuth2 프로바이더

요약

socialiteproviders/apple 패키지 버전 5.10.0은 Laravel Socialite를 통해 "Apple로 로그인(Sign In with Apple)" 기능을 구현할 수 있게 해주는 OAuth2 프로바이더입니다. Apple의 특수한 인증 방식(JWT 기반 client_secret, 6개월 만료 등) 때문에 일반 소셜 로그인보다 설정 난이도가 높으며, 한국 개발자들이 실무에서 자주 마주치는 함정들이 존재합니다. 이 글에서는 설치부터 운영 환경 적용까지 실무 중심으로 안내합니다.


핵심 내용

Apple 소셜 로그인이 다른 OAuth와 다른 이유

일반적인 Google, GitHub 소셜 로그인과 달리, Apple의 "Sign In with Apple"은 몇 가지 중요한 차이점이 있습니다.

  • client_secret이 정적 문자열이 아닌 JWT 토큰: 일반 OAuth2에서 client_secret은 고정된 문자열이지만, Apple은 ES256 알고리즘으로 서명된 JWT를 client_secret으로 사용합니다.
  • JWT 최대 유효기간 6개월: Apple 정책상 client_secret JWT의 최대 수명은 **6개월(180일)**입니다. 이를 간과하면 운영 중 갑작스러운 invalid_client 오류가 발생합니다.
  • 반환 필드가 최소화: 반환되는 사용자 정보가 id, name, email 세 가지뿐이며, 이름과 이메일은 최초 로그인 시에만 제공됩니다. 이후 로그인에서는 name이 null로 반환됩니다.

두 가지 client_secret 전략

패키지는 두 가지 방식으로 client_secret을 처리할 수 있습니다.

방법 1: 수동으로 JWT를 생성하여 .env에 등록

'apple' => [ 'client_id' => env('APPLE_CLIENT_ID'), 'client_secret' => env('APPLE_CLIENT_SECRET'), // 미리 생성한 JWT 문자열 'redirect' => env('APPLE_REDIRECT_URI'), ],
  • 장점: 설정이 단순함
  • 단점: 6개월마다 수동 갱신 필수 — 캘린더 알림 없이 방치하면 운영 장애로 직결됨

방법 2: 매 요청마다 private key로 JWT를 자동 생성 (권장)

'apple' => [ 'client_id' => env('APPLE_CLIENT_ID'), 'client_secret' => '', // 비워둠 'key_id' => env('APPLE_KEY_ID'), 'team_id' => env('APPLE_TEAM_ID'), 'private_key' => env('APPLE_PRIVATE_KEY'), // 절대 경로 필수 (예: /var/www/cert/AuthKey_XYZ.p8) 'passphrase' => env('APPLE_PASSPHRASE'), // 선택 'redirect' => env('APPLE_REDIRECT_URI'), ],
  • 장점: 만료 걱정 없이 자동 갱신
  • 단점: .p8 키 파일의 서버 내 보안 관리 필요

JWT 시간 불일치 오류 (Known Issue)

실무에서 빈번히 발생하는 문제로, 다음과 같은 예외가 발생할 수 있습니다.

InvalidStateException: The token was issued in the future
at /vendor/socialiteproviders/apple/Provider.php:207

이는 Apple 서버와 애플리케이션 서버 간의 시스템 시계 불일치로 인해 발생합니다. 해결 방법은 .env에 leeway 값을 추가하는 것입니다.

APPLE_JWT_ISSUED_TIME_LEEWAY=PT10S # 10초 여유 (기본값: PT3S)

ISO 8601 Duration 형식을 사용합니다: PT3S(3초), PT1M(1분)

invalid_client 400 오류 대응

400 Bad Request {"error":"invalid_client"} 오류 발생 시 점검 순서:

  1. client_secret JWT 만료 여부 확인
  2. APPLE_CLIENT_ID가 Apple Developer Console의 Services ID (Bundle ID가 아님)와 일치하는지 확인
  3. Signer 알고리즘 불일치 시 config/services.php에 명시적으로 지정:
'signer' => \Lcobucci\JWT\Signer\Ecdsa\Sha256::class,

Laravel 버전별 이벤트 리스너 등록 방식 차이

Laravel 버전등록 방법
Laravel 11+AppServiceProviderboot() 메서드에서 Event::listen() 사용
Laravel 10 이하EventServiceProvider$listen 배열에 등록

Laravel 11+ 예시:

// app/Providers/AppServiceProvider.php public function boot(): void { Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) { $event->extendSocialite('apple', \SocialiteProviders\Apple\Provider::class); }); }

한국 Laravel 개발자에게 미치는 영향

호환성

  • Laravel 11 기준: EventServiceProvider가 기본 제거되었으므로, 구버전 가이드를 그대로 따라할 경우 리스너가 동작하지 않습니다. 반드시 AppServiceProvider 방식을 사용해야 합니다.
  • PHP 버전: lcobucci/jwt 라이브러리 의존성이 있으므로, PHP 8.1 이상 환경을 권장합니다.

보안 고려 사항

  • .p8 개인 키 파일은 웹 루트(public/) 외부에 저장해야 합니다.
  • Laravel Sail 환경에서는 컨테이너 내부 절대 경로(/var/www/html/cert/AuthKey_XYZ.p8)를 사용하고, docker-compose.ymlvolumes에 키 파일 경로를 마운트해야 합니다.
  • Laravel Valet 환경에서는 macOS 로컬 절대 경로를 그대로 사용할 수 있으나, .gitignore.p8 파일을 반드시 추가해야 합니다.

Apple 정책 특이사항 (한국 앱스토어 관련)

  • iOS/macOS 앱에서 소셜 로그인을 제공할 경우 Apple은 "Apple로 로그인" 버튼을 반드시 포함하도록 강제합니다. 한국 앱 개발사가 Laravel 백엔드와 연동하는 경우 이 패키지가 사실상 필수입니다.
  • App Store Connect에서 "Sign In with Apple" 기능을 활성화하고, Apple Developer의 Identifiers > Services IDs 설정이 선행되어야 합니다.

6개월 만료 운영 리스크

수동 JWT 방식을 사용하는 팀에서 갱신을 놓치는 경우, 운영 중 모든 Apple 로그인이 일시에 차단됩니다. 자동 생성 방식(private key 방식)을 기본으로 채택하고, 인프라팀과 키 파일 백업 정책을 사전에 합의할 것을 권장합니다.


실무 체크리스트

Apple Developer Console 설정

  • App ID 생성 및 "Sign In with Apple" 기능 활성화
  • Services ID 생성 (이것이 APPLE_CLIENT_ID로 사용됨 — App ID와 혼동 주의)
  • Services ID에 도메인 및 Return URL 등록 (APPLE_REDIRECT_URI와 정확히 일치해야 함)
  • Keys 메뉴에서 "Sign In with Apple" 키 생성 후 .p8 파일 다운로드 (재다운로드 불가)
  • APPLE_KEY_ID(키 생성 시 표시되는 10자리 ID) 및 APPLE_TEAM_ID(Apple Developer 계정 Team ID) 확인

패키지 설치 및 환경 설정

  • composer require socialiteproviders/apple 실행
  • .env에 필요한 환경 변수 추가:
APPLE_CLIENT_ID=com.yourcompany.yourapp.service # Services ID APPLE_CLIENT_SECRET= # private key 방식 사용 시 비워둠 APPLE_KEY_ID=XXXXXXXXXX APPLE_TEAM_ID=XXXXXXXXXX APPLE_PRIVATE_KEY=/var/www/cert/AuthKey_XXXXXXXXXX.p8 APPLE_REDIRECT_URI=https://yourapp.com/auth/apple/callback APPLE_JWT_ISSUED_TIME_LEEWAY=PT5S # 시계 불일치 대비 여유값
  • .p8 파일을 웹 루트 외부에 배치 및 .gitignore 등록 확인
  • config/services.php에 apple 설정 블록 추가

Laravel 버전별 이벤트 리스너 등록

  • Laravel 11+: AppServiceProvider::boot()Event::listen() 방식으로 등록
  • Laravel 10 이하: EventServiceProvider::$listenAppleExtendSocialite::class.'@handle' 등록

로컬 개발 환경 (Valet / Sail)

  • Valet: https:// 환경 필수 — valet secure 명령으로 TLS 활성화 (Apple은 HTTP 콜백 허용 안 함)
  • Sail: docker-compose.yml.p8 파일 볼륨 마운트 추가:
volumes: - ./cert:/var/www/cert:ro
  • Apple Developer Console의 Return URL에 로컬 도메인 추가 (예: https://yourapp.test/auth/apple/callback)

스테이징 / 운영 배포

  • .p8 파일을 CI/CD 시크릿 또는 서버 배포 스크립트로 안전하게 전달 (Git에 절대 포함 금지)
  • 운영 서버의 파일 권한 확인: .p8 파일은 640 또는 600으로 설정
  • 수동 JWT 방식 사용 팀: 6개월 만료일 기준 5개월 차에 갱신 알림을 팀 캘린더에 등록
  • 최초 로그인 시에만 name이 반환됨을 DB 설계에 반영 (이후 로그인 시 null 처리 로직 필수)
  • 콜백 컨트롤러에서 email이 null인 경우 처리 로직 추가 (Apple은 이메일 숨기기 옵션 제공)

테스트

  • 신규 Apple ID로 최초 로그인 → name, email 정상 수신 확인
  • 동일 Apple ID로 재로그인 → name이 null인 경우 정상 처리 확인
  • "이메일 숨기기" 옵션 선택 시 릴레이 이메일(@privaterelay.appleid.com) 정상 저장 확인
  • JWT_ISSUED_TIME_LEEWAY 조정 후 InvalidStateException 재현 여부 확인