• M(Model): 순수 데이터/비즈니스 객체. 저장·불러오기 로직은 Repository에서
• V(View/Controller): 화면 그리는 쪽(UIKit에서는 UIView/UIViewController). 입력 이벤트를 받고, ViewModel의 출력만 바인딩해서 그려줌
• VM(ViewModel): 화면에 필요한 상태/로직을 소유. 모델을 가공해 UI에서 바로 쓸 수 있는 형태로 내보내고, 사용자 입력을 받아 Repository를 통해 Model을 변경
⸻
타이머 추가
1. TimerViewController의 “추가하기” 버튼 탭
2. TimerEditViewController 표시(빈 상태의 TimerEditViewModel 주입)
3. 사용자가 입력 → VC가 VM에 값 전달 (혹은 텍스트 변경 시 VM 바인딩)
4. 저장 버튼 탭 → VC가 viewModel.save() 호출
5. VM이 중복 검사 → 통과 시 TimerRepository.insert(...)
6. 성공 콜백 → VC가 목록 리로드(또는 델리게이트/노티로 상위에 반영) → 닫기
타이머 편집
1. 리스트 셀 탭 → 편집 VC 표시(기존 엔티티 담긴 TimerEditViewModel(editing:) 주입)
2. 기존 값 프리필(VC는 VM의 출력으로 UI 세팅)
3. 수정 후 저장 → VM이 update(...) 호출 → 성공 시 VC가 스냅샷 갱신
타이머 삭제
1. 리스트에서 삭제 액션 → VC가 커스텀 알럿(PodoAlertController) 표시
2. 확인 시 Repository의 delete(...) 호출(여긴 VC에서 직접 or VM 통해서 — 한 곳으로 일원화 권장)
3. 성공 후 Diffable Snapshot 재적용 → 셀 사라짐
데이터 접근 로직(저장·조회·삭제 등)을 한 곳에 모아두는 계층.
Model(TimerModel 등)과 Persistence Layer(SwiftData, CoreData, Realm 등) 사이에서 중간 추상화 계층.
• ViewModel이나 ViewController는 데이터가 어디에/어떻게 저장되는지 몰라도 됨. 그냥 repo.fetchAll() 같은 API만 부르면 됨.
관심사 분리 때문에 사용
VC/VM은 데이터가 필요하다만 알면 되고, 저장 방식은 Repository가 책임
나중에 SwiftData → CoreData, Realm, CloudKit 등 바꿔도 VC/VM은 안 바뀜
-> 의존성 주입
VC에서 직접 SwiftData 안 쓰고, 생성자에 TimerRepository를 주입.
init(repository: TimerRepository) {
self.repository = repository
super.init(nibName: nil, bundle: nil)
}
-> 결합도 낮추기
시간에 따라 변하는 이벤트 스트림을 나타내는 객체
값이 생길 때마다(onNext), 에러가 날 때(onError), 끝날 때(onCompleted) 이벤트를 방출 옵저버(구독자)가 그걸 듣고 반응
CalendarView.swift
private let selectedDateRelay = BehaviorRelay<Date>(value: Date())
var selectedDate: Observable<Date> { selectedDateRelay.asObservable() }
뷰 내부에서 선택된 날짜 상태를 Relay로 관리.
외부에서는 selectedDate Observable을 구독해서 반응.