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 공식 문서는 다음과 같은 흐름을 보여줘.
✔ 필수 설정
- Firebase Console에서 Authentication > Sign-in Method > Apple을 활성화
- Apple Developer에서 서비스 ID와 비밀 키(Key) 설정
- 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단계: “책임 분해”부터 시작한다 (제일 중요)
튜토리얼 코드에서 책임을 문장으로 뽑아보면:
- 어떤 SNS로 로그인할지 결정
- Apple 로그인 요청 생성 (requestedScopes, nonce)
- nonce 생성 + sha256 해싱 (CryptoKit)
- Apple delegate callback 처리
- Firebase credential 생성
- Firebase에 signIn 요청
- 결과(성공/실패) 전달
- 로그인 상태 변화에 따른 화면 전환
여기서 규칙 하나:
✅ 책임이 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
- 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는 “버튼을 눌러 성공했다”가 아니라
“로그인 상태가 바뀌었다”가 본질이다.
✅ 결과
- AuthViewModel이 observeAuthState를 구독해 상태 변경을 방출
- 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 : 상태 기반 루트 분기
'PulseBoard' 카테고리의 다른 글
| 🔐 카카오 로그인은 왜 Firebase Email/Password로 쓰면 안 될까? (0) | 2025.12.24 |
|---|---|
| Firebase Google 로그인 튜토리얼을 “실서비스 아키텍처”로 리팩토링하기 (iOS) (0) | 2025.12.23 |
| UIKit + Firebase Apple 로그인, MVVM & Coordinator로 설계하기 (0) | 2025.12.22 |
| 🤔 Firebase - Apple Login 하기 (Firebase Authentication, Apple Developer 설정) (0) | 2025.12.22 |
| Firebase Email/Password 인증을 사용하지 않은 이유— SNS 로그인만 채택한 서비스 설계 판단 기록 (0) | 2025.12.19 |