728x90
SMALL
LemonLog의 메인 홈 화면에는 달력이 있고,
사용자가 특정 날짜를 선택하면 그 날 작성된 감정일기 목록을 보여주고 싶었다.
이때 가장 고민됐던 지점은 다음 질문이다.
“달력 화면(MainHome)에서
CoreData에서 감정일기를 미리 불러와서 리스트 화면에 넘겨야 할까?
아니면 날짜만 넘기고, 리스트 화면에서 다시 불러와야 할까?”
이 글에서는 그 고민에 대한 결론과,
LemonLog에서 선택한 아키텍처 설계 방향을 정리한다.
❓ 선택지 1
MainHomeVC에서 날짜 + 감정일기 리스트를 모두 전달하는 방식
let diaries = diaryStore.fetchDiaries(date: selectedDate)
let vc = DiaryListViewController(date: selectedDate, diaries: diaries)
문제점
- MainHomeVC의 책임 과다
- 이미 달력, 홈 UI, 상태 관리 역할만으로도 충분히 복잡함
- “리스트 화면용 데이터 구성”까지 담당하면 SRP 위반
- 확장에 취약
- 정렬, 필터, 삭제, 수정 시
→ MainHomeVC가 계속 데이터 흐름에 개입해야 함
- 정렬, 필터, 삭제, 수정 시
- 재사용 불가능
- 같은 “날짜별 일기 리스트” 화면을
검색, 알림, 위젯 등 다른 진입점에서 재사용하기 어려움
- 같은 “날짜별 일기 리스트” 화면을
👉 ViewController 간 데이터 전달이 비대해지는 전형적인 안 좋은 패턴
✅ 선택지 2 (채택)
날짜만 전달하고, 리스트 화면의 ViewModel이 직접 CoreData 조회
let vc = DiaryListViewController(selectedDate: date)
리스트 화면에서는 자신의 ViewModel이
해당 날짜를 기준으로 데이터를 로딩한다.
구조
CoreData
→ DiaryStore
→ DiaryListViewModel
→ DiaryListViewController
🧠 왜 이 방식이 더 좋은가?
1️⃣ 책임 분리가 명확해진다
- MainHomeVC
→ “날짜 선택”만 담당 - DiaryListVC
→ “해당 날짜의 일기 표시”만 담당
각 화면은 자기 역할만 수행한다.
2️⃣ Single Source of Truth 유지
데이터는 항상 하나의 출처에서만 온다.
CoreData → DiaryStore → ViewModel → View
- VC가 CoreData를 직접 알 필요가 없음
- 데이터 흐름이 단순하고 예측 가능해짐
3️⃣ 확장과 변경에 강하다
- 리스트 정렬 추가
- 일기 삭제 / 수정
- Combine 기반 실시간 UI 갱신
👉 모두 DiaryListViewModel 내부 변경만으로 해결 가능
4️⃣ LemonLog 기존 설계와 일관성 유지
LemonLog는 이미 다음 원칙을 사용 중이다.
- ViewController는 데이터 소스를 모른다
- 데이터 로딩과 가공은 ViewModel의 책임
- Store(DiaryStore)를 통해서만 CoreData 접근
날짜 기반 리스트 화면도 이 원칙을 그대로 따른다.
✨ 예시 코드
MainHomeViewController
func didSelectDate(_ date: Date) {
let viewModel = DiaryListViewModel(
date: date,
diaryStore: diaryStore
)
let vc = DiaryListViewController(viewModel: viewModel)
navigationController?.pushViewController(vc, animated: true)
}
DiaryListViewModel
final class DiaryListViewModel {
let date: Date
@Published private(set) var diaries: [EmotionDiary] = []
private let diaryStore: DiaryProviding
private var cancellables = Set<AnyCancellable>()
init(date: Date, diaryStore: DiaryProviding) {
self.date = date
self.diaryStore = diaryStore
bind()
}
private func bind() {
diaryStore.diariesPublisher
.map { diaries in
diaries.filter { $0.createdAt.isSameDay(as: self.date) }
}
.assign(to: &$diaries)
}
}
❓ MainHomeViewModel이 있는데, 리스트용 ViewModel을 따로 만들어야 할까?
👉 반드시 따로 만들어야 한다.
이유
| ViewModel | 책임 |
| MainHomeViewModel | 달력, 월 이동, 일기 존재 여부 표시 |
| DiaryListViewModel | 특정 날짜의 일기 리스트 관리 |
ViewModel도 화면 단위 책임을 가진다.
“ViewModel을 재사용하겠다는 이유로
서로 다른 화면의 상태를 하나에 몰아넣는 순간,
그 ViewModel은 곧 망가진다.”
🧩 이 설계의 핵심 원칙
VC 간에는 상태(날짜)만 전달한다
데이터 로딩은 항상 해당 화면의 ViewModel이 담당한다
이 원칙을 지키면:
- 화면이 늘어나도 구조가 무너지지 않고
- 테스트와 유지보수가 쉬워지며
- 중형 규모 앱까지 자연스럽게 확장된다
728x90
LIST