본문 바로가기
PulseBoard

🤔 Firebase Apple 로그인 튜토리얼 코드 → 우리가 만든 구조

by 밤새는 탐험가89 2025. 12. 22.
728x90
SMALL

https://explorer89.tistory.com/568

 

UIKit + Firebase Apple 로그인, MVVM & Coordinator로 설계하기

📁 Auth 모듈 구조 & 역할 정리Auth ├─ SocialLoginProvider.swift ├─ AuthError.swift ├─ AuthProviding.swift ├─ AppleAuthHandler.swift ├─ AuthService.swift └─ AuthViewModel.swift 왜 Auth 구조를 따로 설계했는가?SNS

explorer89.tistory.com

 

https://firebase.google.com/docs/auth/ios/apple?hl=ko&authuser=0

 

Apple을 사용하여 인증  |  Firebase

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

firebase.google.com

 

🔎 Firebase 공식 문서가 보여주는 것 (핵심)

Firebase 공식 문서는 다음과 같은 흐름을 보여줘.

✔ 필수 설정

  1. Firebase Console에서 Authentication > Sign-in Method > Apple을 활성화
  2. Apple Developer에서 서비스 ID와 비밀 키(Key) 설정
  3. Xcode에서 Sign In with Apple capability 활성화

👉 이 부분은 설정 중심이야 — 프로젝트 준비 단계.

 

📌 공식 문서가 설명하는 코드 흐름 (단계)

① Apple 로그인 요청 생성 

let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)

 

 

  • Apple 로그인 요청을 만든다
  • nonce를 포함하는 점이 중요

 

② Apple 인증 UI 표시 & Delegate 처리 

let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()

 

 

 

  • Apple 로그인 화면이 뜨고
  • 성공/실패가 ASAuthorizationControllerDelegate로 전달됨

 

③ Apple 인증이 끝나면 Firebase Auth로 로그인

let credential = OAuthProvider.credential(withProviderID: "apple.com",
  idToken: idTokenString,
  rawNonce: nonce
)
Auth.auth().signIn(with: credential)

 

 

 

  • Apple에서 얻은 토큰으로 Firebase credential을 만들고
  • Firebase Auth에 로그인 요청을 하는 흐름

 

🧠 공식 문서 코드의 본질

공식 문서의 구현은 기본 동작을 보여주는 예제예요:

✅ Apple 로그인 →
✅ Firebase Auth 로그인

하지만 구조적으로 보면 한 화면(VC) 안에 모든 것이 들어 있어.

 

 

 


0) 출발점: Firebase 튜토리얼 코드(전형적인 형태)

튜토리얼은 대개 VC 안에 아래가 다 들어있어.

  • Apple 로그인 요청 만들기
  • nonce 만들기 + sha256
  • Apple callback(delegate) 처리
  • Firebase credential 생성
  • Firebase signIn 실행
  • 성공/실패에 따라 화면 전환

즉, ViewController가 이런 “God Object”가 된다:

LoginViewController
 ├─ Apple 로그인 요청
 ├─ nonce/sha256
 ├─ delegate 처리
 ├─ Firebase signIn
 └─ 성공 시 화면 전환

 

튜토리얼의 의도: “로그인 되는지”만 보여주기
우리가 원하는 의도: “확장/유지보수/테스트 가능한 구조”

 

1단계: “책임 분해”부터 시작한다 (제일 중요)

튜토리얼 코드에서 책임을 문장으로 뽑아보면:

  1. 어떤 SNS로 로그인할지 결정
  2. Apple 로그인 요청 생성 (requestedScopes, nonce)
  3. nonce 생성 + sha256 해싱 (CryptoKit)
  4. Apple delegate callback 처리
  5. Firebase credential 생성
  6. Firebase에 signIn 요청
  7. 결과(성공/실패) 전달
  8. 로그인 상태 변화에 따른 화면 전환

여기서 규칙 하나:

✅ 책임이 2개 이상이면 분리 대상이다.

 

그래서 분리가 시작된다.

 

2단계: “암호/유틸 책임”을 VC 밖으로 뺀다

🎯 튜토리얼에 있던 것

  • randomNonceString()
  • sha256()

이건 UI 책임이 아니고, Apple 전용도 아니고, Auth 도메인의 핵심 유틸이야.

✅ 결과

➡️ CryptoUtils.swift로 이동

CryptoUtils.swift
 ├─ randomNonceString()
 └─ sha256()

 

좋아지는 점

  • ViewController에서 CryptoKit 사라짐
  • 테스트/재사용 가능
  • Auth 흐름이 깨끗해짐

 

3단계: “Apple 로그인 흐름”을 전담 객체로 격리한다

튜토리얼에서는 VC가 ASAuthorizationControllerDelegate를 붙잡고 있어.

“Apple 로그인 delegate는 UI 책임인가?”

Apple 로그인 delegate는 인증 구현 디테일이다.
UI가 알아야 할 게 아니다.

 

그래서 Apple 관련 책임(2~6)을 뽑아낸다:

  • nonce 생성
  • request 생성 + nonce 주입
  • delegate 구현
  • credential 변환
  • firebase signIn

✅ 결과

➡️ AppleAuthHandler.swift 생성

AppleAuthHandler
 ├─ startLogin()
 └─ ASAuthorizationControllerDelegate 구현

 

좋아지는 점

  • VC가 Apple SDK 의존 제거
  • 나중에 Google 추가해도 VC는 그대로
  • Apple 로직이 한 파일로 응집됨 (유지보수 쉬움)

 

4단계: “Auth 통합 창구”를 만든다 (AuthService)

튜토리얼에서는 “로그인 버튼 누르면 바로 애플 로그인”이지만,
실제로는 SNS가 늘어난다:

  • Apple
  • Google
  • Kakao
  • Naver

이때 필요한 게:

“로그인 방식을 선택해주는 중앙 창구”

✅ 결과

➡️ AuthService.swift

AuthService는 이렇게 된다:

  • Provider에 따라 handler 분기
  • logout / deleteAccount 같은 공통 기능 제공
  • observeAuthState 같은 Firebase Auth 상태 관찰 제공
AuthService
 ├─ login(provider:)
 ├─ logout()
 ├─ deleteAccount()
 └─ observeAuthState()

 

좋아지는 점

  • ViewModel/VC는 “provider 선택”만 하면 됨
  • 공통 기능은 AuthService에 모임 (중복 제거)
  • 확장 시 파일 추가만 하면 됨

 

5단계: “의존성 방향”을 거꾸로 만든다 (Protocol 도입)

여기서 실무 설계의 핵심이 들어간다.

튜토리얼 구조는:

VC → Firebase/Apple SDK에 직접 의존

 

우리는 이렇게 바꾼다:

ViewModel → Protocol(AuthProviding)에 의존
구현체(AuthService)는 바꿀 수 있게

✅ 결과

➡️ AuthProviding.swift

AuthProviding (Protocol)
 ├─ login(...)
 ├─ logout()
 ├─ deleteAccount()
 └─ observeAuthState()

 

좋아지는 점

  • MockAuthService 만들어 테스트 가능
  • Firebase SDK를 UI 레이어에서 완전히 숨김
  • “교체 가능 구조”가 됨

 

6단계: “이벤트가 아니라 상태”로 앱을 굴린다 (ViewModel + Coordinator)

튜토리얼은 대개 이렇게 함:

  • 로그인 성공 콜백 안에서 present(HomeVC) 또는 dismiss()

이게 왜 문제냐면:

  • VC가 화면 전환 책임까지 가져감
  • 로그인 성공 시점/위치가 여러 곳에 흩어짐
  • 앱 재실행 / 자동 로그인 처리에 약함

그래서 관점을 바꾼다:

Auth는 “버튼을 눌러 성공했다”가 아니라
“로그인 상태가 바뀌었다”가 본질이다.

✅ 결과

  1. AuthViewModel이 observeAuthState를 구독해 상태 변경을 방출
  2. RootCoordinator가 그 상태를 받아 root를 교체
AuthViewModel
 ├─ login()
 ├─ logout()
 ├─ deleteAccount()
 └─ onAuthStateChanged (uid)

RootCoordinator
 └─ uid nil? Login : Home

 

좋아지는 점

  • 로그인/로그아웃/탈퇴/자동로그인 모두 “상태 변화” 하나로 통일
  • 화면 전환 책임이 Coordinator에 모임
  • VC는 UI만 담당 → 코드가 얇아짐

 

✅ 최종 변환 결과: “튜토리얼 코드가 흩어진 책임을, 모듈로 정리한 것”

튜토리얼(VC 하나에 몰빵)에서 우리가 만든 구조는 이렇게 바뀜:

[튜토리얼]
LoginViewController
 ├─ Apple Request
 ├─ nonce/sha256
 ├─ delegate
 ├─ Firebase signIn
 └─ 화면 전환

[리팩터링 후]
CryptoUtils          : nonce/sha256
AppleAuthHandler     : Apple 인증 흐름
AuthService          : Auth 통합 창구
AuthProviding        : 의존성 역전(인터페이스)
AuthViewModel        : UI ↔ Auth 중재 + 상태 방출
RootCoordinator      : 상태 기반 루트 분기

 

728x90
LIST