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

🍋 DiaryStore 이후의 ViewModel 설계 — 화면 단위로 나누는 이유와 구조

by 밤새는 탐험가89 2025. 10. 24.
728x90
SMALL

지난 포스트에서는 DiaryStore를 통해 감정일기 데이터를 전역에서 관리하는
SSOT(Single Source of Truth, 단일 데이터 출처) 구조를 완성했습니다.

 

이번에는 이 DiaryStore를 실제 화면(View) 에서 어떻게 사용할지,
즉, ViewModel을 어떻게 나누고 설계할지에 대해 다뤄봅니다.


🎯 핵심 질문

DiaryStore가 전역 상태를 관리한다면,
그 데이터를 사용하는 ViewModel은 “기능 단위”로 만들어야 할까,
아니면 “화면 단위”로 만들어야 할까?


🧭 결론부터 — “화면 단위 ViewModel”이 정답이다.

✅ “데이터 관리”는 DiaryStore

✅ “화면 상태 관리”는 “화면 단위 ViewModel”

 

이 조합이 MVVM 아키텍처의 본질을 가장 깔끔하게 살린 구조입니다.


🧩 기능 단위 vs 화면 단위 ViewModel

구분 기능 단위 화면 단위
장점 여러 곳에서 재사용 가능 View와 1:1 관계, 구조 명확
단점 기능 간 의존성이 얽힘 일부 코드 중복 가능
적합도 대규모 모듈, 복잡한 서비스 ✅ Core Data 기반 개인 앱에 가장 적합
예시 EmotionAnalysisViewModel,
DiarySearchViewModel
HomeViewModel,
DiaryListViewModel,
DiaryEditorViewModel

 

👉 결론적으로, 지금 단계에서는 “화면(ViewController) 기준으로

ViewModel을 분리”하는 게 가장 자연스럽고 유지보수가 쉽습니다.


🧱 프로젝트 구조 예시

ViewModel/
 ├─ SSOT/
 │   └─ DiaryStore.swift       // 전역 데이터 관리 (Single Source of Truth)
 │
 ├─ Home/
 │   └─ HomeViewModel.swift    // 홈화면: 명언, 최근 일기, 주간 감정 통계
 │
 ├─ DiaryList/
 │   └─ DiaryListViewModel.swift // 전체 일기 목록 관리
 │
 ├─ DiaryDetail/
 │   └─ DiaryDetailViewModel.swift // 일기 상세 조회
 │
 └─ DiaryEditor/
     └─ DiaryEditorViewModel.swift // 일기 작성 및 수정

 

각 화면(ViewController)은 자신만의 ViewModel을 가지며,
공통 데이터는 DiaryStore에서 받아옵니다.


🔄 데이터 흐름 요약

Core Data  ←→  DiaryStore (SSOT)
                    ↓
           HomeViewModel / DiaryListViewModel / DiaryEditorViewModel
                    ↓
                  View(UI)

 

 

- DiaryStore는 앱 전체에서 하나만 존재합니다.

- ViewModel은 오직 구독(subscribe) 만 합니다.

- Core Data의 상태가 바뀌면 → DiaryStore가 감지 → ViewModel이 갱신 → View(UI) 반영


🍋  HomeViewModel 구현 예시

import Combine
import Foundation

final class HomeViewModel {
    private let store: DiaryProviding
    private var cancellables = Set<AnyCancellable>()

    @Published private(set) var recentDiary: EmotionDiaryModel?
    @Published private(set) var weeklySummary: [EmotionCategory: Int] = [:]

    init(store: DiaryProviding = DiaryStore.shared) {
        self.store = store

        // DiaryStore의 Publisher를 구독
        store.diariesPublisher
            .sink { [weak self] diaries in
                self?.recentDiary = diaries.first
                self?.weeklySummary = store.countByEmotion(inWeekOf: Date())
            }
            .store(in: &cancellables)
    }
}

 

 

🧠 동작 방식

- DiaryStore가 Core Data에서 변화를 감지하면 diariesPublisher가 새로운 데이터를 발행(emit)

- HomeViewModel은 이를 구독하고 필요한 데이터(recentDiary, weeklySummary)만 가공해서 보관

- View(UI)는 @Published 프로퍼티를 감시하면서 자동으로 갱신됨

728x90
LIST