🔍 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/)
was already running
이 오류 메시지는 Firebase Storage에서 같은 URL에 대한 여러 개의 비동기 요청이 동시에 실행되고 있음을 의미합니다.
🤔 리뷰 삭제
1️⃣ Firebase Storage에서 이미지 삭제
2️⃣ Firestore 데이터 삭제
🔥 최종 정리: 동작 흐름
1️⃣ Firebase Storage에서 리뷰와 관련된 이미지 삭제 요청
2️⃣ 이미지 삭제가 완료되면 Firestore에서 해당 리뷰 문서 삭제
📌 해결책
✅ Firebase의 Storage에서 리뷰로 등록된 이미지 삭제하는 메서드 구현
func deleteReviewPhotos(userID: String, reviewID: String) -> AnyPublisher<Void, Error> {
let storageRef = storage.reference()
let folderPath = "users/\(userID)/reviews/\(reviewID)/"
let reviewFolderRef = storageRef.child(folderPath)
return Future<Void, Error> { promise in
reviewFolderRef.listAll { result in
switch result {
case .success(let storageListResult):
let items = storageListResult.items
if items.isEmpty {
print("✅ 삭제할 이미지 없음 (리뷰만 삭제)")
promise(.success(())) // ✅ 이미지가 없어도 Firestore 리뷰 삭제 진행
return
}
let dispatchGroup = DispatchGroup()
var deletionErrors: [Error] = []
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()
}
}
dispatchGroup.notify(queue: .main) {
if deletionErrors.isEmpty {
print("✅ 모든 이미지 삭제 완료")
promise(.success(()))
} else {
print("❌ 일부 이미지 삭제 실패")
}
}
case .failure(let error):
print("❌ 이미지 목록 불러오기 실패: \(error.localizedDescription)")
promise(.failure(error))
}
}
}
.eraseToAnyPublisher()
}
✅ Firebase의 Database에서 리뷰ID를 통해 리뷰 삭제하는 메서드 구현
func collectionReviews(delete reviewID: String) -> AnyPublisher<Void, Error> {
guard let userID = Auth.auth().currentUser?.uid else {
return Fail(error: NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "로그인한 사용자가 없습니다."]))
.eraseToAnyPublisher()
}
return Future<Void, Error> { promise in
self.db.collection("users")
.document(userID)
.collection("reviews")
.document(reviewID)
.delete { error in
if let error = error {
promise(.failure(error))
} else {
promise(.success(()))
}
}
}
.eraseToAnyPublisher()
}
✅ ViewModel에서 리뷰로 등록된 이미지를 삭제 후 (deleteReviewPhotos), reviewID에 해당하는 리뷰 삭제
func deleteUserReview(reviewID: String) {
guard let userID = Auth.auth().currentUser?.uid else {
self.error = "유저 정보 없음 "
return
}
StorageManager.shared.deleteReviewPhotos(userID: userID, reviewID: reviewID)
.flatMap {
DatabaseManager.shared.collectionReviews(delete: reviewID)
}
.sink { [weak self] completion in
switch completion {
case .failure(let error):
self?.error = "리뷰 삭제 실패: \(error.localizedDescription)"
case .finished:
self?.isUserReviewDeleted = true
}
} receiveValue: {[weak self] in
self?.isUserReviewDeleted = true
}
.store(in: &cancellables)
}
'Project > MovieClip' 카테고리의 다른 글
❌ 컬렉션 뷰를 가로 스크롤할 때 다음 이미지가 살짝 보이는 문제 (0) | 2025.03.11 |
---|---|
❌ 컴파일 오류 발생... (0) | 2025.03.09 |
📌 회원탈퇴 구현 (Storage, Firestore Database) (1) | 2025.03.04 |
✅ 프로필 수정하기! (기존 프로필 입력 창 사용하기) (0) | 2025.03.04 |
❌ 문제 해결... ProfileItem이 Hashable 및 Equatable 프로토콜을 준수하지 않는다? (0) | 2025.03.01 |