포스트

Final (21)

회원 탈퇴시 재인증 문제 해결

개요

이번 작업에서는 Apple/Google 로그인 및 로그아웃, 탈퇴 관련 로직을 다음과 같은 목표로 수정 및 보완했다:

  • Apple 로그인 시 불필요한 fullName 처리 제거
  • Google 로그인 시 토큰 저장 기능 추가
  • Google 탈퇴 로직에서 Firebase 재인증 처리 구현 ← 핵심!
  • 중복 코드 및 콘솔 출력 최소화

변경 내역 상세 정리

1. Apple 로그인 (saveApple)

문제

  • 기존에는 fullName을 credential 생성 시 항상 전달했음
  • 하지만 appleCredential.fullName은 최초 로그인 이후 거의 항상 nil → 충돌 가능성

수정 코드

OAuthProvider.appleCredential(withIDToken: tokenString, rawNonce: nonce, fullName: nil)

  • fullName을 명시적으로 nil 처리하여 credential 안정성 확보
  • 콘솔에 노출되던 JWT, 토큰 관련 print 제거

2. Google 로그인 시 토큰 저장

목적

탈퇴 시 Firebase의 reauthenticate 요구에 대응하기 위해
Google 로그인 성공 시 ID Token과 Access Token을 저장한다.

예시 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
googleSignIn(result: GIDSignInResult, completion: @escaping (Result<AuthDataResult?, Error>) -> Void) {
    let idToken = result.user.idToken?.tokenString ?? ""
    let accessToken = result.user.accessToken.tokenString
    
    if idToken.isEmpty {
        completion(.failure(NSError(domain: "GoogleSignIn", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unable to fetch Google tokens"])))
        return
    }

    let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)

    Auth.auth().signIn(with: credential) { result, error in
        if let error = error {
            completion(.failure(error))
        } else {
            UserDefaults.standard.set(idToken, forKey: "googleIDToken")
            UserDefaults.standard.set(accessToken, forKey: "googleAccessToken")
            completion(.success(result))
        }
    }
}
  • 토큰 저장 키: "googleIDToken", "googleAccessToken"

3. Google 탈퇴 시 Firebase 재인증 처리 ← 핵심

문제

Google 사용자가 탈퇴할 때, 재인증이 없다면 다음 에러가 발생:

auth/requires-recent-login
→ Firebase 보안 정책: 사용자 삭제 전에 최근 인증 필요

해결 전략

  • 로그인 시 저장한 토큰을 사용하여 credential 생성
  • reauthenticate(with:) 실행
  • 성공하면 실제 사용자 삭제 수행

예시 코드 (SignManager 내부)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
deleteCurrentUser(completion: @escaping (Result<Void, Error>) -> Void) {
    guard let user = Auth.auth().currentUser else {
        completion(.failure(...))
        return
    }

    for provider in user.providerData {
        switch provider.providerID {
        case "google.com":
            var credential: AuthCredential?
            if let idToken = UserDefaults.standard.string(forKey: "googleIDToken"),
               let accessToken = UserDefaults.standard.string(forKey: "googleAccessToken") {
                credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
            }

            guard let authCredential = credential else {
                completion(.failure(...))
                return
            }

            user.reauthenticate(with: authCredential) { _, error in
                if let error = error {
                    completion(.failure(error))
                } else {
                    self.performDelete(user: user, completion: completion)
                }
            }

        default:
            break
        }
    }
}

결과

  • Firebase의 재인증 요구 충족
  • 실제 탈퇴 가능
  • 사용자 경험 개선 (더 이상 실패 메시지로 막히지 않음)

4. 공통 삭제 로직 분리 (performDelete)

Google과 Apple의 삭제 로직을 중복 없이 관리하기 위해 다음처럼 공통 처리 분리:

performDelete(user: FirebaseAuth.User, completion: @escaping (Result<Void, Error>) -> Void)

→ 내부에서 providerID에 따라 분기 처리 후 삭제


해결된 문제 요약

문제이전 상태해결 방법
Apple 로그인 시 fullName 충돌 가능성fullName 전달nil로 고정
Google 탈퇴 시 재인증 오류 (requires-recent-login)재인증 생략 → 탈퇴 실패저장된 토큰으로 reauthenticate 후 삭제
민감한 콘솔 로그JWT/토큰 print 노출디버깅용 print 제거
탈퇴 로직 중복provider마다 중복 코드performDelete로 통합 관리

결론

이번 수정은 Firebase 인증을 사용하는 앱에서 발생할 수 있는 실질적인 문제들을 해결한 중요한 리팩토링이었다.
특히 Google 탈퇴 시 발생하는 requires-recent-login 문제는 직접 경험해보지 않으면 원인 파악이 어려운 문제였다.

이를 해결하기 위해:

  • 로그인 시 토큰 저장
  • 탈퇴 시 재인증
  • 공통 삭제 흐름 분리

까지 체계적으로 구성되었으며, 다른 Firebase 기반 앱에서도 유용하게 재사용할 수 있는 구조이다.

이 문제로 당시 지피티를 이용해도 직접적인 해결이 어려워 애를 먹었기 때문에, 이 문서가 같은 문제를 겪는 개발자에게 큰 도움이 될 수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.