티스토리 뷰

iOS 개발을 하다 보면 ViewController가 너무 비대해지는 문제를 자주 마주하게 된다.
네트워크 요청, UI 로직, 데이터 변환 등이 한 곳에 몰리면서 코드가 복잡해지고 유지보수가 어려워지는 문제가 발생한다.

이 문제를 해결하는 방법 중 하나가 바로 MVVM(Model-View-ViewModel) 패턴이다!
MVVM을 활용하면 코드를 더 구조적으로 분리할 수 있고, 테스트가 쉬워지며, UI 로직을 깔끔하게 정리할 수 있다.

이번 포스팅에서는 MVVM 패턴의 개념, 역할별 구조, 그리고 적용 방법까지 정리해보자.


1️⃣ MVVM이란?

✅ MVVM의 기본 개념

MVVM은 Model-View-ViewModel의 약자로, iOS 개발에서 ViewController의 책임을 줄이고 역할을 분리하기 위해 사용되는 패턴이다.

컴포넌트 
역할
Model 데이터 및 비즈니스 로직을 담당
View 사용자 인터페이스(UI) 및 이벤트 처리
ViewModel View와 Model 사이에서 데이터 변환 및 UI 업데이트를 처리

📌 MVVM의 핵심 목표:

  • ViewController의 역할을 최소화하고, UI 로직을 ViewModel에서 처리
  • UI(View)와 비즈니스 로직(Model)을 분리하여 코드의 재사용성을 높임
  • 데이터 바인딩(Data Binding) 기술을 활용해 View와 ViewModel을 쉽게 연결

이제 MVVM이 왜 필요한지 예제와 함께 살펴보자!


2️⃣ MVVM이 필요한 이유 (MVC의 문제점)

 기존 MVC 패턴의 문제점

전통적인 iOS 개발에서는 MVC(Model-View-Controller) 패턴을 사용한다.

하지만 MVC의 가장 큰 문제는 ViewController가 너무 많은 역할을 담당하게 된다는 점이다.
이를 흔히 "Massive ViewController" 문제라고 한다.

class UserViewController: UIViewController {
    let networkService = NetworkService()
    var users: [User] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        fetchUsers()
    }

    func fetchUsers() {
        networkService.getUsers { [weak self] users in
            self?.users = users
            self?.updateUI()
        }
    }

    func updateUI() {
        // UI 업데이트 코드
    }
}

문제점:

  • ViewController가 네트워크 요청, 데이터 변환, UI 업데이트까지 모두 담당
  • 테스트가 어렵다 → ViewController를 유닛 테스트하려면 네트워크 로직도 함께 테스트해야 함
  • 재사용성이 낮다 → 다른 화면에서도 유사한 로직이 필요하면 중복 코드가 발생

이 문제를 해결하는 방법이 바로 MVVM 패턴이다!


3️⃣ MVVM의 역할과 구조

MVVM을 적용하면 코드 구조가 이렇게 변경된다.

1️⃣ Model → 데이터를 나타내는 구조체 또는 클래스로, 네트워크나 데이터베이스에서 가져온 데이터를 저장
2️⃣ ViewModel → Model을 가공하여 View에 전달, UI 로직을 처리
3️⃣ View(ViewController) → ViewModel에서 제공하는 데이터를 UI에 표시

[ViewController] <------> [ViewModel] <------> [Model]

 


4️⃣ MVVM 적용하기

✅ 1. Model 만들기

Model은 데이터 구조를 정의하고, 네트워크나 데이터베이스에서 가져온 데이터를 저장하는 역할을 한다.

struct User {
    let id: Int
    let name: String
}

 

✅ 2. ViewModel 만들기

ViewModel은 데이터를 가공하고 UI 로직을 처리하는 역할을 한다.
View에서 직접 Model을 다루지 않고, ViewModel을 통해 필요한 데이터를 전달받도록 한다.

class UserViewModel {
    private let networkService = NetworkService()
    private(set) var users: [User] = []

    var onUsersUpdated: (() -> Void)?

    func fetchUsers() {
        networkService.getUsers { [weak self] users in
            self?.users = users
            self?.onUsersUpdated?()
        }
    }
}

 

📌 핵심 포인트
✅ ViewModel은 Model을 직접 노출하지 않고, 필요한 데이터를 가공해서 제공
✅ onUsersUpdated 클로저를 통해 데이터가 변경되었을 때 UI를 업데이트하도록 함
✅ 네트워크 로직을 ViewController에서 분리하여 코드가 더 깔끔해짐

✅ 3. View (ViewController)

View는 UI만 담당하고, ViewModel을 통해 데이터를 받아서 UI를 업데이트한다.

class UserViewController: UIViewController {
    private let viewModel = UserViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.onUsersUpdated = { [weak self] in
            self?.updateUI()
        }

        viewModel.fetchUsers()
    }

    func updateUI() {
        print("UI 업데이트: \(viewModel.users)")
    }
}

 

📌 핵심 포인트
✅ ViewController는 오직 UI만 담당하며, 데이터 처리는 ViewModel에 위임
✅ ViewModel에서 데이터를 받아 updateUI()를 호출해 화면을 갱신
ViewController는 가볍게 유지되어 유지보수와 테스트가 쉬워짐


5️⃣ MVVM + Combine 활용하기 (데이터 바인딩)

MVVM의 강력한 장점 중 하나는 데이터 바인딩을 활용할 수 있다는 점이다.
Combine을 사용하면 ViewModel에서 변경된 데이터를 자동으로 UI에 반영할 수 있다.

import Combine

class UserViewModel {
    private let networkService = NetworkService()
    @Published private(set) var users: [User] = []

    func fetchUsers() {
        networkService.getUsers { [weak self] users in
            self?.users = users
        }
    }
}
class UserViewController: UIViewController {
    private let viewModel = UserViewModel()
    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.$users
            .sink { [weak self] _ in
                self?.updateUI()
            }
            .store(in: &cancellables)

        viewModel.fetchUsers()
    }
}

데이터가 변경될 때마다 UI가 자동으로 업데이트됨!


정리

MVVM은 ViewController의 역할을 줄이고, UI와 비즈니스 로직을 분리하는 패턴
Model → 데이터를 저장하고 관리
ViewModel → 데이터를 가공하고 UI 로직을 처리
View(ViewController) → ViewModel에서 제공하는 데이터를 UI에 표시
MVVM을 사용하면 유지보수가 쉬워지고, 유닛 테스트가 용이해짐
✅ RxSwift, Combine을 활용하면 데이터 바인딩을 통해 UI 업데이트를 자동화할 수 있음

 

이제 MVVM의 개념은 이해했지만, 진짜 중요한 것은 직접 적용해보는 것이다.
이 패턴을 도입하면 프로젝트 구조가 더 깔끔해지고, 유지보수가 쉬워지는 것을 체감할 수 있다.

다음 단계로, 현재 진행 중인 프로젝트에서 ViewController의 역할이 과도한 부분을 찾아보자.
그리고 해당 로직을 ViewModel로 분리해보는 것부터 시작하면 자연스럽게 MVVM 패턴을 익힐 수 있을 것이다.

MVVM을 적용하면 더 유연하고 테스트하기 쉬운 iOS 앱을 만들 수 있다.
기존 프로젝트에서 실험해보고, 변화를 직접 경험해보는게 가장 좋은것 같다!

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함