본문 바로가기

UIKIT/Firebase

🔥 Firebase의 Storage 내에 저장된 여러 이미지 삭제하기!

https://firebase.google.com/docs/storage/ios/delete-files?hl=ko

 

Apple 플랫폼에서 Cloud Storage로 파일 삭제  |  Cloud Storage for Firebase

4월 9~11일, Cloud Next에서 Firebase가 돌아옵니다. 지금 등록하기 의견 보내기 Apple 플랫폼에서 Cloud Storage로 파일 삭제 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하

firebase.google.com

https://firebase.google.com/docs/storage/ios/list-files?hl=ko

 

Apple 플랫폼에서 Cloud Storage로 파일 나열  |  Cloud Storage for Firebase

4월 9~11일, Cloud Next에서 Firebase가 돌아옵니다. 지금 등록하기 의견 보내기 Apple 플랫폼에서 Cloud Storage로 파일 나열 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하

firebase.google.com

 

1. Firebase Storage에서 여러 파일을 가져와 삭제하는 로직 설명

🔹 기본 개념

  • Firebase Storage는 클라우드에 저장된 파일을 관리하는 서비스입니다.
  • Storage에서 특정 폴더(reference()) 아래에 있는 모든 파일을 가져온 후, 각각의 파일을 개별적으로 삭제하는 것이 목표입니다.

 

✅ 2. deleteReviewPhotos(userID:reviewID:) 메서드 분석

func deleteReviewPhotos(userID: String, reviewID: String) -> AnyPublisher<Void, Error> {
    let storageRef = storage.reference()  // 🔹 Firebase Storage의 root reference 가져오기
    let folderPath = "users/\(userID)/reviews/\(reviewID)/"
    let reviewFolderRef = storageRef.child(folderPath) // 🔹 특정 리뷰 폴더 경로 설정

    return Future<Void, Error> { promise in
        // 1️⃣ Firebase Storage에서 해당 폴더의 모든 파일 목록을 가져옴
        reviewFolderRef.listAll { result in
            switch result {
            case .success(let storageListResult):
                let items = storageListResult.items // 🔹 가져온 파일 목록
                
                // 2️⃣ 삭제할 파일이 없으면 바로 종료
                if items.isEmpty {
                    print("✅ 삭제할 이미지 없음 (리뷰만 삭제)")
                    promise(.success(())) 
                    return
                }

                let dispatchGroup = DispatchGroup() // 🔹 여러 개의 비동기 삭제 요청을 관리할 DispatchGroup 생성
                var deletionErrors: [Error] = [] // 🔹 삭제 실패한 파일의 에러를 저장할 배열

                // 3️⃣ 가져온 파일 목록을 순회하면서 개별 파일 삭제 요청 실행
                for item in items {
                    dispatchGroup.enter() // 🔹 비동기 작업 시작
                    item.delete { error in
                        if let error = error {
                            print("❌ 이미지 삭제 실패: \(error.localizedDescription)")
                            deletionErrors.append(error) // 🔹 실패한 경우 에러 저장
                        } else {
                            print("✅ 이미지 삭제 성공: \(item.fullPath)")
                        }
                        dispatchGroup.leave() // 🔹 비동기 작업 완료
                    }
                }

                // 4️⃣ 모든 삭제 작업이 끝나면 결과 처리
                dispatchGroup.notify(queue: .main) {
                    if deletionErrors.isEmpty {
                        print("✅ 모든 이미지 삭제 완료")
                        promise(.success(()))
                    } else {
                        print("❌ 일부 이미지 삭제 실패")
                        promise(.failure(NSError(domain: "FirebaseStorageError", code: -2, userInfo: [NSLocalizedDescriptionKey: "일부 이미지 삭제 실패"]))))
                    }
                }

            case .failure(let error):
                // 🔹 Storage에서 파일 목록을 가져오는 과정에서 실패한 경우
                print("❌ 이미지 목록 불러오기 실패: \(error.localizedDescription)")
                promise(.failure(error))
            }
        }
    }
    .eraseToAnyPublisher() // 🔹 Combine의 AnyPublisher 형태로 변환하여 반환
}

 

 

📌 참고한 Firebase 공식 문서의 내용 적용 방법

🔹 (1) Firebase에서 파일 목록 가져오기 (listAll())

이 코드를 deleteReviewPhotos()에서 reviewFolderRef.listAll {}로 적용하여 파일 목록을 가져옴.

// 특정 폴더의 모든 파일 목록 가져오기
let storageRef = Storage.storage().reference().child("images")
storageRef.listAll { result, error in
    if let error = error {
        print("❌ 파일 목록 가져오기 실패: \(error.localizedDescription)")
        return
    }
    for item in result.items {
        print("📂 파일 경로: \(item.fullPath)")
    }
}

 

🔹 (2) Firebase에서 개별 파일 삭제 (delete())

✅ 이 코드를 deleteReviewPhotos()에서 item.delete {}로 적용하여 개별 파일을 삭제.

// 특정 파일 삭제하기
let desertRef = Storage.storage().reference().child("images/desert.jpg")

desertRef.delete { error in
    if let error = error {
        print("❌ 파일 삭제 실패: \(error.localizedDescription)")
    } else {
        print("✅ 파일 삭제 성공")
    }
}

 

 

🚀 DispatchGroup을 사용한 핵심 이유:

✅ delete()는 개별적으로 실행되는 비동기 작업이므로, 모든 삭제 작업이 끝날 때까지 기다리기 위해 DispatchGroup을 사용.
✅ for item in items {} 반복문에서 각 삭제 요청이 끝나는 시점을 dispatchGroup.notify(queue:)를 통해 감지.
✅ dispatchGroup.notify(queue: .main) {}를 사용하여 모든 삭제 작업이 끝난 후 최종 결과를 반환.

 

https://explorer89.tistory.com/379

 

❌ 리뷰 삭제 ... 왜 안되니?

🔍 GTMSessionFetcher 오류 분석GTMSessionFetcher 0x10141bab0 (https://firebasestorage.googleapis.com:443/v0/b/movieclip-6a2c3.firebasestorage.app/o?delimiter=/&prefix=users/L21gNu8OeIQa9BIDGuaoFwJmXD63/reviews/E6380A5A-CB53-490C-A044-D8E962791A2D/) wa

explorer89.tistory.com