OverView

애플은 2019년 애플 로그인 기능을 발표했습니다. 동시에 앱 내에 다른 소셜 로그인 서비스를 사용하고 있다면 반드시! 애플 로그인을 제공해야 한다는 심사지침도 함께 내놓았습니다.

브랜디는 당시 이미 구글과 페이스북 로그인을 제공하고 있어서 애플 로그인을 필수로 적용해야 하는 상황에 마주했고, 20년 4월에 애플 로그인을 적용한 후 출시하였습니다.

이번 글에서는 iOS에서 브랜디에 애플 로그인을 적용한 과정을 정리하고자 합니다.

💡
타 플랫폼 적용 방법은 다루지 않습니다.
iOS 13.0 이상만 Sign In with Apple 기능을 사용할 수 있습니다.


1. Capability 추가하기

애플 로그인을 적용하기 위한 기본 설정부터 알아보겠습니다.

먼저 XCode Project의 Signing & Capabilities를 열어 왼쪽 상단의 + Capability를 선택합니다.

athena

이후 Sign In with Apple을 검색하여 클릭하면 추가되는데, 다른 소셜 로그인 서비스들을 개발할 때처럼 SDK를 사용하지도 않고 별다른 설정도 필요 없습니다.

아래 스크린샷과 같이 하나만 추가해주면 설정은 끝입니다!

athena

Apple Developer - Certificates, Identifiers & Profiles 메뉴에서 애플로그인을 추가할 앱에 아래와 같이 Sign In with Apple을 선택해줍니다.

athena


2. 버튼 추가하기

애플 로그인을 위한 버튼을 만들 차례입니다.

ASAuthorizationAppleIDButton 클래스를 사용하여 코드상으로 시스템이 제공해 주는 로그인 버튼을 생성하는 방법도 있지만, 다른 로그인 버튼들과 디자인 통일성을 위해 커스텀 하여 사용하였습니다.

HIG(Human Interface Guidelines) 에 Sign In with Apple 버튼 디자인 기준이 세세하게 명시되어 있는데, 버튼을 커스텀 해야 하는 상황이라면 꼼꼼히 살펴보고 작업을 해야 합니다.

아래 스크린샷은 애플에서 제공한 로고 이미지를 원형으로 커스텀 하여 브랜디에 적용한 모습입니다.

athena


3. Apple Login 인증 창 띄우기

버튼이 생겼으니 본격적인 기능을 적용할 수 있게 되었습니다.

브랜디는 다수의 소셜 로그인 방법이 존재하기 때문에 자칫 코드가 복잡해질 수 있어, 애플 로그인만을 담당하는 Manager를 별도로 생성하였습니다.

애플 로그인 기능을 사용하기 위해 AuthenticationServices 프레임워크를 import 해준 뒤, 로그인 인증 창을 띄우기 위한 코드부터 작성합니다.

import AuthenticationServices

final class AppleLoginManager: NSObject {
    weak var viewController: UIViewController?
    weak var delegate: AppleLoginManagerDelegate?
    
    func setAppleLoginPresentationAnchorView(_ view: UIViewController) {
        self.viewController = view
    }
}

extension AppleLoginManager: ASAuthorizationControllerPresentationContextProviding 
    @available(iOS 13.0, *)
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return viewController!.view.window!
    }
}

애플 로그인 인증 창을 띄우기 위해 먼저 어느 화면에 띄울지 결정을 해주어야 합니다.

setAppleLoginPresentationAnchorView 는 해당 함수를 호출하는 ViewController의 화면 위에 Apple Login 인증 창을 띄울 수 있도록 하기 위해, AppleLoginManager에 view를 저장하는 역할을 합니다.

ASAuthorizationControllerPresentationContextProviding는 인증창을 띄워야 할 뷰의 window를 반환해줍니다. 여기선 set할때 전달받은 viewController의 window를 반환합니다.

두 번째로 로그인 버튼이 있는 클래스에서도 세팅이 필요합니다.

추가로, 애플 로그인 기능은 iOS 13 이상의 기기에서만 사용 가능하기 때문에 이하의 기기에서는 버튼이 미 노출 되도록 작업하였습니다.

import UIKit
import AuthenticationServices

final class SNSLoginButtonContainer: UIView {
// ... 생략 ...

    //Apple Login Button Setting
    private func showSignInWithAppleButton() {
    if #available(iOS 13.0, *) {
            stackViewButtonContainer.addArrangedSubview(buttonAppleLogin)
            if let vc = viewController {
                appleLoginManager.setAppleLoginPresentationAnchorView(vc)
                appleLoginManager.delegate = self
            }
        }
    }

// ... 생략 ...

    //Apple Login Button Clicked
    @available(iOS 13.0, *)
    private func handleAppleLoginButtonClicked() { //버튼을 누를 때 수행되는 핸들러
        let request = ASAuthorizationAppleIDProvider().createRequest() //request 생성
        //요청을 날릴 항목 설정 : 이름, 이메일
        request.requestedScopes = [.fullName, .email]
        //request를 보내줄 controller 생성
        let controller = ASAuthorizationController(authorizationRequests: [request])
        //controller의 delegate와 presentationContextProvider 설정
        controller.delegate = appleLoginManager
        controller.presentationContextProvider = appleLoginManager
        controller.performRequests() //요청 보냄
    }

// ... 생략 ...
}

handleAppleLoginButtonClicked() 는 Apple 로그인 버튼을 클릭하였을 때 호출이 되는데, 이때 Apple 계정의 fullName과 email 두 가지 정보를 요구할 수 있습니다.

performRequests() 가 호출되면 다음과 같은 창이 하나 나타납니다.

11
Apple Login 인증 화면


Apple Login 인증 창에서 우리가 요구한 사용자의 email과 FullName을 보여줍니다. 이때 사용자는 이메일 공유 방법을 선택할 수 있습니다.

이메일 공유하기를 선택하면 내 Apple ID 계정의 이메일 그대로 앱에 전달합니다.

이때 나의 이메일 가리기를 선택하게 되면 실제 이메일을 사용하여 ~@privaterelay.appleid.com 의 형태로 원래 이메일을 통해 임의로 생성된 이메일 주소를 받게 됩니다.

11
이메일 가리기를 통해 전달받은 이메일 값


더 나아가서 최초 애플 로그인을 시도 시에만 email과 fullName을 애플에서 제공되며, 두 번째 인증부터는 제공되지 않아 개인정보를 좀 더 안전하게 보호할 수 있습니다.

밑에서 설명할 Apple ID 사용 중단을 통해 사용자의 email과 fullName을 다시 받을 수도 있습니다.

4. Apple Login 인증 결과 다루기

Apple ID의 인증 결과 및 요청 값인 email과 fullName을 전달받기 위해 ASAuthorizationControllerDelegate를 채택합니다.

인증이 성공하면 didCompleteWithAuthorization, 실패하면 didCompleteWithError가 호출됩니다.

앱에서 사용자가 인증을 마치면 credential에서 요청한 정보들인 userIdentifier, email, fullName을 여기서 받을 수 있습니다. userIdentifier는 Apple ID의 고유한 값으로 변경되지 않습니다.

사용자 정보는 didCompleteWithAuthorization에서 아래와 같이 확인할 수 있습니다.

extension AppleLoginManager: ASAuthorizationControllerDelegate {
    
    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {

            let userIdentifier = appleIDCredential.user //userIdentifier
            let userName = appleIDCredential.fullName //fullName
            let userEmail = appleIDCredential.email //email

            delegate?.appleLoginSuccess()//apple 로그인 성공
        }
    }
    
    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        delegate?.appleLoginFail() //apple 로그인 실패
    }
    
}


5. 세션 변경 처리하기

사용자가 브랜디 앱 진입 시점 또는 사용 도중에 Apple ID 사용중단 버튼을 클릭하면 어떻게 될까요? 최초 인증 시도 상태로 돌아가서 애플로그인을 처음 시도했을 때와 같이 이메일 공유 여부를 결정할 수 있는 인증창을 다시 볼 수 있게 됩니다. 또한 개발자는 사용자의 email과 fullName을 다시 전달받을 수 있게됩니다.

Apple ID 사용중단 버튼은 [ 설정 > Apple ID 클릭 > 암호 및 보안 > 내 Apple ID를 사용하는 앱 ] 에서 찾을 수 있습니다.

11
Apple ID 사용중단 화면


이전에 조회한 사용자 정보 값 중 appleIDCredential.user 를 통해 현재 어떤 인증 상태인지 알 수 있는데,

아래 코드와 같이 ASAuthorizationAppleIDProvider 내의 getCredentialState 를 사용하여 현재 사용자 인증 상태를 조회할 수 있습니다.

//AppDelegate.swift
func applicationDidBecomeActive(_ application: UIApplication) {
    if #available(iOS 13.0, *) {
             ...
            let appleIDProvider = ASAuthorizationAppleIDProvider()
            appleIDProvider.getCredentialState(forUserID: userIdentifier) { (credentialState, error) in
                switch credentialState {
                case .authorized:
                    //인증성공 상태
                case .revoked:
                    //인증만료 상태
                default:
                    //.notFound 등 이외 상태
                }
            }
        ...
    }
}

Apple ID가 사용 중단된 경우에는 credentialState 값이 revocked라는 타입으로 들어오게 됩니다.

개발자가 사용자 정보를 확인하는 용도가 아닌, 사용자가 앱에서 Apple ID 사용을 중지하거나 기기에서 로그아웃을 하기 위한 경우에도 사용 중단을 할 수 있습니다. 따라서 사용중단 후 앱을 재 실행한 경우, 그리고 앱을 사용하던 도중(background에 앱이 내려가 있는 경우)에 사용중단이 되는경우 두가지 케이스 모두 처리해주어야 합니다. 브랜디에선 didFinishLaunchingWithOptions 와 applicationDidBecomeActive 두 군데서ASAuthorizationAppleIDProvider()를 통하여 현재 인증 상태를 조회하고 인증상태를 나타내는 credentialState의 값이 revocked 이면 앱에서 자동 로그아웃하도록 하였습니다.

만약 잘못된 UserIdendifier로 credentialState를 조회했거나, 애플 로그인 시스템에 문제가 있을 때에는 notFound status가 반환됩니다.


Conclusion

애플 로그인은 이름, 이메일 외에 모든 불필요한 정보를 제공하지 않기 때문에 안전하고 아이폰 사용자라면 가장 쉬운 로그인 방법일 것 같습니다.

ios개발자로서 애플로그인은 시스템에서 매우 많은 것을 제공해주기 때문에 가이드만 잘 따라 하면 비교적 쉽게 적용할 수 있는 기능이라고 생각합니다. 오늘도 열심히 고군분투할 주니어 개발자들에게 이 글이 용기가 되길 바라며 마치겠습니다. :)

참고자료

Sign in with Apple Documentation

HIG(Human Interface Guidelines)


조서현 | MA 개발팀
chosh@brandi.co.kr
브랜디, 오직 예쁜 옷만