iOS

[iOS] MVVM 패턴에서 데이터 로딩 책임 분리

양밀루 2025. 6. 18. 20:05

✅ 문제 상황

json 파일에 있는 데이터를 파싱해와서 보여주는 과제를 하던 중 ViewController의 viewDidLoad()에서 데이터 로딩 로직을 처리하고 있는 것이 MVVM 패턴에 적합하지 않다고 느꼈다.

기존 코드 (문제가 있던 코드)

// BookViewController.swift
override func viewDidLoad() {
    super.viewDidLoad()
    
    setupViews()
    setupConstraints()
    
    // 🚨 문제: ViewController에서 데이터 로딩 제어
    viewModel.loadBooks { [weak self] result in
        switch result {
        case .success:
            self?.updateUI()
        case .failure(let error):
            self?.showErrorAlert(error: error)
        }
    }
}

 

✅ 문제 분석

1. MVVM 패턴 위반

  • ViewController (View Layer): UI만 담당해야 함
  • ViewModel (Presentation Layer): 데이터 로딩과 비즈니스 로직 담당해야 함

2. 책임 분리 원칙 위반

  • Single Responsibility Principle (SRP) 위반
  • ViewController가 UI 관리 + 데이터 로딩 제어를 동시에 담당

3. 아키텍처 일관성 문제

  • View Layer가 Presentation Logic을 처리

 

💡 해결 방법

// BookViewModel.swift - 개선된 코드
final class BookViewModel {
    private let bookRepository: BookRepository
    private var books: [Book] = []
    private var selectedBookIndex: Int = 0
    private(set) var error: DataServiceError?
    
    // MARK: - Closures
    var onDataLoaded: (() -> Void)?
    var onError: ((DataServiceError) -> Void)?
    
    // ✅ ViewModel이 스스로 상태를 관리하고 외부에 알림
    func loadBooks() {
        let result = bookRepository.loadBooks()
        
        switch result {
        case .success(let books):
            self.books = books
            DispatchQueue.main.async { [weak self] in
                self?.onDataLoaded?()
            }
        case .failure(let error):
            self.error = error
            DispatchQueue.main.async { [weak self] in
                self?.onError?(error)
            }
        }
    }
    
   ...
}
// BookViewController.swift - 개선된 코드
override func viewDidLoad() {
    super.viewDidLoad()
    setupViews()
    setupConstraints()
    setupBindings()  // ✅ 단순히 바인딩만
    
    viewModel.loadBooks()
}

private func setupBindings() {
    viewModel.onDataLoaded = { [weak self] in
        self?.updateUI()
    }
    
    viewModel.onError = { [weak self] error in
        self?.showErrorAlert(error: error)
    }
}

private func updateUI() {
    ...
}

private func showErrorAlert(error: DataServiceError) {
    ...
}

 

이렇게 ViewController는 UI 관리만

ViewModel은 데이터 변환, 로딩 로직 관리, 상태 관리를 할 수 있게 되었다!

 

+ 추후에 RxSwift나 Combine으로 해결해볼 예정이다