[TIL] 10.26

Junyoung_Hong·2023년 10월 26일
0

TIL_10월

목록 보기
17/20
post-thumbnail

1. Firebase Storage에 이미지 저장하기

Firebase를 이용하면서 이미지를 저장해야하는 경우에는 Storage에 저장을 시킨다. Storage에 저장을 시키는 이유는 다음과 같다고 한다.

  1. 스케일링: Firebase Storage는 Google Cloud Storage를 기반으로 하고 있으며, 이는 거의 무한한 스케일링이 가능하다. 이는 사용자의 앱이 성장함에 따라 많은 양의 이미지나 다른 파일을 저장하는 데 문제가 없음을 의미한다.

  2. 성능: 이미지를 Firebase Storage에 저장하면, 이미지가 전 세계에 걸쳐 분산된 서버에 저장된다. 사용자가 이미지를 요청할 때, 그들에게 가장 가까운 서버에서 이미지가 제공되어 더 빠른 로딩 시간과 더 나은 사용자 경험을 제공할 수 있다.

  3. 보안: Firebase Storage는 파일에 대한 세밀한 액세스 제어를 허용한다. 개발자는 보안 규칙을 설정하여 특정 사용자가 특정 파일에 액세스하거나 수정할 수 있는지 여부를 정확하게 제어할 수 있다.

  4. 관리 용이성: Firebase Console을 통해 업로드된 이미지와 파일을 쉽게 관리하고 검색할 수 있다. 이는 개발자가 파일을 프로그래매틱하게 관리할 필요 없이 직접 관리할 수 있음을 의미한다.

  5. 무결성 및 신뢰성: Google Cloud Storage의 기반이 되므로, Firebase Storage는 높은 수준의 무결성과 신뢰성을 제공한다. 데이터는 중복 저장되며, 여러 가지 재난 복구 시나리오에서도 안전하게 보호된다.

  6. 백업 및 복구: 데이터는 자동으로 백업되며, 실수로 삭제되거나 손상된 경우에도 복구할 수 있는 옵션이 있다.

  7. CDN 통합: Firebase Storage는 Content Delivery Network (CDN)과 통합될 수 있어, 전 세계 어디에서나 콘텐츠를 빠르게 제공할 수 있다.

1-1. 변수 만들기

흐름은 로컬에서 추가 → 함수의 매개변수에 추가 → Storage에 저장 이다. 로컬에서 추가를 하기 위한 딕셔너리를 만들어주자. RealTime Database와 Storage가 딕셔너리 형태로 저장이 되기 때문에 딕셔너리 형태로 만들어주었다.

var selectedImage: [String: UIImage] = [:]

1-2. 함수 만들기

이제 이미지를 업로드 하는 함수를 만들자. Storage에 지정된 경로에 이미지를 업로드 해야하기 때문에 Storage의 참조를 설정해주어야 한다.

let storageRef = Storage.storage().reference().child("noticeBoards").child(clubID).child(noticeBoardID).child("images")

저장하려는 이미지를 JPEG 포맷으로 변환하고, putData 메서드를 사용하여 업로드 한다. PNG가 아닌 JPEG로 포맷을 결정한 이유는, 크기가 더 작기 때문이다.

let uploadData = image.jpegData(compressionQuality: 0.5)

이미지를 업로드하는 동안 다른 작업이 실행되지 않도록 DispatchGroup 을 사용하여 동기화 처리를 해준다.

let dispatchGroup = DispatchGroup()

모든 이미지 작업이 완료되면 dispatchGroup.notify 블록이 호출되고, 결과가 반환된다. 모든 이미지가 성공적으로 업로드되면 true 와 함께 업로드된 이미지의 경로 목록이 반환된다.

이미지의 경로는 ref.fullPath 를 사용하여 이미지의 전체 경로를 가져오고, 이를 imageURLs 배열에 저장한다.

var imageURLs: [String] = []
.
.
.
let fullPath = ref.fullPath
imageURLs.append(fullPath)

1-3. 전체 코드

func uploadImages(clubID: String, noticeBoardID: String, imageList: [String: UIImage], completion: @escaping (Bool, [String]?) -> Void) {
    let storageRef = Storage.storage().reference().child("noticeBoards").child(clubID).child(noticeBoardID).child("images")
    var imageURLs: [String] = []
    
    let dispatchGroup = DispatchGroup()
    
    for (index, image) in imageList {
        dispatchGroup.enter()
        
        if let uploadData = image.jpegData(compressionQuality: 0.5) {
            ref.putData(uploadData, metadata: nil) { _, error in
                if let error = error {
                    print("Failed to upload image:", error)
                } else {
                    // fullPath 속성을 사용하여 참조 경로를 저장
                    let fullPath = ref.fullPath
                    imageURLs.append(fullPath)
                }
                dispatchGroup.leave()
            }
        } else {
            dispatchGroup.leave()
        }
    }
    
    dispatchGroup.notify(queue: .main) {
        completion(imageURLs.count == imageList.count, imageURLs.sorted())
    }
}

1-4. 사용하기

게시글을 처음 만들 때, 이미지도 같이 저장을 하기위해 createNoticeBoard() 에 넣었다.

func createNoticeBoard(title: String, content: String, clubID: String, completion: @escaping (Bool) -> Void) {
    let ref = Database.database().reference().child("noticeBoards").child(clubID)
    let newNoticeBoardID = ref.childByAutoId().key ?? ""
    
    guard let currentUserID = MyProfile.shared.myUserInfo?.id else { return }
    guard let currentUserNickName = MyProfile.shared.myUserInfo?.nickName else { return }
    guard let currentUserProfileURL = MyProfile.shared.myUserInfo?.profileImageURL else { return }
    
    let currentUser = UserSummary(id: currentUserID, profileImageURL: currentUserProfileURL, nickName: currentUserNickName)
    
    self.uploadImages(clubID: clubID, noticeBoardID: newNoticeBoardID, imageList: self.selectedImage) { success, imageURLs in
        if success {
            let createDate = Date()
            let newNoticeBoard = NoticeBoard(id: newNoticeBoardID, rootUser: currentUser, createDate: createDate, clubID: clubID, title: title, content: content, imageList: imageURLs ?? [], commentCount: "0")
            
            self.saveNoticeBoard(noticeBoard: newNoticeBoard) { success in
                if success {
                    self.noticeBoards.insert(newNoticeBoard, at: 0)
                    self.delegate?.reloadData()
                }
                completion(success)
            }
        } else {
            completion(false)
        }
    }
}

갤러리에서 사진을 선택한 후, 앞에서 만들었던 딕셔너리에 추가를 해준다.

let index = firebaseManager.selectedImage.count
firebaseManager.selectedImage[String(index)] = image

그 다음, 게시글 작성 완료 버튼에서 앞에서 만들었던 createNoticeBoard() 를 불러주자.

firebaseManager.createNoticeBoard(title: newTitleText, content: newContentText, clubID: club.id) { success in
    if success {
        print("게시판 생성 성공")
    }
    else {
    	print("게시판 생성 실패")
    }
}
profile
iOS 개발자를 향해 성장 중

0개의 댓글