iOS 아키텍처 면접에서 단골로 나오는 질문이 있습니다.
“MVP와 MVVM의 차이가 뭔가요?”
둘 다 뷰컨트롤러를 다이어트시키는 패턴이고 둘 다 가운데에 중간 객체(Presenter/ViewModel)를 둡니다. 구조도만 보면 상자 이름 하나 바뀐 수준이라 답하기가 은근히 까다로워요.
오늘은 이 둘의 차이를 딱 하나의 기준으로 정리해보겠습니다. **“중간 객체가 View를 아는가, 모르는가”**입니다.
MVP: Presenter가 View에게 명령합니다
MVP(Model-View-Presenter)에서 Presenter는 View를 알고 있습니다. 정확히는 View가 구현한 프로토콜(인터페이스)을 참조해요.
protocol ProfileViewProtocol: AnyObject {
func showName(_ name: String)
func showLoading(_ isLoading: Bool)
}
final class ProfilePresenter {
weak var view: ProfileViewProtocol?
func load() {
view?.showLoading(true)
// ...데이터 로드 후
view?.showName("홍길동")
view?.showLoading(false)
}
}
흐름이 보이시나요? Presenter가 데이터를 가공한 뒤 view에게 직접 “이걸 표시해”라고 명령합니다. View(뷰컨트롤러)는 프로토콜 메서드를 구현해두고 시키는 대로만 그리면 돼요.
- 장점: 흐름이 명시적이라 따라가기 쉽고 바인딩 프레임워크가 전혀 필요 없습니다
- 단점: 화면 요소가 늘어날수록 프로토콜 메서드가 계속 불어납니다.
showName,showAge,showBadge…
MVVM: ViewModel은 View의 존재를 모릅니다
반면 MVVM(Model-View-ViewModel)의 ViewModel은 View를 참조하지 않습니다. 그냥 상태를 들고 있을 뿐이에요.
final class ProfileViewModel {
@Published private(set) var displayName = ""
@Published private(set) var isLoading = false
func load() {
isLoading = true
// ...데이터 로드 후
displayName = "홍길동"
isLoading = false
}
}
view?.showName(...) 같은 호출이 없습니다. ViewModel은 자기 상태만 갱신하고 View 쪽에서 그 상태를 구독(바인딩)해서 알아서 그립니다. 명령의 방향이 뒤집힌 거예요.
- MVP: Presenter → View로 밀어넣기(push)
- MVVM: View가 ViewModel을 구독해서 끌어오기(observe)
이 차이 때문에 MVVM은 바인딩 수단(Combine, @Observable 등)이 사실상 필수입니다. 바인딩 없는 MVVM이 반쪽짜리인 이유는 지난 편에서 자세히 다뤘어요.
테스트 코드를 보면 차이가 선명해집니다
두 패턴 모두 “화면 없이 로직을 테스트한다”가 목표인데, 테스트 방식이 다릅니다.
MVP 테스트는 가짜 View를 만들어서 호출을 기록해야 합니다.
final class MockView: ProfileViewProtocol {
var shownName: String?
func showName(_ name: String) { shownName = name }
func showLoading(_ isLoading: Bool) {}
}
// presenter.load() 후 mockView.shownName 검증
MVVM 테스트는 mock 없이 상태 값만 확인하면 끝입니다.
let vm = ProfileViewModel()
vm.load()
#expect(vm.displayName == "홍길동")
프로토콜도, mock 객체도 필요 없어요. ViewModel이 View를 모르기 때문에 생기는 실질적인 이득입니다.
그럼 뭘 써야 할까요?
기준은 의외로 단순합니다. 바인딩 수단이 자연스럽게 있느냐예요.
- SwiftUI, 또는 Combine을 쓰는 UIKit → MVVM이 자연스럽습니다. 바인딩이 공짜니까요.
- 바인딩 도입이 부담스러운 레거시 UIKit → MVP가 오히려 실용적입니다. 프로토콜만으로 굴러가고 흐름이 명시적이라 온보딩도 쉬워요.
요즘 iOS에서 MVVM이 사실상 표준처럼 자리 잡은 건 MVP보다 우월해서라기보다, Combine과 @Observable이 프레임워크 차원에서 제공되면서 MVVM의 유일한 진입 장벽(바인딩)이 사라졌기 때문에 가깝습니다.
정리
- MVP와 MVVM의 본질적 차이는 하나입니다. Presenter는 View(프로토콜)를 알고 직접 명령하고 ViewModel은 View를 모른 채 상태만 노출합니다.
- 그래서 MVP는 바인딩이 필요 없고 MVVM은 바인딩이 필수입니다.
- 테스트에서 MVP는 mock View가 필요하고 MVVM은 상태 검증만으로 끝납니다.
- 바인딩이 공짜인 환경(SwiftUI·Combine)이면 MVVM, 아니면 MVP도 충분히 좋은 선택입니다.
다음 편에서는 여기서 한 발 더 나간 모듈 분리의 극단, VIPER를 다룹니다. 대형 앱들이 왜 도입했고 왜 떠났는지까지 정리해볼게요.
