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

🤔 프로토콜을 어디에 붙여야 하나? - MVVM 설계의 성숙도

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

🧩 먼저 요약 답부터

🔹 HappinessService → 프로토콜을 채택해야 하는 쪽
🔹 HappinessViewModel → 프로토콜을 의존해야 하는 쪽

 

즉,
ViewModel이 프로토콜을 “채택”하는 게 아니라,
프로토콜 타입을 “의존성(Dependency)” 으로 받아야 합니다.


🧠 왜냐면?

이걸 한 문장으로 표현하면 이렇게 돼요 👇

“ViewModel은 네트워크의 세부 구현을 몰라야 하기 때문이에요.”


🔍 구체적으로 살펴보자

✅ 1️⃣ 지금 상황: HappinessService 구조

final class HappinessService {
    func fetchRandomQuote() -> AnyPublisher<HappinessQuote, Error> {
        // 네트워크 요청
    }
}

 

현재 이 클래스는 API 통신이라는 ‘구현’을 담당합니다.
즉, ViewModel 입장에서는 “데이터를 어떻게 가져오는지”는 몰라도 되는 영역이에요.
그냥 “명언을 가져와줘” 라고만 요청하면 됩니다.

 

✅ 2️⃣ 그래서 프로토콜을 “서비스 역할”로 추상화

protocol HappinessServiceProviding {
    func fetchRandomQuote() -> AnyPublisher<HappinessQuote, Error>
}

 

→ 이 프로토콜은 “명언을 가져올 수 있는 객체라면 누구든 사용 가능하다”는 계약(Contract)을 정의한 거예요.

그 다음 실제 구현체(HappinessService)가 이걸 채택합니다.

final class HappinessService: HappinessServiceProviding {
    func fetchRandomQuote() -> AnyPublisher<HappinessQuote, Error> { ... }
}

 

✅ 3️⃣ ViewModel은 프로토콜을 ‘의존’하는 구조로 설계

final class HappinessViewModel: ObservableObject {
    private let service: HappinessServiceProviding

    init(service: HappinessServiceProviding = HappinessService()) {
        self.service = service
    }
}

 

이렇게 하면

 

- ViewModel은 네트워크의 세부 구현을 몰라도 되고,

- 나중에 테스트 시엔 MockHappinessService를 끼워넣을 수 있습니다.

 

즉, “확장 가능하고 테스트 가능한 구조” 완성 🎯


🧠 반대로 “ViewModel에 프로토콜을 채택”하면?

예를 들어 이런 구조를 상상해볼 수 있죠 👇

protocol HappinessProviding {
    func fetchRandomQuote() -> AnyPublisher<HappinessQuote, Error>
}

final class HappinessViewModel: ObservableObject, HappinessProviding {
    ...
}

 

이건 ViewModel이 스스로 데이터를 제공하는 역할을 떠안게 됩니다.

즉, “데이터 요청(서비스)”과 “UI 상태 관리(ViewModel)”가 뒤섞이게 돼요 ❌
→ SRP(단일 책임 원칙, Single Responsibility Principle)를 위반하게 됩니다.

 

ViewModel은 데이터를 직접 가져오는 주체가 아니라, 가져온 데이터를 보여주는 주체여야 해요.
그렇기 때문에, ViewModel이 직접 프로토콜을 채택하면 “역할이 잘못된 추상화”가 되어버립니다.


💡 그럼 “DiaryStore”는 왜 프로토콜을 채택했나?

DiaryStore가 프로토콜(DiaryProviding)을 채택한 이유는
이 구조가 SSOT(Single Source of Truth) 로 작동하기 때문이에요.

 

정리하면 👇

클래스 역할 프로토콜을 채택한 이유
HappinessService 외부 API에서 데이터를 “가져오는 역할” 네트워크 호출을 Mocking / 교체하기 위함
DiaryStore Core Data 기반으로 “데이터를 보관하고 동기화하는 역할” (앱 내 SSOT) 데이터 소스 전체를 인터페이스화해서 MockStore, PreviewStore로 교체 가능하게 하기 위함

 

즉,

 

1. DiaryStore는 앱 내부의 데이터 허브

2. HappinessService는 외부 API 데이터 공급자

 

둘 다 “데이터를 제공하는 역할”이기 때문에,
“프로토콜 채택 = 데이터 공급자 인터페이스 구현” 이라는 점에서는 같지만,
적용 맥락이 다릅니다.


✅ 결론 요약

항목 올바른 방향
프로토콜을 채택해야 하는 곳 데이터 제공자(Service, Store)
프로토콜을 의존해야 하는 곳 ViewModel
ViewModel이 직접 채택하면? SRP(단일 책임 원칙) 위반 ❌
DiaryStore의 경우 내부 데이터 공급자(Single Source of Truth) 역할이라 채택이 합리적 ✅
HappinessService의 경우 외부 API 공급자 역할이라 프로토콜로 추상화 ✅

💬 한 줄 요약

💡 “프로토콜은 ‘역할’을 정의하는 것이지 ‘누가 쓸지’를 정의하는 게 아니다.
데이터 공급자(Service, Store)가 프로토콜을 채택하고,
ViewModel은 그 프로토콜에 의존성 주입을 받는 구조가 가장 깔끔하다.”

 

728x90
LIST