본문 바로가기

Project/FirebaseTest

FireBase - 로그인과 로그아웃 처리하는 메서드 정의

AutheService

import Foundation
import FirebaseAuth
import FirebaseFirestore

class AutheService {
	
    ...
    
    public func signIn(with userRequest: LoginUserRequest,
                       completion: @escaping (Error?) -> Void) {
        
        let email = userRequest.email
        let password = userRequest.password
        
        Auth.auth().signIn(withEmail: email, password: password) { result, error in
            if let error = error {
                completion(error)
                return
            } else {
                completion(nil)
            }
        }
    }
    
    public func signOut(completion: @escaping (Error?) -> Void) {
        do {
            try Auth.auth().signOut()
            completion(nil)
        } catch let error {
            completion(error)
        }
    }
}

 

위 코드는 Firebase Authentication을 활용해 사용자 로그인로그아웃을 처리하는 메서드를 정의한 부분입니다.

 

1. 메서드 설명

1.1 signIn 메서드

public func signIn(with userRequest: LoginUserRequest,
                   completion: @escaping (Error?) -> Void)

 

  • 목적: 사용자의 이메일과 비밀번호를 기반으로 Firebase Authentication을 사용해 로그인을 수행합니다.
  • 매개변수:
    • userRequest: 사용자의 로그인 정보(이메일, 비밀번호)가 담긴 객체.
    • completion: 비동기 작업 완료 후 결과를 전달하기 위한 클로저.
      • 클로저의 매개변수:
        • Error?: 로그인 성공 시 nil, 실패 시 발생한 오류 객체.
  • 작동 흐름:
    1. Auth.auth().signIn(withEmail: password) 호출.
    2. Firebase Authentication 서버와 통신 후, 성공 여부와 오류 정보를 반환.
    3. 결과를 completion 클로저로 전달:
      • 성공: completion(nil).
      • 실패: completion(error).

 

1.2 signOut 메서드

public func signOut(completion: @escaping (Error?) -> Void)

 

 

  • : 현재 로그인한 사용자를 로그아웃합니다.
  • 매개변수:
    • completion: 비동기 작업 완료 후 결과를 전달하기 위한 클로저.
      • 클로저의 매개변수:
        • Error?: 로그아웃 성공 시 nil, 실패 시 발생한 오류 객체.
  • 작동 흐름:
    1. Auth.auth().signOut() 호출.
    2. Firebase Authentication의 세션을 종료.
    3. 결과를 completion 클로저로 전달:
      • 성공: completion(nil).
      • 실패: completion(error) (예: 로그아웃 중 문제가 발생한 경우).

 

2. 접근 제어자 public의 역할

2.1 public의 의미

  • public 접근 제어자는 모듈 외부에서도 해당 메서드에 접근 가능하도록 허용합니다.
    • 모듈: 앱을 구성하는 코드의 단위. 기본적으로 동일한 프로젝트 내부 코드가 하나의 모듈을 이룹니다.
    • 외부 모듈: 프로젝트에서 가져온 외부 프레임워크 또는 라이브러리.

2.2 왜 public을 사용했는가?

  • signInsignOut 메서드는 앱 전역에서 사용해야 하는 기능입니다.
  • 예: 사용자 인증은 로그인 화면, 설정 화면 등 다양한 곳에서 호출될 수 있습니다.
  • public 키워드를 사용하면, 앱의 다른 모듈 또는 다른 뷰 컨트롤러에서도 이 메서드를 호출할 수 있습니다.

2.3 public을 사용하지 않으면?

  • 기본 접근 제어자인 internal이 적용됩니다.
    • internal: 동일 모듈 내에서만 접근 가능.
    • 즉, 동일 프로젝트 내에서는 사용할 수 있지만, 다른 모듈에서는 사용할 수 없습니다.

 

3. 꼭 public을 써야 할까?

언제 public을 사용해야 하는가?

  • 이 클래스가 외부 프레임워크 또는 라이브러리로 제공되는 경우.
    • 예: AuthService를 별도의 모듈로 만들고, 다른 프로젝트에서 사용할 경우.

언제 internal로 충분한가?

  • 클래스와 메서드가 프로젝트 내에서만 사용되는 경우.
    • 만약 AuthService가 이 앱 프로젝트 내에서만 사용된다면 internal로 충분합니다. (public을 제거하면 기본적으로 internal이 됩니다.)

 

로그인 사용

let userRequest = LoginUserRequest(email: "johndoe@example.com", password: "password123")

AuthService.shared.signIn(with: userRequest) { error in
    if let error = error {
        print("Sign-in failed: \(error.localizedDescription)")
    } else {
        print("Sign-in successful!")
    }
}

 

로그아웃 사용

AuthService.shared.signOut { error in
    if let error = error {
        print("Sign-out failed: \(error.localizedDescription)")
    } else {
        print("Sign-out successful!")
    }
}

 

 

결론

  1. public 사용 이유:
    • signIn과 signOut 메서드를 앱 전역에서 호출할 수 있도록 하기 위함.
    • 외부 모듈이나 프레임워크로 제공할 경우 필수.
  2. public 없이도 되는 경우:
    • 클래스와 메서드가 동일 프로젝트 내에서만 사용될 때는 기본 접근 제어자인 internal로 충분.
  3. 클로저의 역할:
    • Firebase와 같은 비동기 작업의 결과를 호출자에게 전달하기 위한 방식. 결과로 Error?를 전달하며, 성공 여부를 알려줌.

 

 

SceneDelegate

import UIKit
import FirebaseAuth

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        self.setupWindow(with: scene)
        self.checkAuthentication()
    }

    private func setupWindow(with scene: UIScene) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        self.window = window
        self.window?.makeKeyAndVisible()
    }
    
    
    public func checkAuthentication() {
        if Auth.auth().currentUser == nil {
            // Go to sign in screen
            self.goToController(with: LoginController())
        } else {
            // Go to home Screen
            self.goToController(with: HomeController())
        }
    }
    
    private func goToController(with viewController: UIViewController) {
        DispatchQueue.main.async { [weak self] in
            UIView.animate(withDuration: 0.25) {
                self?.window?.layer.opacity = 0
            } completion: { [weak self] _ in
                
                let nav = UINavigationController(rootViewController: viewController)
                nav.modalPresentationStyle = .fullScreen
                self?.window?.rootViewController = nav
                
                UIView.animate(withDuration: 0.25) { [weak self] in
                    self?.window?.layer.opacity = 1
                }
            }
        }
    }
}

 

 위 코드는 SceneDelegate 클래스의 구현으로, iOS 앱에서 여러 씬(Scene)을 관리하기 위해 사용됩니다. 특히, 이 코드는 Firebase Authentication을 사용하여 사용자가 로그인 상태인지 확인하고, 로그인 여부에 따라 적절한 화면(로그인 화면 또는 홈 화면)을 표시하는 기능을 제공합니다.

 

 

1. 주요 구성 요소

1.1 scene(_:willConnectTo:options:)

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    self.setupWindow(with: scene)
    self.checkAuthentication()
}

 

  • 역할:
    • 앱의 특정 씬이 연결될 때 호출됩니다.
    • 앱의 기본적인 초기화를 수행합니다.
    • 이 메서드에서 setupWindow와 checkAuthentication 메서드를 호출합니다.
  • 동작 흐름:
    1. setupWindow(with:):
      • UIWindow를 설정하고 화면에 표시.
    2. checkAuthentication():
      • Firebase Authentication을 사용하여 로그인 여부를 확인하고, 로그인 상태에 따라 적절한 화면으로 전환.

 

1.2 setupWindow(with:)

private func setupWindow(with scene: UIScene) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    let window = UIWindow(windowScene: windowScene)
    self.window = window
    self.window?.makeKeyAndVisible()
}

 

 

  • 역할:
    • 앱의 UIWindow를 생성하고 활성화.
    • UIWindow는 앱의 모든 UI 요소를 표시하는 컨테이너 역할을 합니다.
    • 이 메서드는 UIScene 객체를 받아 UIWindowScene을 생성하고 UIWindow에 연결합니다.

 

1.3 checkAuthentication()

public func checkAuthentication() {
    if Auth.auth().currentUser == nil {
        // Go to sign in screen
        self.goToController(with: LoginController())
    } else {
        // Go to home Screen
        self.goToController(with: HomeController())
    }
}
  • 역할:
    • Firebase Authentication을 사용하여 로그인 상태를 확인.
    • Auth.auth().currentUser를 사용해 현재 로그인된 사용자가 있는지 검사:
      • nil인 경우:
        • 사용자가 로그인하지 않았으므로 로그인 화면(LoginController)으로 이동.
      • 로그인 상태인 경우:
        • 홈 화면(HomeController)으로 이동.

 

1.4 goToController(with:)

private func goToController(with viewController: UIViewController) {
    DispatchQueue.main.async { [weak self] in
        UIView.animate(withDuration: 0.25) {
            self?.window?.layer.opacity = 0
        } completion: { [weak self] _ in
            let nav = UINavigationController(rootViewController: viewController)
            nav.modalPresentationStyle = .fullScreen
            self?.window?.rootViewController = nav
            
            UIView.animate(withDuration: 0.25) { [weak self] in
                self?.window?.layer.opacity = 1
            }
        }
    }
}

 

  • 역할:
    • 특정 화면으로 전환하며, 전환 과정에서 페이드 애니메이션을 적용.
    • 새 UIViewController를 UINavigationController에 래핑하여 표시.
  • 동작 흐름:
    1. 첫 번째 애니메이션:
      • self?.window?.layer.opacity = 0로 설정하여 현재 화면을 점점 투명하게 만듦.
    2. 화면 전환:
      • self?.window?.rootViewController를 새로운 UIViewController로 설정.
    3. 두 번째 애니메이션:
      • self?.window?.layer.opacity = 1로 설정하여 새로운 화면을 점점 불투명하게 표시.
  • DispatchQueue.main.async 사용:
    • UI 작업은 메인 스레드에서 실행되어야 하므로 안전하게 UI 업데이트를 보장.
  • weak self 사용:
    • 메모리 누수를 방지하기 위해 [weak self]를 사용하여 클로저 내부에서 self를 약하게 참조.

 

2. 코드의 동작 흐름

  1. 앱 실행 시 scene(_:willConnectTo:options:) 호출:
    • setupWindow(with:)를 통해 UIWindow를 초기화.
    • checkAuthentication()를 호출하여 로그인 상태 확인.
  2. 로그인 여부에 따라 화면 전환:
    • Firebase Authentication에서 현재 사용자를 확인:
      • 로그인하지 않은 상태: 로그인 화면(LoginController) 표시.
      • 로그인 상태: 홈 화면(HomeController) 표시.
  3. 화면 전환 애니메이션:
    • goToController(with:)를 사용해 부드러운 페이드 애니메이션을 통해 화면 전환.

3. 코드에 사용된 주요 기술

3.1 Firebase Authentication

  • Auth.auth().currentUser:
    • 현재 로그인된 사용자의 정보를 가져옴.
    • 로그인되지 않은 경우 nil을 반환.

3.2 UIWindow 및 UIWindowScene

  • UIWindow:
    • 앱의 UI 요소를 포함하는 컨테이너.
    • 앱에서 화면을 표시하려면 UIWindow를 초기화하고 활성화해야 함.
  • UIWindowScene:
    • iOS 13 이후, 멀티윈도우를 지원하기 위해 추가된 씬 관리 객체.

3.3 UIView 애니메이션

  • UIView.animate:
    • 간단한 애니메이션을 적용하는 메서드.
    • 여기서는 페이드 효과(투명도 변화)를 사용해 부드러운 화면 전환 구현.

 

 

🔥  로그인 상태 캐싱:

  • 로그인 상태를 로컬 스토리지에 캐싱하여 매번 Firebase Authentication 호출을 줄일 수 있음.