본문 바로가기
감정일기(가칭)

📅 날짜 선택 → 감정일기 리스트 화면 설계하기 (LemonLog 사례)

by 밤새는 탐험가89 2025. 12. 16.
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