본문 바로가기
PulseBoard

🤔 SocialAuthCoordinator 생성 조건 및 구조 설계

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

1️⃣ SocialAuthCoordinator를 만든 이유 (왜 이 클래스가 필요했나)

소셜 로그인은 한 단계짜리 작업이 아니다.

카카오 로그인을 예로 들면 실제 흐름은 이거야:

  1. Kakao SDK → accessToken 발급
  2. 서버(Firebase Functions) → accessToken 검증
  3. 서버 → Firebase Custom Token 생성
  4. 앱 → Firebase Auth signIn
  5. Firebase Auth → 인증 상태 변경

👉 “로그인”이라는 말 하나에 4~5개의 책임이 섞여 있음

 

❌ Coordinator가 없을 때의 문제

만약 이걸 AuthService 하나에 몰아넣으면:

  • Kakao / Naver SDK를 알아야 하고
  • Firebase Functions 호출을 알아야 하고
  • Custom Token 구조를 알아야 하고
  • Firebase Auth API도 알아야 함

즉,

AuthService가 ‘모든 인증 세계관’을 다 아는 God Object가 됨

이 상태가 되면:

  • 테스트 불가
  • 교체 불가
  • provider 추가 시 코드 폭발

 

2️⃣ “그래서 왜 Coordinator인가?”

Coordinator의 핵심 역할은 딱 하나야

여러 단계를 “하나의 유스케이스”로 묶어주는 것

SocialAuthCoordinator는:

  • Kakao SDK ❌ (여기서 안 다룸)
  • UI ❌
  • 화면 전환 ❌

오직 이것만 함 👇

“소셜 accessToken → Firebase 인증 상태”로 변환

 

그래서 이름도 정확히 SocialAuthCoordinator

 

3️⃣ 로직 구조에서의 위치 (중요)

현재 구조를 한 줄로 요약하면 이거야:

UI / SDK
  ↓
AuthService
  ↓
SocialAuthCoordinator
  ↓
Firebase Functions + Firebase Auth

여기서 각 계층의 책임을 보면 👇

계층 책임
KakaoAuthHandler SDK 로그인, accessToken 획득
AuthService 로그인 진입점, provider 분기
SocialAuthCoordinator 소셜 인증 → Firebase 인증 변환
FirebaseFunctionsService 서버 호출
FirebaseAuthService Firebase Auth signIn

📌 Coordinator는 ‘경계 변환기’ 역할
→ 외부 인증 세계 → Firebase 인증 세계

 

4️⃣ 왜 async/await를 여기서는 써도 되는가?

이 포인트도 중요해.

SocialAuthCoordinator의 특징

  • UI 호출 ❌
  • 외부 앱 이동 ❌
  • SceneDelegate ❌
  • 순수 비동기 작업만 있음

하는 일은 전부:

  • 네트워크
  • 서버
  • Firebase SDK

👉 100% 앱 내부 비동기 작업

그래서 여기서는:

func signIn(...) async throws

 

이게 가장 자연스럽고, 가장 안전한 형태.

 

5️⃣ “AuthService가 아니라 왜 Coordinator인가?”

이 질문 정말 중요하다.

AuthService의 역할

  • “로그인을 시작한다”
  • “어떤 provider인지 결정한다”
  • “SDK 핸들러를 선택한다”

👉 진입점 (Facade)

SocialAuthCoordinator의 역할

  • “이 accessToken을 Firebase 인증으로 바꾼다”

👉 유스케이스 실행자

즉,

AuthService는 ‘무엇을 할지’,
Coordinator는 ‘어떻게 완료할지’

 

이 분리가 되어 있어서 구조가 깨끗한 거야.

 

📖 이 클래스를 만들어둔 진짜 가치 

이 구조 덕분에 나중에:

  • ✅ Naver 추가
  • ✅ Google Custom Token
  • ✅ Server 인증 정책 변경
  • ✅ Firebase → 다른 Auth로 교체

전부 Coordinator 내부만 수정하면 돼.

AuthService / ViewModel / UI는 그대로.

 

▶️ 전체코드 

import Foundation


// MARK: - SocialAuthCoordinator

/// 소셜 로그인(Kakao, Naver)을 Firebase 인증으로 변환하는 Coordinator 구현체입니다.
///
/// 📌 이 클래스의 핵심 목적은
/// "소셜 인증 세계"와 "Firebase 인증 세계"를 분리하는 것입니다.
///
/// 책임:
/// 1. 소셜 SDK에서 발급된 accessToken을 입력으로 받습니다.
/// 2. Firebase Functions를 통해 서버에서 Custom Token을 발급받습니다.
/// 3. 발급된 Custom Token으로 Firebase Auth 인증을 완료합니다.
///
/// ❗️이 클래스는 UI, SDK 호출, 화면 전환을 전혀 알지 않습니다.
/// 오직 "인증 변환(use case)"만 담당합니다.
///
/// AuthService는 이 Coordinator에만 의존하며,
/// Firebase Functions, Custom Token, Firebase Auth의
/// 구체적인 구현 디테일을 알 필요가 없습니다.
///
/// 👉 결과적으로
/// - AuthService는 단순해지고
/// - provider 확장이 쉬워지며
/// - 인증 로직 테스트가 가능해집니다.
final class SocialAuthCoordinator: SocialAuthCoordinating {

    
    // MARK: - Dependencies

    /// Firebase Functions 호출을 담당하는 인터페이스
    private let functionsService: FirebaseFunctionsServicing

    /// Firebase Auth 인증을 담당하는 인터페이스
    private let authService: FirebaseAuthServicing

    
    // MARK: - Initializer

    /// SocialAuthCoordinator를 초기화합니다.
    ///
    /// - Parameters:
    ///   - functionsService: Firebase Functions 호출을 담당하는 서비스
    ///   - authService: Firebase Auth 인증을 담당하는 서비스
    init(
        functionsService: FirebaseFunctionsServicing,
        authService: FirebaseAuthServicing
    ) {
        self.functionsService = functionsService
        self.authService = authService
    }

    
    // MARK: - SocialAuthCoordinating

    /// 소셜 로그인 accessToken을 이용해 Firebase 인증을 수행합니다.
    ///
    /// 이 메서드는 다음 순서로 동작합니다:
    /// 1. 전달받은 provider가 지원되는 소셜 로그인인지 검증합니다.
    /// 2. Firebase Functions를 호출하여 Custom Token을 요청합니다.
    /// 3. 응답에서 Custom Token을 파싱합니다.
    /// 4. Firebase Auth에 Custom Token으로 로그인합니다.
    ///
    /// - Parameters:
    ///   - accessToken: Kakao / Naver SDK에서 발급된 accessToken
    ///   - provider: 소셜 로그인 제공자 타입
    ///
    /// - Throws:
    ///   - AuthError.unsupportedProvider: 지원하지 않는 provider인 경우
    ///   - AuthError.invalidCustomToken: Custom Token 파싱 실패
    ///   - Firebase 관련 에러
    func signIn(
        with accessToken: String,
        provider: SocialLoginProvider
    ) async throws {
        
        // 1️⃣ 지원 Provider 검증
        try validate(provider)

        // 2️⃣ Firebase Functions 호출
        let response = try await functionsService.requestCustomToken(
            accessToken: accessToken,
            provider: provider
        )

        // 3️⃣ Custom Token 파싱
        guard let customToken = response.customToken else {
            throw AuthError.invalidCustomToken
        }

        // 4️⃣ Firebase Auth 로그인
        try await authService.signIn(withCustomToken: customToken)
    }
}


// MARK: - Private Helpers

private extension SocialAuthCoordinator {

    /// 지원 가능한 소셜 로그인 Provider인지 검증합니다.
    ///
    /// - Parameter provider: 소셜 로그인 제공자
    /// - Throws: AuthError.unsupportedProvider
    func validate(_ provider: SocialLoginProvider) throws {
        switch provider {
        case .kakao, .naver:
            return
        default:
            throw AuthError.unsupportedProvider
        }
    }
}
728x90
LIST