Swift Combine과 MVVM 패턴의 통합

작성일 :

Swift Combine과 MVVM 패턴의 통합

Swift Combine은 애플의 리액티브 프로그래밍 프레임워크로, 비동기 이벤트 처리와 데이터 스트림 관리에 탁월한 성능을 발휘합니다. MVVM(Model-View-ViewModel) 패턴은 애플리케이션의 구조를 효율적으로 설계하는 데 유용한 아키텍처 패턴입니다. 이 글에서는 Swift Combine과 MVVM 패턴을 통합하여 애플리케이션을 개발하는 방법을 살펴보겠습니다.

MVVM 패턴이란?

MVVM 패턴은 애플리케이션의 코드베이스를 분리하여 유지보수성과 테스트 용이성을 높이는 데 도움을 주는 디자인 패턴입니다. MVVM 패턴은 다음 세 가지 주요 구성 요소로 이루어져 있습니다:

  • Model: 애플리케이션의 데이터와 비즈니스 로직을 담당합니다.
  • View: 사용자 인터페이스(UI)를 담당합니다.
  • ViewModel: Model과 View 사이의 중개자로서, 데이터를 변환하여 View에 제공하고, 사용자의 입력을 Model로 전달합니다.

MVVM 패턴을 사용하면 코드의 재사용성을 높이고, 각 구성 요소를 독립적으로 테스트할 수 있습니다.

Swift Combine과 MVVM 패턴의 통합

Swift Combine은 MVVM 패턴과 잘 어울리는 프레임워크입니다. Combine을 사용하면 ViewModel에서 비동기 데이터를 손쉽게 관리하고, View와의 데이터 바인딩을 간편하게 구현할 수 있습니다.

예제 애플리케이션

간단한 예제 애플리케이션을 통해 Swift Combine과 MVVM 패턴의 통합을 살펴보겠습니다. 이 예제에서는 사용자가 입력한 텍스트를 실시간으로 반영하여 표시하는 기능을 구현합니다.

Model

Model은 애플리케이션의 데이터와 비즈니스 로직을 담당합니다. 이 예제에서는 단순히 사용자의 입력을 저장하는 역할을 합니다.

swift
import Foundation

struct UserInput {
    var text: String
}

ViewModel

ViewModel은 Model과 View 사이의 중개자로서, Combine을 사용하여 데이터를 관리하고 변환합니다.

swift
import Combine
import Foundation

class UserInputViewModel: ObservableObject {
    @Published var userInput: UserInput = UserInput(text: "")
    @Published var displayText: String = ""

    private var cancellables = Set<AnyCancellable>()

    init() {
        $userInput
            .map { $0.text }
            .assign(to: \.displayText, on: self)
            .store(in: &cancellables)
    }

    func updateUserInput(text: String) {
        userInput.text = text
    }
}

이 ViewModel은 @Published 속성을 사용하여 userInputdisplayText를 선언하고, userInputtext 값이 변경될 때마다 displayText를 업데이트합니다. @Published는 Combine에서 제공하는 속성 래퍼로, 값의 변경을 구독자에게 자동으로 알립니다.

View

View는 사용자 인터페이스를 담당하며, SwiftUI를 사용하여 구현합니다. View는 ViewModel을 구독하여 데이터가 변경될 때마다 UI를 업데이트합니다.

swift
import SwiftUI

struct UserInputView: View {
    @ObservedObject var viewModel: UserInputViewModel

    var body: some View {
        VStack {
            TextField("Enter text", text: Binding(
                get: { self.viewModel.userInput.text },
                set: { self.viewModel.updateUserInput(text: $0) }
            ))
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .padding()

            Text(viewModel.displayText)
                .padding()
        }
    }
}

이 View는 @ObservedObject 속성을 사용하여 ViewModel을 구독하고, TextFieldText를 사용하여 사용자의 입력과 반영된 텍스트를 표시합니다. TextField는 Binding을 사용하여 ViewModel의 userInput.text와 바인딩합니다.

애플리케이션의 통합

마지막으로, ViewModel과 View를 통합하여 애플리케이션을 완성합니다.

swift
import SwiftUI

@main
struct CombineMVVMApp: App {
    var body: some Scene {
        WindowGroup {
            UserInputView(viewModel: UserInputViewModel())
        }
    }
}

이 코드에서는 CombineMVVMAppbody 속성에서 UserInputView를 생성하고, UserInputViewModel을 전달하여 애플리케이션을 시작합니다.

데이터 바인딩

Combine과 MVVM 패턴을 사용하면 데이터 바인딩을 손쉽게 구현할 수 있습니다. ViewModel에서 데이터의 변경을 처리하고, View는 이를 자동으로 반영합니다. 예를 들어, 사용자가 TextField에 텍스트를 입력하면, ViewModel의 userInput.text가 업데이트되고, 이는 다시 displayText에 반영되어 View가 자동으로 업데이트됩니다.

비동기 작업 처리

Combine을 사용하면 비동기 작업을 간단하게 처리할 수 있습니다. 예를 들어, 네트워크 요청을 통해 데이터를 가져오는 작업을 ViewModel에서 처리할 수 있습니다.

swift
import Combine
import Foundation

class UserInputViewModel: ObservableObject {
    @Published var userInput: UserInput = UserInput(text: "")
    @Published var displayText: String = ""

    private var cancellables = Set<AnyCancellable>()

    init() {
        $userInput
            .map { $0.text }
            .assign(to: \.displayText, on: self)
            .store(in: &cancellables)
    }

    func updateUserInput(text: String) {
        userInput.text = text
    }

    func fetchRemoteData() {
        let url = URL(string: "https://api.example.com/data")!
        URLSession.shared.dataTaskPublisher(for: url)
            .map { $0.data }
            .decode(type: UserInput.self, decoder: JSONDecoder())
            .replaceError(with: UserInput(text: "Error fetching data"))
            .receive(on: DispatchQueue.main)
            .assign(to: \.userInput, on: self)
            .store(in: &cancellables)
    }
}

이 예제에서는 fetchRemoteData 메서드를 사용하여 원격 데이터를 가져오고, 이를 userInput에 반영합니다. receive(on:) 연산자를 사용하여 메인 스레드에서 데이터를 수신하고, UI를 안전하게 업데이트합니다.

결론

Swift Combine과 MVVM 패턴을 통합하면 비동기 데이터를 효율적으로 관리하고, View와의 데이터 바인딩을 간편하게 구현할 수 있습니다. Combine의 강력한 기능을 활용하여 비동기 작업을 손쉽게 처리하고, MVVM 패턴을 통해 코드의 재사용성과 유지보수성을 높일 수 있습니다. 이 글에서 소개한 예제와 원칙을 따라, 더욱 효율적이고 직관적인 애플리케이션을 개발해 보세요.