본문 바로가기
PulseBoard

🤔 카카오, 네이버 로그인 - accessToken 처리 방향

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

🔑 결론 한 줄

Firebase Functions 연동 → Custom Token 파싱 → signIn(withCustomToken:) 는
AuthService 안에 직접 쓰지 말고,
“공용 컴포넌트”로 분리하는 게 정답이다.

 

 

1️⃣ 지금 정확히 짚은 문제의 본질

의문을 구조로 풀어보면 이거야 👇

Kakao 로그인
 ├─ KakaoAuthHandler → accessToken
 └─ (공통 단계)
     ├─ Firebase Functions 호출
     ├─ Custom Token 파싱
     └─ Firebase signIn

Naver 로그인
 ├─ NaverAuthHandler → accessToken
 └─ (공통 단계)
     ├─ Firebase Functions 호출
     ├─ Custom Token 파싱
     └─ Firebase signIn

 

👉 이 아래 단계는 완전히 동일
👉 provider만 다를 뿐, 흐름은 100% 같다

“이걸 AuthService 안에 박아두면,
네이버 붙일 때 코드가 중복될 것 같은데?”

 

정확하다.

 

2️⃣ 선택지 정리 (그리고 왜 하나만 정답인지)

❌ 선택지 A: AuthService 안에 전부 작성

final class AuthService {
    func loginWithKakao() {
        // accessToken
        // functions 호출
        // signIn
    }

    func loginWithNaver() {
        // accessToken
        // functions 호출
        // signIn
    }
}

 

문제점

  • Kakao / Naver 로직이 부분 중복
  • Functions URL 하드코딩 중복
  • 테스트 어려움
  • AuthService가 너무 비대해짐

👉 실무에서 가장 먼저 리팩토링 대상

 

 ❌ 선택지 B: AuthService extension으로 분리

extension AuthService {
    func exchangeCustomToken(...) { ... }
}

 

이건 구조만 나눈 것이지,
책임 분리는 아님.

👉 “코드 위치만 다른 AuthService”

 

✅ 선택지 C (정답): 공용 컴포넌트로 분리

📌 구조 그림

AuthService
 ├─ KakaoAuthHandler   → accessToken
 ├─ NaverAuthHandler   → accessToken (예정)
 └─ SocialAuthCoordinator (공용)
       ├─ Firebase Functions 호출
       ├─ Custom Token 파싱
       └─ Firebase signIn

 

👉 이게 정답 구조

 

✅ 이 설계의 “근거”

이건 임의 설계가 아니라 아래 원칙에 딱 맞아.

  • SRP (단일 책임 원칙)
  • Clean Architecture – UseCase 분리
  • Coordinator 패턴
  • Firebase 공식 가이드: Custom Token 로그인은 클라이언트 공용 흐름

실무에서도:

  • Kakao / Naver / Apple / Google
  • 모두 accessToken까지만 다르고
  • Firebase 로그인 파이프라인은 하나다

 

SocialAuthCoordinator가 필요한가?

이유부터 명확히 하자면:

  • 네이버 / 카카오는
    각 SDK에서 accessToken을 얻는 방식은 다르지만
  • Firebase Auth + Functions로 넘기는 흐름은
    완전히 동일

즉 역할이 이렇게 나뉘어야 깔끔해 👇


책임 담당
네이버 SDK 로그인 NaverAuthProvider
카카오 SDK 로그인 KakaoAuthProvider
accessToken → Firebase 인증 SocialAuthCoordinator
앱 전체 인증 흐름 AuthService

👉 그래서 SocialAuthCoordinator는 반드시 필요한 추상화야.

SocialTokenProviding 새로 만들어야 할까?

❌ 결론: “새로 만들 필요 없음”

✅ 정답: 기존 KakaoAuthProviding의 개념을 일반화해서 이름만 바꾸자

왜냐하면

네이버 / 카카오의 공통점은 딱 이거 하나야:

accessToken을 발급한다

 

그래서 프로토콜의 본질은 이미 정해져 있어.

🔁 리네이밍 추천

 기존 KakaoAuthProviding → 이렇게 변경

protocol SocialTokenProviding {
    func fetchAccessToken() async throws -> String
}
final class KakaoAuthProvider: SocialTokenProviding { ... }
final class NaverAuthProvider: SocialTokenProviding { ... }

 

 

SocialLoginProvider enum에 apple / google 있어도 되나?

🔥 이게 핵심 질문인데, 결론은 이거야

지금은 써도 된다
단, Coordinator 레벨에서는 “사용 범위”를 제한하자

왜 써도 되냐면

  • 이 enum의 의미는 “Firebase에 전달할 provider 타입”
  • Firebase 관점에서는
    → apple / google / kakao / naver 전부 같은 레벨의 provider임
enum SocialLoginProvider {
    case apple
    case google
    case kakao
    case naver
}

 

이 자체는 문제 없음 ❌❌

 

그럼 “네이버·카카오만 필요”한데 왜 괜찮냐?

👉 의존 방향을 제한하면 됨

❌ 잘못된 사용 

SocialAuthCoordinator.signIn(
    accessToken: token,
    provider: .apple // ❌ 책임 침범
)

✅ 올바른 사용

SocialAuthCoordinator.signIn(
    accessToken: token,
    provider: .kakao
)

 

그리고 Coordinator 내부에서만 제한해도 충분해 👇

guard provider == .kakao || provider == .naver else {
    throw AuthError.unsupportedProvider
}

 

즉,

  • enum은 확장 가능하게 두고
  • Coordinator가 허용 범위를 통제

👉 이게 “닫혀 있지 않은 설계”야

 

추천 구조 (이게 제일 안정적) 

protocol SocialTokenProviding {
    func fetchAccessToken() async throws -> String
}

enum SocialLoginProvider {
    case apple
    case google
    case kakao
    case naver
}


/// 소셜 로그인(Kakao, Naver)을 Firebase 인증으로 연결하는 Coordinator 인터페이스입니다.
///
/// 이 Coordinator는
/// - Kakao / Naver SDK에서 발급된 accessToken을 전달받아
/// - Firebase Auth (또는 Firebase Functions)를 통해
///   공용 인증 플로우를 수행하는 책임을 가집니다.
///
/// AuthService는 이 Protocol에만 의존하며,
/// 각 소셜 로그인 SDK의 구현 디테일을 알 필요가 없습니다.
///
/// 👉 역할 요약
/// - 소셜 로그인 Provider 간 분기 처리
/// - Firebase 인증 공통 로직 캡슐화
/// - AuthService와 소셜 SDK 사이의 중간 계층
protocol SocialAuthCoordinating {
    func signIn(
        with accessToken: String,
        provider: SocialLoginProvider
    ) async throws
}

 

실제 흐름

[LoginVC]
   ↓
[KakaoAuthProvider]  ── fetchAccessToken()
   ↓
[SocialAuthCoordinator] ── Firebase / Functions
   ↓
[AuthService] ── auth state 관리
728x90
LIST