728x90
SMALL
1️⃣ 문제 상황 정리
앱을 처음 실행했을 때:
- 감정일기 ❌
- 주간 감정일기 ❌
- “이번 주 랜덤 일기 1개”를 보여주려 했지만
- 보여줄 데이터 자체가 없음
이때 요구사항은 이거다:
- 데이터가 있으면 → 랜덤 1개 표시
- 데이터가 없으면 → 플레이스홀더 뷰 표시
👉 이건 더 이상 “데이터만 관리”하는 문제가 아니다.
2️⃣ 핵심 판단 기준 (아주 중요)
❓ 이 화면의 상태는
“데이터 하나”로 표현 가능한가?
- [Diary] 하나면 충분한가? ❌
- “없다” + “그래서 placeholder 보여준다” → ❌
이 순간부터 상태는 2개 이상이다:
- randomDiary
- isEmpty
👉 이 시점이 바로 .assign을 내려놓는 지점
3️⃣ 왜 .assign이 부족해지는가?
기존 .assign 패턴
.assign(to: \.randomDiary, on: self)
문제는 이거다:
- 데이터 ❌ → nil
- 근데 UI는:
- placeholder 보여야 함
- 애니메이션 돌릴 수도 있음
- CTA 버튼 노출할 수도 있음
nil 하나로는
UI 상태를 설명하기엔 정보가 부족하다
4️⃣ 그래서 .sink로 “상태 묶음”을 관리한다
✅ 추천 패턴: 상태를 함께 계산
store.diariesPublisher
.map { diaries -> (EmotionDiaryModel?, Bool) in
let weekly = diaries.filter { $0.isInThisWeek }
if weekly.isEmpty {
return (nil, true)
} else {
return (weekly.randomElement(), false)
}
}
.sink { [weak self] diary, isEmpty in
self?.randomDiary = diary
self?.isEmpty = isEmpty
}
.store(in: &cancellables)
여기서 중요한 점
- 상태 판단은 map에서 끝냄
- sink는 오직 “대입”만
- UI 판단 로직 ❌ (ViewController에서)
5️⃣ 이 패턴의 진짜 장점
✔️ 1. “빈 상태”가 명확해진다
isEmpty == true
→ “아직 작성된 감정일기가 없음”
이건:
- 에러 ❌
- 예외 ❌
- 정상 상태 ✅
✔️ 2. 플레이스홀더 UI가 자연스럽다
ViewController에서는:
viewModel.$isEmpty
.sink { [weak self] isEmpty in
self?.placeholderView.isHidden = !isEmpty
self?.contentView.isHidden = isEmpty
}
👉 조건 분기 없는 UI 반응
✔️ 3. 확장에 강하다
나중에 이런 요구가 생겨도:
- “첫 일기 작성 유도 CTA”
- “샘플 감정일기 보여주기”
- “최근 7일 기준으로 변경”
👉 map만 수정하면 된다.
6️⃣ 그럼 언제 다시 .assign으로 돌아갈까?
상태가 하나로 수렴할 때
예를 들어:
- 날짜 선택 → 일기 목록
- 검색 결과 → 리스트
.assign(to: \.diaries, on: self)
하나의 상태 = assign
7️⃣ 상태 설계 관점에서 한 단계 더 나아가면
상태를 enum으로 표현하는 방법 (고급)
enum HomeWeeklyState {
case empty
case content(EmotionDiaryModel)
}
store.diariesPublisher
.map { diaries -> HomeWeeklyState in
let weekly = diaries.filter { $0.isInThisWeek }
return weekly.randomElement().map(HomeWeeklyState.content)
?? .empty
}
.assign(to: &$weeklyState)
마지막으로, 이 한 문장만 기억해
.assign vs .sink의 기준은
데이터 유무가 아니라, 상태의 개수다.
728x90
LIST
'감정일기(가칭)' 카테고리의 다른 글
| ViewController에서 ViewModel의 데이터를 어디까지 써도 될까? (1) | 2025.12.17 |
|---|---|
| Combine에서 .sink와 .assign을 “상태 관리”에 쓰는 2가지 방식 (0) | 2025.12.17 |
| Combine의 .assign vs .sink (0) | 2025.12.16 |
| LemonLog: isSameDay와 bind()로 날짜별 일기 리스트 만들기 (Combine + @MainActor) (0) | 2025.12.16 |
| 📘 diariesPublisher vs fetchDiaryByDay (0) | 2025.12.16 |