본문 바로가기

Project/FirebaseTest

FireBase - 회원가입할 때 프로필 이미지 포함

saveUserToFirestore 메서드

private func saveUserToFirestore(userUID: String, username: String, email: String, profileImageURL: String, completion: @escaping (Bool, Error?) -> Void) {
        let db = Firestore.firestore()
        
        let userData: [String: Any] = [
            "username": username,
            "email": email,
            "profileImageURL": profileImageURL,
            "createdAt": Timestamp()
        ]
        
        db.collection("users").document(userUID).setData(userData) { error in
            if let error = error {
                print("Firestore 저장 실패: \(error.localizedDescription)")
                completion(false, error)
            } else {
                print("Firestore 저장 성공")
                completion(true, nil)
            }
        }
    }

 

이 메서드는 Firestore의 users 컬렉션에 사용자 정보를 저장하는 역할을 합니다. 이 정보는 사용자 UID를 문서 ID로 사용하여 Firestore 데이터베이스에 저장됩니다.

 

Firestore 인스턴스 가져오기:

let db = Firestore.firestore()

 

  • Firestore 데이터베이스에 접근하기 위해 Firestore.firestore()를 호출하여 데이터베이스 인스턴스를 가져옵니다.

 

저장할 데이터 생성:

let userData: [String: Any] = [
    "username": username,
    "email": email,
    "profileImageURL": profileImageURL,
    "createdAt": Timestamp()
]
  • 저장할 데이터를 딕셔너리 형태로 정의합니다:
    • "username": 사용자 이름.
    • "email": 사용자 이메일.
    • "profileImageURL": 프로필 이미지의 Firebase Storage 다운로드 URL.
    • "createdAt": Firestore의 Timestamp를 사용하여 데이터 생성 시간을 저장.

 

Firestore에 데이터 저장:

db.collection("users").document(userUID).setData(userData) { error in

 

  • Firestore의 users 컬렉션에 접근합니다.
  • userUID를 문서 ID로 설정하여 데이터를 저장합니다.
  • setData를 호출하여 userData를 Firestore에 저장합니다.

 

에러 처리 및 성공 여부 반환:

  • 저장 과정에서 발생한 에러를 처리합니다:
if let error = error {
    print("Firestore 저장 실패: \(error.localizedDescription)")
    completion(false, error)
} else {
    print("Firestore 저장 성공")
    completion(true, nil)
}

 

 

  • 저장 실패:
    • 에러 메시지를 출력하고, completion(false, error)를 호출하여 실패를 알립니다.
  • 저장 성공:
    • 성공 메시지를 출력하고, completion(true, nil)를 호출하여 성공을 알립니다.

 

사용 예제

saveUserToFirestore(userUID: "12345", username: "JohnDoe", email: "johndoe@example.com", profileImageURL: "https://firebase.storage.url") { success, error in
    if success {
        print("사용자 정보가 Firestore에 저장되었습니다.")
    } else if let error = error {
        print("Firestore 저장 실패: \(error.localizedDescription)")
    }
}

 

Firestore 구조 예시

저장 후 Firestore 데이터는 다음과 같은 구조를 갖습니다:

users (컬렉션)
  └── 12345 (문서, userUID)
       ├── username: "JohnDoe"
       ├── email: "johndoe@example.com"
       ├── profileImageURL: "https://firebase.storage.url"
       └── createdAt: {타임스탬프}

 

 

 

 

uploadProfileAndSaveUserToFirestore 메서드

private func uploadProfileAndSaveUserToFirestore(userUID: String, request: RegisterUserRequest, completion: @escaping (Bool, Error?) -> Void) {
        // 1. 이미지를 Firebase Storage에 업로드
        guard let imageData = request.userImage.jpegData(compressionQuality: 0.5) else {
            completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 데이터를 변환할 수 없습니다."]))
            return
        }
        
        let storageRef =
        Storage.storage().reference().child("profile_images/\(userUID).jpg")
        
        storageRef.putData(imageData, metadata: nil) { metadata, error in
            if let error = error {
                print("Firebase Storage 업로드 실패: \(error.localizedDescription)")
                completion(false, error)
                return
            }
            
            // 2. 다운로드 URL 가져오기
            storageRef.downloadURL { url, error in
                if let error = error {
                    print("Firebase Storage 다운로드 URL 가져오기 실패: \(error.localizedDescription)")
                    completion(false, error)
                    return
                }
                
                guard let profileImageURL = url?.absoluteString else {
                    completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 URL을 가져올 수 없습니다."]))
                    return
                }
                
                // 3. Firestore에 사용자 정보 저장
                self.saveUserToFirestore(userUID: userUID, username: request.username, email: request.email, profileImageURL: profileImageURL) { success, error in
                    completion(success, error)
                }
            }
        }
    }

 

이 메서드는 Firebase Storage에 이미지를 업로드한 뒤, 업로드된 이미지의 다운로드 URL을 가져와 Firestore에 사용자 데이터를 저장하는 역할을 합니다.

 

이미지 데이터 변환 및 업로드 준비

guard let imageData = request.userImage.jpegData(compressionQuality: 0.5) else {
    completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 데이터를 변환할 수 없습니다."]))
    return
}

 

  • request.userImage:
    • UIImage 타입으로 전달된 프로필 이미지를 가져옵니다.
  • jpegData(compressionQuality:):
    • 이미지를 JPEG 포맷으로 변환하며, 압축 품질은 0.5로 설정(50% 품질). 이는 이미지 크기를 줄여 업로드 속도를 높이고 저장 공간을 절약하기 위함입니다.

 

Firebase Storage 경로 설정

let storageRef = Storage.storage().reference().child("profile_images/\(userUID).jpg")

 

 

  • Storage.storage():
    • Firebase Storage 인스턴스를 가져옵니다.
  • .reference():
    • Storage의 루트 경로를 참조합니다.
  • .child("profile_images/\(userUID).jpg"):
    • 업로드 경로를 설정:
      • profile_images: Firebase Storage 내 폴더 이름.
      • \(userUID).jpg: 파일 이름으로 사용자 고유 UID를 사용하여 충돌 방지.
    • 전체 경로는 다음과 같습니다:
profile_images/{userUID}.jpg

 

 

 

Firebase Storage에 이미지 업로드

storageRef.putData(imageData, metadata: nil) { metadata, error in
    if let error = error {
        print("Firebase Storage 업로드 실패: \(error.localizedDescription)")
        completion(false, error)
        return
    }
}

 

  • putData(_:metadata:):
    • Firebase Storage에 이미지 데이터를 업로드.
    • metadata는 생략되어 기본 메타데이터로 저장됩니다.
  • 에러 처리:
    • 업로드가 실패하면 에러 메시지를 출력하고 completion(false, error)를 호출하여 실패를 알립니다.

 

다운로드 URL 가져오기

storageRef.downloadURL { url, error in
    if let error = error {
        print("Firebase Storage 다운로드 URL 가져오기 실패: \(error.localizedDescription)")
        completion(false, error)
        return
    }
    
    guard let profileImageURL = url?.absoluteString else {
        completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 URL을 가져올 수 없습니다."]))
        return
    }
}

 

 

  • downloadURL:
    • 업로드된 이미지의 공개 URL을 반환.
  • URL 검증:
    • URL이 없거나 변환에 실패하면 에러를 반환.

 

Firestore에 사용자 정보 저장

self.saveUserToFirestore(userUID: userUID, username: request.username, email: request.email, profileImageURL: profileImageURL) { success, error in
    completion(success, error)
}

 

  • saveUserToFirestore:
    • userUID를 문서 ID로 사용하여 Firestore의 users 컬렉션에 사용자 데이터를 저장.
    • 전달되는 데이터:
      • username: 사용자 이름.
      • email: 사용자 이메일.
      • profileImageURL: Firebase Storage에서 가져온 이미지 다운로드 URL.

 

로직 순서

  1. 이미지 데이터를 준비:
    • UIImage를 JPEG 포맷으로 변환.
    • 압축 품질을 설정하여 이미지 크기를 줄임.
  2. Firebase Storage 경로 설정:
    • profile_images/{userUID}.jpg 경로에 이미지를 저장할 준비.
  3. Firebase Storage에 이미지 업로드:
    • putData를 사용하여 이미지 데이터를 업로드.
  4. 다운로드 URL 가져오기:
    • 업로드가 완료된 후, 다운로드 가능한 URL을 가져옴.
  5. Firestore에 사용자 정보 저장:
    • 다운로드 URL과 함께 사용자 정보를 users 컬렉션에 저장.

 

Firestore와 연동 후 저장 결과

Firestore 구조:

users (컬렉션)
  └── {userUID} (문서)
       ├── username: "JohnDoe"
       ├── email: "johndoe@example.com"
       ├── profileImageURL: "https://firebasestorage.googleapis.com/v0/b/{bucket}/o/profile_images%2F{userUID}.jpg"
       └── createdAt: {Timestamp}

 

Firebase Storage 구조:

profile_images (폴더)
  └── {userUID}.jpg (파일)

 

 

 

registerUser 메서드

func registerUser(request: RegisterUserRequest, completion: @escaping (Bool, Error?) -> Void) {
        // 1. Firebase Auth로 사용자 생성
        Auth.auth().createUser(withEmail: request.email, password: request.password) { authResult, error in
            if let error = error {
                print("Firebase Auth 사용자 생성 실패: \(error.localizedDescription)")
                completion(false, error)
                return
            }
            
            guard let userUID = authResult?.user.uid else {
                let uidError = NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "사용자 UID를 가져올 수 없습니다."])
                completion(false, uidError)
                return
            }
            
            // 2. 이미지 업로드와 Firestore 데이터 저장 통합
            self.uploadProfileAndSaveUserToFirestore(userUID: userUID, request: request) { success, error in
                if success {
                    print("회원가입 성공")
                    completion(true, nil)
                } else {
                    print("회원가입 실패: \(error?.localizedDescription ?? "알 수 없는 에러")")
                    completion(false, error)
                }
            }
        }
    }

 

이 메서드는 Firebase Auth를 통해 사용자 계정을 생성하고, 계정 생성 후 프로필 이미지 업로드 및 Firestore에 사용자 데이터 저장을 처리하는 역할을 합니다.

 

Firebase Auth로 사용자 생성

Auth.auth().createUser(withEmail: request.email, password: request.password) { authResult, error in
    if let error = error {
        print("Firebase Auth 사용자 생성 실패: \(error.localizedDescription)")
        completion(false, error)
        return
    }

 

  • Auth.auth().createUser:
    • Firebase Authentication을 통해 사용자를 생성합니다.
    • 이메일(request.email)과 비밀번호(request.password)를 사용하여 계정을 만듭니다.
    • 계정 생성 성공 시 authResult를 반환, 실패 시 error를 반환.
  • 에러 처리:
    • 사용자 생성 실패 시 에러 메시지를 출력하고, completion(false, error)를 호출하여 호출자에게 실패를 알립니다.

 

UID 확인

guard let userUID = authResult?.user.uid else {
    let uidError = NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "사용자 UID를 가져올 수 없습니다."])
    completion(false, uidError)
    return
}

 

 

  • authResult?.user.uid:
    • Firebase가 생성한 고유 사용자 ID(UID)를 가져옵니다.
    • UID는 Firebase Storage 및 Firestore에 데이터를 저장할 때 경로로 사용됩니다.
  • 에러 처리:
    • UID를 가져올 수 없는 경우, 커스텀 에러(uidError)를 생성하여 반환합니다.

 

이미지 업로드 및 Firestore 데이터 저장

self.uploadProfileAndSaveUserToFirestore(userUID: userUID, request: request) { success, error in
    if success {
        print("회원가입 성공")
        completion(true, nil)
    } else {
        print("회원가입 실패: \(error?.localizedDescription ?? "알 수 없는 에러")")
        completion(false, error)
    }
}

 

 

  • uploadProfileAndSaveUserToFirestore:
    • 프로필 이미지를 Firebase Storage에 업로드하고, Firestore에 사용자 정보를 저장합니다.
    • UID, 이메일, 비밀번호, 프로필 이미지를 전달합니다.
  • 에러 처리:
    • 이미지 업로드 및 데이터 저장 성공 여부를 success 플래그와 error 객체로 판단.
    • 성공 시 completion(true, nil) 호출, 실패 시 에러 메시지를 출력하고 completion(false, error) 호출.

 

로직 순서

  1. Firebase Auth로 사용자 계정 생성:
    • 이메일과 비밀번호로 계정을 생성.
    • 계정 생성 성공 시 사용자 UID를 가져옴.
  2. UID 확인:
    • UID는 Firebase Storage 및 Firestore에서 사용자 데이터를 고유하게 식별하기 위한 값입니다.
    • UID를 가져오지 못하면 실패로 처리.
  3. 이미지 업로드 및 데이터 저장:
    • UID를 기반으로 Firebase Storage에 이미지를 업로드.
    • 다운로드 URL을 Firestore에 저장하여 프로필 이미지와 사용자 정보를 통합.

 

이 메서드의 역할

이 메서드는 회원가입 과정을 하나의 흐름으로 묶어 처리합니다:

  1. Firebase Auth를 통해 사용자 계정을 생성.
  2. 계정 생성 후 UID 기반으로 프로필 이미지를 저장.
  3. Firestore에 사용자의 정보를 저장하며 회원가입을 완료.

이를 통해 회원가입 과정에서 필요한 인증, 데이터 저장, 이미지 업로드를 통합적으로 관리합니다.

 

 

🔥 user.uid 란? 🔥

user.uid는 Firebase Authentication에서 사용자 계정을 생성하거나 로그인했을 때 Firebase가 자동으로 생성해 제공하는 고유 사용자 ID입니다.

이 UID는 AuthDataResult 객체의 user 속성을 통해 접근할 수 있으며, Firebase의 다양한 서비스에서 사용자를 식별하는 핵심 역할을 합니다.

 

예시: AuthDataResult의 구조

Auth.auth().createUser 호출 시 반환되는 데이터 구조는 다음과 같습니다:

Auth.auth().createUser(withEmail: "test@example.com", password: "password123") { authResult, error in
    if let authResult = authResult {
        let user = authResult.user
        print("사용자 UID: \(user.uid)") // Firebase가 생성한 고유 사용자 ID
        print("사용자 이메일: \(user.email ?? "없음")")
    } else if let error = error {
        print("사용자 생성 실패: \(error.localizedDescription)")
    }
}

 

 

  • authResult.user.uid:
    • Firebase가 생성한 고유한 사용자 ID.
  • authResult.user.email:
    • 사용자 계정에 등록된 이메일.

 

UID의 용도

  • 사용자 고유 식별:
    • Firebase Authentication에서 사용자 계정을 고유하게 식별하기 위해 UID를 사용합니다.
    • UID는 중복되지 않으며, Firebase의 각 서비스(예: Firestore, Storage)에서 사용자별 데이터를 관리하는 데 사용됩니다.
  • Firebase Storage 경로:
    • UID를 기준으로 프로필 이미지 경로나 사용자 파일을 저장합니다.
let storageRef = Storage.storage().reference().child("profile_images/\(userUID).jpg")

 

 

  • 3. Firestore 문서 ID:
    • UID를 Firestore의 문서 ID로 사용하여 사용자 데이터를 저장합니다.
db.collection("users").document(userUID).setData(userData)