728x90
SMALL
가계부 앱을 만들면서 생기는 대표적인 고민 중 하나는 "기존에 등록된 내역을 수정할 때 이미지나 메모를 어떻게 반영하지?" 입니다.
이번 글에서는 Core Data에서 기존 내역을 업데이트하고, 선택적으로 이미지까지 덮어쓸 수 있는 구조를 소개합니다.
💡 목표 기능
- 사용자가 등록한 기존 내역(지출/수입)을 수정
- 메모, 금액, 카테고리 등 변경 사항 반영
- 기존 이미지가 있을 경우:
- 이미지 삭제
- 이미지 교체
- 기존에 이미지가 없더라도 새로 추가 가능
📦 업데이트 함수 코드
func updateTransaction(_ updatedTransaction: ExpenseModel) -> AnyPublisher<ExpenseModel, Error> {
return Future { [weak self] promise in
guard let self = self else {
print("❌ TransactionManager: self가 nil")
return
}
let fetchRequest: NSFetchRequest<ExpenseEntity> = ExpenseEntity.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "id == %@", updatedTransaction.id.uuidString)
fetchRequest.fetchLimit = 1
do {
guard let entity = try self.context.fetch(fetchRequest).first else {
print("❌ 업데이트할 항목 없음")
promise(.failure(NSError(domain: "TransactionUpdateError", code: 404)))
return
}
// 기본 필드 업데이트
entity.transaction = updatedTransaction.transaction.rawValue
entity.amount = Int64(updatedTransaction.amount)
entity.date = updatedTransaction.date
entity.category = updatedTransaction.category
entity.memo = updatedTransaction.memo
// 이미지 처리
if let newImage = updatedTransaction.image {
// 기존 이미지가 있을 경우 제거
if let oldPath = entity.imagePath, !oldPath.isEmpty {
let deleted = self.storageManager.deleteFolder(for: oldPath)
if !deleted {
print("⚠️ 기존 이미지 삭제 실패: \(oldPath)")
}
}
// 새 이미지 저장
if let newPath = self.storageManager.saveImage(newImage, updatedTransaction.id.uuidString) {
entity.imagePath = newPath
} else {
print("❌ 새 이미지 저장 실패")
promise(.failure(NSError(domain: "ImageSaveFailed", code: 500)))
return
}
} else {
// 새 이미지가 없는 경우
// 기존 이미지 삭제 대상일 수도 있음
if let oldPath = entity.imagePath, !oldPath.isEmpty {
let deleted = self.storageManager.deleteFolder(for: oldPath)
if deleted {
entity.imagePath = nil
print("🗑️ 기존 이미지 삭제 완료")
} else {
print("⚠️ 기존 이미지 삭제 실패")
}
} else {
print("ℹ️ 삭제할 이미지 없음")
}
}
try self.context.save()
print("✅ 업데이트 성공!")
promise(.success(updatedTransaction))
} catch {
print("❌ Core Data 업데이트 실패: \(error.localizedDescription)")
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
🧭 함수 흐름 설명: updateTransaction(_:) 은 어떻게 작동하는가?
🔢 1단계: 기존 데이터 탐색
let fetchRequest: NSFetchRequest<ExpenseEntity> = ExpenseEntity.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "id == %@", updatedTransaction.id.uuidString)
fetchRequest.fetchLimit = 1
- 무엇을 하는가?
Core Data 안에서, 수정하고자 하는 UUID와 동일한 ExpenseEntity 하나를 찾기 위해 FetchRequest를 생성합니다. - 왜 중요한가?
수정은 "기존 데이터"가 존재할 때만 의미가 있기 때문입니다. 없는 데이터를 수정하려 하면 실패 처리해야 하죠
🔁 2단계: 기본 필드 수정
entity.transaction = updatedTransaction.transaction.rawValue
entity.amount = Int64(updatedTransaction.amount)
entity.date = updatedTransaction.date
entity.category = updatedTransaction.category
entity.memo = updatedTransaction.memo
- 무엇을 하는가?
사용자로부터 전달받은 최신 데이터(ExpenseModel)를 Core Data의 Entity에 반영합니다. - 포인트
이 단계는 이미지와 관계없이, 금액, 카테고리, 날짜, 메모 등의 "텍스트 기반 정보"만을 업데이트합니다.
🖼️ 3단계: 이미지 처리 (조건 분기)
if let newImage = updatedTransaction.image {
// 기존 이미지 삭제 → 새 이미지 저장
} else {
// 기존 이미지만 삭제 (새 이미지 없음)
}
- 무엇을 하는가?
사용자가 이미지를 변경하거나 삭제했는지 여부에 따라 처리 방식을 나눕니다.
💾 4단계: 변경 내용 저장
try self.context.save()
- 무엇을 하는가?
위에서 변경한 필드와 이미지 경로 정보를 Core Data에 실제로 반영합니다. - 에러 발생 시 사용자에게 실패 메시지를 반환합니다.
✅ 5단계: 최종 성공 반환
promise(.success(updatedTransaction))
- 무엇을 하는가?
모든 과정이 성공적으로 완료되면, 변경된 모델을 Combine 구독자에게 전달합니다. - 왜 필요한가?
UI에서는 이 성공 결과를 받아서 화면을 갱신하거나, 알림을 줄 수 있어야 하기 때문입니다.
🔁 요약 플로우 차트
📍 사용자로부터 수정된 ExpenseModel 수신
↓
1️⃣ ID 기반으로 기존 ExpenseEntity 조회
↓
2️⃣ 텍스트 기반 필드(transaction, amount, date 등) 수정
↓
3️⃣ 이미지 처리 (삭제/교체/유지 여부에 따라 분기)
↓
4️⃣ context.save()로 Core Data 저장
↓
5️⃣ 성공 시 → 업데이트된 모델 반환
🔍 로직 정리 (4가지 이미지 처리 상황)
| 기존 이미지 | 새 이미지 | 동작 요약 |
| ✅ 있음 | ✅ 있음 | 기존 이미지 삭제 → 새 이미지 저장 |
| ✅ 있음 | ❌ 없음 | 기존 이미지 삭제 |
| ❌ 없음 | ✅ 있음 | 새 이미지 저장 |
| ❌ 없음 | ❌ 없음 | 이미지 관련 작업 없음 |
[함께 보면 좋은 글]
https://explorer89.tistory.com/444
📚Core Data로 지출 내역 불러오기(Read) 정리
✨ 목표이번 글에서는 가계부 앱에서 Core Data로 저장된 지출 내역을 전체 조회하거나 특정 ID로 조회하는 방법과, Core Data Entity(ExpenseEntity)를 우리가 ViewModel/화면에서 사용하기 위한 데이터 모델(E
explorer89.tistory.com
728x90
LIST
'Project > ReceiptMind' 카테고리의 다른 글
| 💸 iOS MVVM 가계부 앱 - ViewModel(TransactionViewModel) 구조 완벽 정리 (0) | 2025.08.03 |
|---|---|
| 📦 iOS MVVM 가계부 앱 - Core Data 삭제(Delete) + 이미지 제거까지 정리 (1) | 2025.08.02 |
| 📚Core Data로 지출 내역 불러오기(Read) 정리 (0) | 2025.08.02 |
| 💾 Core Data와 FileManager로 지출 내역 저장하기 (2) | 2025.08.01 |
| 📦 Swift에서 FileManager로 이미지 저장하기 – 이미지 저장, 경로 관리, 불러오기까지의 전체 흐름 정리 (1) | 2025.07.31 |