728x90
SMALL
1. 클래스 개요
DiaryWriteViewModel은 감정일기 작성 과정에서 사용되는 모든 비즈니스 로직을 중앙에서 관리하는 ViewModel이다.
다음의 책임을 수행한다:
- 화면 상태(UI State) 관리
- 감정 선택 및 검증 로직 처리
- 일기 데이터 수정(editableDiary) 관리
- 다음 단계 이동 가능 여부 검증
- ViewController에 errorMessage 전달해 Alert 표시 유도
UI 계층(ViewController / View)은 이 ViewModel의 상태 변화를 구독하여 반응하도록 설계되어 있다.
2. 주요 프로퍼티 및 역할 정의
🧩 2.1 Dependencies
private let store: DiaryProviding
let mode: DiaryMode
- 일기 저장소(DiaryStore)에 접근할 수 있도록 주입된 의존성
- 생성/수정 모드를 나타내는 mode 포함
- 저장/업데이트 시 store를 호출하도록 설계됨
🧩 2.2 UI State
@Published var uiState: DiaryEditorUIState
- 화면 타이틀, 버튼 텍스트 등 UI 구성에 필요한 상태를 제공
- VC는 이를 구독해 UI를 반영함
🧩 2.3 Data (Editable Diary)
@Published var editableDiary: EmotionDiaryModel
private let originalDiary: EmotionDiaryModel?
- 감정일기 편집을 위한 실시간 작업 데이터
- 원본 Diary를 보관(originalDiary)해 업데이트 시 비교/저장 가능
- 감정, 상황, 생각, 행동 모두 여기에 저장됨
🧩 2.4 에러 메시지 전달
@Published var errorMessage: String?
- 유효성 검사 실패 시 메시지를 발행(Publish)
- VC는 이를 구독하여 Alert 띄움
- ViewModel은 Alert 로직을 가지지 않는다 → MVVM 일관성 유지
🧩 2.5 감정 선택 가능 여부 (UI 제어)
@Published var canSelectEmotion: Bool = true
- 특정 상황에서 감정 선택 UI를 잠시 비활성화할 수 있도록 제공
- 현재는 사용 빈도가 낮지만 확장성을 위해 유지
3️⃣ 감정 선택 로직 (trySelectEmotion)
3.1 목적
- 유저가 subEmotion을 탭할 때마다
“이 선택이 유효한지” 판단하고
결과에 따라 ViewModel 상태를 업데이트하거나 UI를 막는 역할.
3.2 처리 흐름
EmotionStepCell → trySelectEmotion → Return true/false → UI 반영 또는 Alert
3.3 세부 규칙
✔ Case 1: 아무것도 선택하지 않은 경우
if emotion.subEmotion.isEmpty { ... }
- 감정을 0개 선택하는 상태는 허용
- 하지만 다음 단계 이동 시에는 별도 검증(canProceedToNextStep)에서 막힘
✔ Case 2: 최대 선택 개수 초과
if emotion.subEmotion.count > 3
- 3개 초과는 즉시 불허
- UI는 변경되지 않음
- errorMessage로 Alert 유도
✔ Case 3: 다른 카테고리 섞기 불가
if currentCategory != .none && currentCategory != emotion.category
- 한 번 카테고리를 선택했다면
그 카테고리 안에서만 subEmotion 선택 가능하도록 제한
✔ Case 4: 정상 선택
updateEmotion(emotion)
- 감정 선택이 규칙을 만족하면 editableDiary에 반영
- StepCell이 reloadItems를 통해 UI 갱신
4️⃣ 다음 단계 이동 유효성 검사 (canProceedToNextStep)
4.1 목적
유저가 "다음" 버튼을 눌렀을 때
해당 단계에서 필요한 최소 조건을 충족했는지 검사한다.
4.2 감정 단계(.emotion) 검증 규칙
if editableDiary.emotion.subEmotion.isEmpty {
triggerError("최소한 1개의 감정을 선택해주세요!")
return false
}
- 감정 선택이 0개라면 다음 단계로 이동할 수 없다
- 선택 시 trySelectEmotion에서는 허용하지만
"다음 단계 전환"에서는 별도의 검증 절차로 처리함 → UX적으로 자연스러움
4.3 다른 단계(situation/thought/action)는 현재 패스
- 필요 시 확장 가능
- 이후 일기 내용 단계에서도 동일 패턴 유지 가능
5️⃣ 에러 처리(triggerError)
역할
- 메시지를 errorMessage에 publish
- VC는 이를 구독하여 Alert 표시
private func triggerError(_ message: String) {
errorMessage = message
}
ViewModel은 Alert을 띄우지 않음 → UI 책임은 VC에게 있음
MVVM 아키텍처에서 매우 올바른 구조.
6️⃣ 전체 데이터/이벤트 흐름도
(유저 탭)
EmotionCategoryCell
↓ (이벤트 전달)
EmotionStepCell
↓ (candidate 생성)
ViewModel.trySelectEmotion
↓ true/false
EmotionStepCell UI 반영 or 유지
↓
editableDiary.emotion 업데이트
그리고 다음 버튼 흐름:
Next 버튼터치
↓
ViewModel.canProceedToNextStep
↓ (false)
Alert 표시728x90
LIST
'감정일기(가칭)' 카테고리의 다른 글
| 📘 단계형 UI(Form Wizard)에서 유효성 검사를 어떻게 설계할까?— 감정일기 작성 화면(DiaryWrite)을 구현하면서 얻은 설계 인사이트 (0) | 2025.12.09 |
|---|---|
| 📘 DiaryWriteViewController 설계 문서 (함수·프로퍼티 단위 상세 버전) (0) | 2025.12.09 |
| 📘 Emotion Selection Logic - 설계 문서(개발 전 단계 로직 정리) (0) | 2025.12.09 |
| ❌ 한 셀만 열리고, 나머지는 닫히는 상태(sync)가 유지되지 않는다... (0) | 2025.12.08 |
| 🤔 왜 지금 애니메이션이 딱딱하게 보이고, fade-in/fade-out이 안 보이는지, 그리고 정확한 해결 방법까지 아주 명확히 설명 (0) | 2025.12.08 |