본문 바로가기

Project/FirebaseTest

FireBase - 회원 정보 불러오기

fetchUser 메서드

    public func fetchUser(completion: @escaping (User?, Error?) -> Void) {
        guard let userUID = Auth.auth().currentUser?.uid else {
            completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "사용자가 로그인되어 있지 않습니다."]))
            return
        }
        
        let db = Firestore.firestore()
        
        db.collection("users")
            .document(userUID)
            .getDocument { snapshot, error in
            if let error = error {
                completion(nil, error)
                return
            }
            
            guard let snapshotData = snapshot?.data(),
                  let username = snapshotData["username"] as? String,
                  let email = snapshotData["email"] as? String else {
                completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Firestore 데이터가 올바르지 않습니다."]))
                return
            }
            
            let pathRef = Storage.storage().reference(withPath: "profile_images/\(userUID).jpg")
            
            pathRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
                var userImage: UIImage? = nil
                
                if let data = data {
                    userImage = UIImage(data: data)
                } else {
                    print("이미지를 가져오지 못했습니다: \(error?.localizedDescription ?? "알 수 없는 에러")")
                }
                
                let user = User(username: username, email: email, userUID: userUID, userImage: userImage!)
                completion(user, nil)
            }
        }
    }

 

이 메서드는 현재 로그인된 유저의 데이터를 Firestore와 Firebase Storage에서 가져오는 역할을 합니다. Firestore에서 사용자 기본 정보(username, email)를 가져오고, Firebase Storage에서 프로필 이미지를 다운로드하여 User 객체를 생성한 후, completion 핸들러를 통해 반환합니다.

 

현재 로그인된 사용자 UID 가져오기

guard let userUID = Auth.auth().currentUser?.uid else {
    completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "사용자가 로그인되어 있지 않습니다."]))
    return
}

 

  • Auth.auth().currentUser?.uid:
    • Firebase Authentication을 통해 현재 로그인된 사용자의 UID를 가져옵니다.
    • UID는 Firebase의 모든 서비스(Firestore, Storage 등)에서 사용자를 고유하게 식별하는 데 사용됩니다.
  • 에러 처리:
    • 로그인이 되어 있지 않아 currentUser가 nil이면 에러를 반환하고 메서드를 종료합니다.

 

Firestore에서 사용자 문서 가져오기

let db = Firestore.firestore()

db.collection("users").document(userUID).getDocument { snapshot, error in

 

 

  • db.collection("users").document(userUID):
    • Firestore의 users 컬렉션에서 로그인된 사용자의 UID를 문서 ID로 가진 문서를 가져옵니다.
  • getDocument:
    • 해당 문서를 가져오는 비동기 작업입니다. 성공 시 snapshot 객체를 반환합니다.
  • 에러 처리:
    • Firestore에서 데이터를 가져오는 과정에서 에러가 발생하면 에러를 반환합니다.

 

Firestore 데이터 검증

guard let snapshotData = snapshot?.data(),
      let username = snapshotData["username"] as? String,
      let email = snapshotData["email"] as? String else {
    completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Firestore 데이터가 올바르지 않습니다."]))
    return
}

 

 

  • snapshot?.data():
    • Firestore 문서 데이터를 Dictionary 형태로 반환합니다.
  • 데이터 파싱 및 검증:
    • "username"과 "email" 필드를 파싱하여 String 타입으로 변환합니다.
    • 데이터가 없거나 타입 변환에 실패하면 에러를 반환합니다.

 

Firebase Storage에서 프로필 이미지 가져오기

let pathRef = Storage.storage().reference(withPath: "profile_images/\(userUID).jpg")

pathRef.getData(maxSize: 1 * 1024 * 1024) { data, error in

 

 

  • Storage.storage().reference(withPath:):
    • Firebase Storage에서 profile_images/{userUID}.jpg 경로를 참조합니다.
    • userUID는 로그인된 사용자의 고유 ID입니다.
  • getData(maxSize:):
    • Storage에서 이미지를 다운로드합니다.
    • maxSize는 다운로드할 최대 파일 크기(1MB로 제한).
  • 에러 처리:
    • 이미지 다운로드에 실패하면 로그를 출력하고, 이후 기본 처리가 진행됩니다.

 

이미지 데이터 변환 및 User 객체 생성

var userImage: UIImage? = nil

if let data = data {
    userImage = UIImage(data: data)
} else {
    print("이미지를 가져오지 못했습니다: \(error?.localizedDescription ?? "알 수 없는 에러")")
}

let user = User(username: username, email: email, userUID: userUID, userImage: userImage!)
completion(user, nil)

 

 

  • 이미지 변환:
    • 다운로드된 이미지 데이터를 UIImage로 변환.
    • 변환 실패 시 nil로 설정됩니다.
  • User 객체 생성:
    • username, email, userUID, userImage를 사용하여 User 객체를 생성.
    • 변환된 이미지를 userImage로 전달.
  • 에러 처리 누락 문제:
    • userImage가 nil인 경우에도 강제로 언래핑(userImage!)하고 있어, 앱이 크래시될 위험이 있습니다.
    • 이를 해결하려면 기본 이미지를 설정하거나 적절히 에러를 반환해야 합니다.

 

문제점 및 개선 사항

  • userImage 강제 언래핑 문제
    • 이미지 변환에 실패하면 UIImage(data:)가 nil을 반환할 수 있습니다.
    • 현재는 강제 언래핑(userImage!)으로 처리하고 있어 크래시 가능성이 존재합니다.
    • 해결 방안:
      • 기본 이미지를 설정하거나, 실패 시 에러를 반환하도록 수정합니다.
let userImage = UIImage(data: data) ?? UIImage(named: "default_profile")

 

  • Storage 다운로드 실패 시 기본 처리 부족
    • 이미지 다운로드가 실패해도 기본적인 사용자 데이터는 반환할 수 있습니다.
    • 다운로드 실패 시 기본 이미지를 설정하여 반환하도록 수정합니다.
var userImage: UIImage = UIImage(named: "default_profile")!

if let data = data, let image = UIImage(data: data) {
    userImage = image
}

 

 

  • 비동기 작업 중 중복 에러 처리
    • Firestore와 Storage에서 각각 에러 처리를 하고 있으나, 특정 에러를 분리하여 처리하는 추가적인 개선이 필요합니다.

 

개선된 코드

public func fetchUser(completion: @escaping (User?, Error?) -> Void) {
    guard let userUID = Auth.auth().currentUser?.uid else {
        completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "사용자가 로그인되어 있지 않습니다."]))
        return
    }
    
    let db = Firestore.firestore()
    
    db.collection("users").document(userUID).getDocument { snapshot, error in
        if let error = error {
            completion(nil, error)
            return
        }
        
        guard let snapshotData = snapshot?.data(),
              let username = snapshotData["username"] as? String,
              let email = snapshotData["email"] as? String else {
            completion(nil, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Firestore 데이터가 올바르지 않습니다."]))
            return
        }
        
        let pathRef = Storage.storage().reference(withPath: "profile_images/\(userUID).jpg")
        
        pathRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
            let userImage: UIImage = {
                if let data = data, let image = UIImage(data: data) {
                    return image
                } else {
                    print("이미지를 가져오지 못했습니다: \(error?.localizedDescription ?? "알 수 없는 에러")")
                    return UIImage(named: "default_profile")!
                }
            }()
            
            let user = User(username: username, email: email, userUID: userUID, userImage: userImage)
            completion(user, nil)
        }
    }
}