iOS/흡구오디 -> 어딨쥐

[흡구오디] 에플 로그인 구현하기

23g 2025. 11. 20.

안녕하세요?

 

잠이 안와서 글 쓰러 하러왓서요

 

밀린 포스팅들 레쓰고...

 

사실 앱 심사 냈는데 2주째 리젝의 늪에서 씨름 중이라

 

억울하고 분하고 슬푸고 엉엉슨

 

그래도...하던건 해야하니까 마음 가담듬고 애플로 로그인 고고싱 ㅠㅠ

 

Apple은 다른 업체의 소셜 로그인 기능 제공 시 apple 로그인을 제공하지 않으면 100% 리젝당한다고 합니다.

그러니까 소셜 로그인 구현한다면 애플 로그인은 필수겠죠

 

참고로 애플 로그인 구현하려면 애플 개발자 계정이 필수랍니다!

그런데 이제 129,000원으로 유료인...

 

0. 애플 개발자 계정 등록하기

https://developer.apple.com/kr/programs/enroll/

 

멤버십 가입하기 - Apple Developer Program

Apple Developer Program에 등록하기 위해 필요한 사항을 알아보세요.

developer.apple.com

 

여기서 등록한 계정으로 Xcode에 로그인하시면 됩니다. (아마 다들 원래 되어있겠지만?)

 

관련 설정은 Xcode에서 Xcode > Settings > Account

가서 해당 애플 계정을 등록해주세요.

 

1. Identifiers 등록하기

링크 : https://developer.apple.com/account/resources/identifiers/list

 

로그인 - Apple

 

idmsa.apple.com

 

 

위 화면에서 식별자 들어가서

 

 

 

 

+ 버튼 클릭

 

 

 

App IDs 누르고 Continue

 

 

 

App 누르고 Continue 

 

 

 

 

설명(영어로 적어야 함) 하고 번들 ID 적어주면 되고

 

(번들 아이디는 여기서 확인 가능합니다.)

 

 

 

 

아래로 쭉 내려서 Sign in with Apple 눌러주기

 

그럼 이 페이지에서 할 설정은 끝납니다!

 

2. Sign in with Apple 추가하기

 

앱 > TARGETS > + Capability 눌러서 Sign in with Apple 추가해주면 됨

 

참고로 위의 0, 1번 과정이 완료되어야 sign in with apple이 보여요!

 

2. Apple 로그인 버튼 만들기

 

그럼 초기 설정을 해줬으니 버튼을 만들어야겠죠?

 

https://developer.apple.com/kr/design/human-interface-guidelines/sign-in-with-apple

 

Apple로 로그인 | Apple Developer Documentation

‘Apple로 로그인’은 앱 및 웹사이트에 로그인할 수 있는 빠르고 개인적인 방법을 제공하여 사람들이 신뢰할 수 있는 일관적인 경험과 여러 계정 및 암호를 기억하지 않아도 되는 편리함을 누릴

developer.apple.com

 

위 링크에서 관련된 내용을 확인 할 수 있고요

 

 

저는 다른 소셜 로그인 버튼이랑 동일하게 만들려고 커스텀 로그인 버튼을 만들었답니다.

 

private let appleButton = UIButton().then {
    $0.setTitle("Apple로 계속하기", for: .normal)
    $0.setTitleColor(.white, for: .normal)
    $0.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
    $0.backgroundColor = .black
    $0.layer.cornerRadius = Metric.cornerRadius
    $0.layer.borderWidth = 1.5
    $0.layer.borderColor = UIColor.black.cgColor
  }
  
  private let appleIcon = UIImageView().then {
    $0.image = UIImage(systemName: "apple.logo")
    $0.tintColor = .white
    $0.contentMode = .scaleAspectFit
  }

 

이렇게 만들어서

 

3. 동작 함수 구독시키기

private func bindAction() {
   ...생략
    
    // 애플 로그인
    self.appleButton.rx.tap
      .subscribe(onNext: { [weak self] in
        self?.startSignInWithAppleFlow()
      })
      .disposed(by: disposeBag)
      
   ...생략     
}

 

동작 함수 구독시켜요

 

 

4. 로직 구현하기

 

관련 내용은 공식 문서를 참고하세용

https://firebase.google.com/docs/auth/ios/apple?hl=ko&authuser=0&_gl=1*sp8b87*_up*MQ..*_ga*MjA4NjAzODQ4Ni4xNzYzNjM1OTU5*_ga_CW55HF8NVT*czE3NjM2MzU5NTkkbzEkZzAkdDE3NjM2MzU5NTkkajYwJGwwJGgw

 

Apple을 사용하여 인증  |  Firebase

의견 보내기 Apple을 사용하여 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Firebase SDK를 통해 엔드 투 엔드 OAuth 2.0 로그인 과정을 실행하여 사용자가 A

firebase.google.com

 

근데 전 솔직히 아직 뭔소린지 모르겠네요 ; _ ;

 

4-1 필요한 프레임워크 import 

먼저, LoginViewController 상단에 필요한 모듈을 임포트합니다.

import FirebaseAuth // Firebase 연동
import CryptoKit // Nonce(논스) 처리를 위해 사용
import AuthenticationServices // Apple 로그인 핵심 프레임워크

 

4-2. Delegate 채택 및 변수 정의

Apple 로그인 처리를 위해 LoginViewController에 두 개의 프로토콜을 채택하고, 생성된 논스 값을 임시로 저장할 변수를 정의합니다.

// MARK: Apple Login
extension LoginViewController: ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {

    // 현재 요청에서 사용된 논스(Nonce) 값을 임시로 저장합니다.
    fileprivate var currentNonce: String?
    
    // ... 나머지 코드
}

 

4-3 Nonce 생성 및 해시 함수

Apple 로그인은 ID 토큰 리플레이 공격(ID Token Replay Attack)을 방지하기 위해 논스(Nonce) 값을 필수로 요구합니다. 이 논스는 요청 시 생성되어 SHA256으로 해시된 후 Apple에 전달되며, 응답으로 받은 ID 토큰에 포함된 논스(해시되기 전)와 일치하는지 확인하여 요청의 유효성을 검증합니다.

라고 하네요 아 글쿤

 

코드를 보면, 안전한 논스 생성을 위한 두 개의 헬퍼 함수가 정의되어 있습니다.

 

1. randomNonceString()

길이 32의 무작위 문자열(Nonce)을 생성합니다. 이는 일회성 토큰으로 보안을 강화합니다.

private func randomNonceString(length: Int = 32) -> String {
 precondition(length > 0)
  var randomBytes = [UInt8](repeating: 0, count: length)
  let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
  if errorCode != errSecSuccess {
    fatalError(
      "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
    )
  }

  let charset: [Character] =
    Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")

  let nonce = randomBytes.map { byte in
    // Pick a random character from the set, wrapping around if needed.
    charset[Int(byte) % charset.count]
  }

  return String(nonce)
}

 

2. sha256(_:)

CryptoKit의 SHA256 함수를 사용하여 생성된 논스 문자열을 해시합니다.

private func sha256(_ input: String) -> String {
    let inputData = Data(input.utf8)
    let hashedData = SHA256.hash(data: inputData)
    // 해시된 데이터를 16진수 문자열로 변환하여 반환
    return hashedData.compactMap { String(format: "%02x", $0) }.joined()
}

 

4-4 Apple 로그인 요청 시작 (startSignInWithAppleFlow)

사용자가 "Apple로 계속하기" 버튼을 탭하면, 이 메서드가 호출됩니다.

func startSignInWithAppleFlow() {
    // 1. 보안을 위한 Nonce(논스) 생성 및 저장
    let nonce = randomNonceString()
    currentNonce = nonce // 응답 검증을 위해 논스 값을 저장합니다.

    // 2. Apple 인증 요청 생성
    let request = ASAuthorizationAppleIDProvider().createRequest()
    request.requestedScopes = [.fullName, .email] // 사용자에게 요청할 정보
    
    // 3. 보안 논스 값 전달 (반드시 해시된 형태)
    request.nonce = sha256(nonce) // SHA256 해시된 논스를 Apple 서버로 보냅니다.
    
    // 4. 인증 컨트롤러 실행
    let controller = ASAuthorizationController(authorizationRequests: [request])
    controller.delegate = self
    controller.presentationContextProvider = self
    controller.performRequests() // Apple 로그인 팝업 실행
}

 

4-5 인증 완료 및 Firebase 연동 (didCompleteWithAuthorization)

인증이 성공적으로 완료되면 ASAuthorizationControllerDelegate의 이 메서드가 호출되며, 여기서 Firebase Authentication과 연동합니다.

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { return }
    
    // 1. 저장된 Nonce 값과 ID Token 추출 확인
    guard let nonce = currentNonce else {
        print("Invalid state: No login request was sent.")
        return
    }
    guard let appleIDToken = appleIDCredential.identityToken,
          let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
        print("Unable to fetch identity token.")
        return
    }
    
    // 2. Firebase Apple 인증 정보 생성 (중요!)
    // Nonce 값과 ID Token을 이용해 Firebase 인증 정보(Credential)를 생성합니다.
    let credential = OAuthProvider.appleCredential(withIDToken: idTokenString,
                                                   rawNonce: nonce, // 저장된 원본 논스 값 전달
                                                   fullName: appleIDCredential.fullName)
    
    // 3. Firebase 로그인 시도
    Auth.auth().signIn(with: credential) { [weak self] authResult, error in
        if let error = error {
            print("Error during Firebase sign-in: \(error.localizedDescription)")
            // 사용자에게 오류를 알리는 처리 필요
            return
        }
        
        print("✅ Apple login success: \(authResult?.user.uid ?? "")")
        self?.goHome() // 성공 시 다음 화면으로 전환
    }
}

 

4-6 팝업 표시를 위한 앵커 제공 (presentationAnchor)

로그인 창(팝업)이 정상적으로 표시되려면 ASAuthorizationControllerPresentationContextProviding 프로토콜을 구현해야 합니다.

func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
    // 팝업이 표시될 윈도우를 반환합니다.
    return view.window!
}

 

후... 이렇게 하면 드디어 애플 로그인 구현 끝!!! 🍎🍏 

 

사실 아직 뭔소리인지 잘모르겠어요 ;;;

 

지피티 도움 받아서 엄청 오래 쓴 게시글이네요

 

이 글을 보고 도움이 되는 분들이 계셨으면....좋겠지만 그리 도움은 안될거 같네여 쩝

 

그럼 안녕히 계세요~!

 

 

댓글