본문 바로가기
Project/ReceiptMind

📦 iOS MVVM 가계부 앱 - Core Data 삭제(Delete) + 이미지 제거까지 정리

by 밤새는 탐험가89 2025. 8. 2.
728x90
SMALL

 가계부 앱에서 한 번 입력한 지출/수입 데이터를 삭제하는 기능은 반드시 필요합니다. 특히 사용자가 실수로 입력했거나, 잘못된 이미지를 포함한 경우 Core Data + 이미지 파일까지 함께 삭제하는 구조가 되어야 합니다.

 이번 글에서는 deleteTransaction(id:) 함수를 중심으로 Core Data에서의 삭제 처리 흐름과 이미지 정리 방식까지 상세히 정리합니다.


🧩 1. 삭제 함수 코드

func deleteTransaction(id: UUID) -> AnyPublisher<Bool, Error> {
    return Future { [weak self] promise in
        guard let self = self else {
            print("❌ deleteTransaction: self가 nil입니다.")
            return
        }
        
        let fetchRequest: NSFetchRequest<ExpenseEntity> = ExpenseEntity.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "id == %@", id.uuidString)
        
        do {
            let result = try self.context.fetch(fetchRequest)
            guard let entityToDelete = result.first else {
                print("❌ deleteTransaction: 해당 ID에 해당하는 항목을 찾을 수 없음")
                promise(.failure(NSError(domain: "TransactionNotFound", code: 404)))
                return
            }
            
            // 이미지 경로가 있는 경우 파일도 삭제
            if let imagePath = entityToDelete.imagePath {
                let imageDeleted = self.storageManager.deleteFolder(for: imagePath)
                if imageDeleted {
                    print("🗑️ 이미지 삭제 성공")
                } else {
                    print("⚠️ 이미지 삭제 실패 (파일이 없을 수도 있음)")
                }
            }
            
            self.context.delete(entityToDelete)
            
            try self.context.save()
            print("✅ deleteTransaction: Core Data 삭제 성공")
            promise(.success(true))
        } catch {
            print("❌ deleteTransaction: 삭제 중 오류 발생 - \(error.localizedDescription)")
            promise(.failure(error))
        }
    }
    .eraseToAnyPublisher()
}

🧭 함수 흐름 설명: deleteTransaction(_:) 은 어떻게 작동하는가?

사용자가 특정 트랜잭션을 삭제 요청하면, 이 함수는 Core Data와 파일 시스템을 함께 정리합니다. 각 단계의 흐름은 아래와 같습니다.

 

🔍 1단계: 해당 ID의 엔티티 조회

fetchRequest.predicate = NSPredicate(format: "id == %@", id.uuidString)

 

  • 무엇을 하는가?
    전달받은 UUID로 일치하는 데이터를 Core Data에서 찾습니다.
  • 포인트
    존재하지 않는다면 실패 처리하며, 이후 단계는 실행되지 않습니다.

🖼️ 2단계: 이미지 파일 삭제 (있다면)

if let imagePath = entityToDelete.imagePath {
    let imageDeleted = self.storageManager.deleteFolder(for: imagePath)
}

 

 

  • 무엇을 하는가?
    이미지가 저장되어 있던 폴더(파일 경로)를 찾아 삭제합니다.
  • 왜 필요한가?
    Core Data는 이미지 자체를 저장하지 않고 경로만 저장하므로, 직접 파일 시스템에서 삭제해야 공간이 낭비되지 않습니다.

🗑️ 3단계: Core Data에서 데이터 삭제

self.context.delete(entityToDelete)

 

  • 무엇을 하는가?
    찾은 ExpenseEntity 객체를 context에서 제거합니다.

💾 4단계: 저장 & 성공 반환

try self.context.save()
promise(.success(true))
  • 무엇을 하는가?
    변경 사항을 Core Data에 반영하고, 성공 결과를 Combine 구독자에게 전달합니다.

🔁 요약 플로우 차트

📍 사용자로부터 삭제할 UUID 전달
     ↓
1️⃣ Core Data에서 해당 ID의 엔티티 검색
     ↓
2️⃣ 이미지 경로가 있다면 → 파일 시스템에서 이미지 삭제
     ↓
3️⃣ Core Data에서 해당 엔티티 삭제
     ↓
4️⃣ context.save()로 저장 후 → 성공 반환

💡 정리 포인트

  • 단순히 CoreData만 지우면 안 되고, 연관된 이미지 파일까지 반드시 삭제해야 실제 앱 용량 관리가 됩니다.
  • 삭제 후에도 .sink(receiveValue:) 등을 통해 UI 상태를 갱신할 수 있도록 Combine 구조를 활용합니다.
  • ID 기준 삭제는 안정적인 방식이며, 이 방식은 iCloud 연동이나 멀티 디바이스 앱에서도 유용하게 활용됩니다.

 

[함꼐 보면 좋은 글]

https://explorer89.tistory.com/445

728x90
LIST