728x90
SMALL
가계부 앱을 MVVM 구조로 개발하면서 핵심 역할을 하는 TransactionViewModel에 대해 소개합니다.
이 글에서는 ViewModel의 역할, 내부 구조, init 설계 이유, 그리고 View와의 데이터 흐름까지 모두 짚어볼게요.
🎯 ViewModel이란?
MVVM에서 ViewModel은 View와 Model 사이의 중간 매개체로 작동합니다.
사용자의 입력을 받아 Model에 반영하고, Model의 데이터를 가공해 View에 제공합니다.
💡 이 글에서는 실제 프로젝트에 사용한 TransactionViewModel을 기준으로 설명합니다.
📦 전체 구조 살펴보기
final class TransactionViewModel {
// MARK: - Published Properties
@Published var transaction: ExpenseModel?
@Published var transactions: [ExpenseModel] = []
@Published var errorMessage: String?
// MARK: - Derived Data
var currentCategories: [String] {
transaction?.transaction.categoryOptions ?? []
}
var currentCategoryIcons: [String: String] {
transaction?.transaction.categoryImageMap ?? [:]
}
// MARK: - Dependencies
private var cancellables: Set<AnyCancellable> = []
private let transactionManager = TransactionCoreDataManager.shared
let mode: AddTransactionMode?
// MARK: - Init
init(mode: AddTransactionMode? = nil) {
self.mode = mode
if let mode = mode {
switch mode {
case .create:
self.transaction = ExpenseModel(
id: UUID(),
transaction: .expense,
category: "",
amount: 0,
image: nil,
date: Date(),
memo: ""
)
case .edit(let id):
readByIDTransaction(by: id)
}
} else {
// 기본 초기화 (예: 홈 화면 등에서 사용)
self.transaction = nil
}
}
}
🔍 핵심 초기화 로직 설명
🧩 mode 기반 설계란?
TransactionViewModel은 생성 시점에 mode라는 enum 값을 받아 create(새로운 항목 생성)인지, edit(기존 항목 수정)인지 구분합니다.
enum AddTransactionMode {
case create
case edit(UUID)
}
이렇게 설계하면 다음과 같은 장점이 있습니다:
- 하나의 ViewModel로 두 역할을 처리할 수 있어 코드 재사용성이 높아짐
- ViewController에서 복잡한 분기를 만들지 않고도 상황에 맞게 대응 가능
🏠 홈 화면 등에서는 왜 transaction = nil?
// 기본 초기화 (예: 홈 화면 등에서 사용)
self.transaction = nil
이건 중요한 설계 포인트입니다.
- 홈 화면 등에서는 단일 항목이 아니라 전체 거래 리스트만 다루기 때문에, 개별 transaction은 필요하지 않습니다.
- 그래서 ViewModel에서 transaction = nil로 초기화하여 불필요한 상태값을 피하고, 가볍게 유지할 수 있습니다.
🛠️ 주요 기능 함수
✅ 생성 함수 (createTransaction())
func createTransaction() {
guard let transaction = transaction else {
errorMessage = "Transaction no data"
return
}
transactionManager.createTransaction(transaction)
.sink(receiveCompletion: { completion in
if case .failure(let error) = completion {
self.errorMessage = error.localizedDescription
}
}, receiveValue: { [weak self] savedTransaction in
self?.transactions.append(savedTransaction)
self?.transaction = savedTransaction
})
.store(in: &cancellables)
}
입력값 검증 → Core Data 저장 → 성공 시 목록과 상태 갱신까지 한 번에 처리합니다.
✨ 마무리 요약
- TransactionViewModel은 생성/수정/조회의 모든 역할을 맡는 ViewModel입니다.
- mode 값으로 상황을 구분하고, 이에 따라 초기 상태(transaction)를 설정합니다.
- 홈 화면 등에서는 불필요한 상태 생성을 피하기 위해 transaction = nil로 초기화합니다.
- 이 구조는 MVVM에서 ViewModel의 책임을 명확히 나누는 좋은 예시입니다.
728x90
LIST
'Project > ReceiptMind' 카테고리의 다른 글
| 💡 Swift에서 배열 필터링하기: filteredTransactions 메서드 완전 해부 (0) | 2025.08.03 |
|---|---|
| 🧾 iOS MVVM 가계부 앱 - 거래 내역 조회(Read), 수정(Update), 삭제(Delete) 기능 완벽 구현 (1) | 2025.08.03 |
| 📦 iOS MVVM 가계부 앱 - Core Data 삭제(Delete) + 이미지 제거까지 정리 (1) | 2025.08.02 |
| 📚 iOS MVVM 가계부 앱 - Core Data 수정(Update) + 이미지까지 깔끔히 반영하는 법 (1) | 2025.08.02 |
| 📚Core Data로 지출 내역 불러오기(Read) 정리 (0) | 2025.08.02 |