728x90
SMALL
Core Data + FileManager + Combine 조합으로 데이터와 이미지까지 한 번에 저장하기
✨ 목표
지출 내역을 저장할 때,
- 기본 정보(날짜, 금액, 메모 등)는 Core Data에
- 선택 이미지가 있다면 FileManager에 저장하고 → 저장된 이미지 경로를 Core Data에 함께 저장해 관리하는 방식.
✅ 핵심 클래스: TransactionCoreDataManager
final class TransactionCoreDataManager {
// MARK: - Variable
static let shared = TransactionCoreDataManager()
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
private let storageManager = TransactionFileManager.shared
}
- 싱글톤 패턴으로 한 곳에서 데이터 저장/불러오기 관리
- context는 Core Data의 NSManagedObjectContext
- storageManager는 이미지를 저장하기 위한 FileManager 객체
📦 트랜잭션(지출 내역) 저장 함수
func createTransaction(_ transaction: ExpenseModel) -> AnyPublisher<ExpenseModel, Error> {
return Future { [weak self] promise in
guard let self = self else { return }
var savePath: String? = nil
if let selectedImage = transaction.image {
savePath = self.storageManager.saveImage(selectedImage, transaction.id.uuidString)
if savePath == nil {
promise(.failure(NSError(domain: "TransactionImageSaveError", code: 1)))
return
}
}
print("🧾 저장할 정보 로그...")
let expenseEntity = ExpenseEntity(context: self.context)
expenseEntity.id = transaction.id.uuidString
expenseEntity.amount = Int64(transaction.amount)
expenseEntity.date = transaction.date
expenseEntity.category = transaction.category
expenseEntity.transaction = transaction.transaction.rawValue
expenseEntity.memo = transaction.memo
expenseEntity.imagePath = savePath ?? ""
do {
try self.context.save()
if let model = expenseEntity.toModel() {
promise(.success(model))
} else {
promise(.failure(NSError(domain: "ModelConversionError", code: 2)))
}
} catch {
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
⚙️ 실행 흐름 (단계별 설명)
| 단계 | 설명 |
| 1단계 | ExpenseModel을 인자로 받아 트랜잭션 저장 시도 |
| 2단계 | 이미지가 있다면 FileManager를 통해 저장 (파일명은 UUID 기반) |
| 3단계 | 이미지 경로가 nil이면 실패 처리 |
| 4단계 | ExpenseEntity 인스턴스 생성 및 데이터 바인딩 |
| 5단계 | Core Data에 저장 후, Model로 변환해서 전달 |
| 6단계 | 성공/실패 여부에 따라 Combine Publisher 반환 |
💡 왜 이렇게 구성했는가?
- MVVM 구조에서 ViewModel → Model → CoreDataManager로 흘러가는 흐름을 유지
- Combine의 Future를 사용하여 비동기 처리와 에러 핸들링을 분리
- 이미지 저장은 DB에 직접 넣기보다 FileManager에 저장 후 경로만 DB에 저장, 성능과 구조 모두 고려
✅ 이렇게 하면 좋은 점
- 이미지 용량 걱정 없음 → DB에 이미지를 넣지 않음
- 이미지 로딩 분리 → 뷰에서 FileManager 경로만 받아 직접 불러오기 가능
- Combine 도입으로 ViewModel에서도 코드 간결해짐
- 테스트가 쉬워짐 → 저장 성공/실패를 Combine으로 바로 구독 가능
🔚 마무리
이 구조는 단순히 데이터를 저장하는 수준을 넘어, 앱의 확장성과 유지보수를 고려한 아키텍처적 선택
- 이미지가 필수인 앱(앨범, 메모 등)
- MVVM + Combine 기반 구조
- Core Data + FileManager 조합
728x90
LIST
'Project > ReceiptMind' 카테고리의 다른 글
| 📚 iOS MVVM 가계부 앱 - Core Data 수정(Update) + 이미지까지 깔끔히 반영하는 법 (1) | 2025.08.02 |
|---|---|
| 📚Core Data로 지출 내역 불러오기(Read) 정리 (0) | 2025.08.02 |
| 📦 Swift에서 FileManager로 이미지 저장하기 – 이미지 저장, 경로 관리, 불러오기까지의 전체 흐름 정리 (1) | 2025.07.31 |
| 📱 iOS 가계부 앱 데이터 모델 설계하기 (3) | 2025.07.30 |
| ✅ 앱 수정 후 재심사 제출하는 방법 (App Store Connect) (3) | 2025.07.30 |