Swift Combine MVVM 예제 프로젝트

작성일 :

Swift Combine MVVM 예제 프로젝트

Swift Combine은 애플의 리액티브 프로그래밍 프레임워크로, 비동기 이벤트 처리와 데이터 스트림 관리를 위한 강력한 도구입니다. MVVM(Model-View-ViewModel) 패턴은 코드의 유지보수성과 테스트 용이성을 높이는 데 도움을 주는 아키텍처 패턴입니다. 이 글에서는 Swift Combine과 MVVM 패턴을 통합하여 예제 프로젝트를 구현하는 방법을 자세히 살펴보겠습니다. 예제 프로젝트는 간단한 네트워크 요청을 통해 데이터를 받아와 사용자 인터페이스에 표시하는 기능을 구현합니다.

프로젝트 개요

이번 예제 프로젝트에서는 JSONPlaceholder API를 사용하여 가상의 사용자 목록을 가져와 SwiftUI를 통해 화면에 표시합니다. 이 프로젝트는 Swift Combine과 MVVM 패턴을 활용하여 데이터를 비동기적으로 가져오고, UI와 데이터를 바인딩하는 방법을 설명합니다.

프로젝트 구조

프로젝트는 다음과 같은 구조로 구성됩니다:

  • Model: 데이터를 표현하는 구조체
  • ViewModel: 비즈니스 로직과 데이터 변환을 담당
  • View: 사용자 인터페이스를 담당

Model

Model은 애플리케이션의 데이터를 표현합니다. 이번 예제에서는 사용자 정보를 나타내는 User 구조체를 정의합니다.

swift
import Foundation

struct User: Codable, Identifiable {
    let id: Int
    let name: String
    let username: String
    let email: String
}

ViewModel

ViewModel은 Model과 View 사이의 중개자로서, Combine을 사용하여 데이터를 관리하고 변환합니다. 또한 네트워크 요청을 통해 데이터를 가져오는 역할도 수행합니다.

swift
import Combine
import Foundation

class UserViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var isLoading: Bool = false
    @Published var errorMessage: String?

    private var cancellables = Set<AnyCancellable>()

    func fetchUsers() {
        isLoading = true
        errorMessage = nil

        let url = URL(string: "https://jsonplaceholder.typicode.com/users")!

        URLSession.shared.dataTaskPublisher(for: url)
            .map { $0.data }
            .decode(type: [User].self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                self.isLoading = false
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    self.errorMessage = error.localizedDescription
                }
            }, receiveValue: { users in
                self.users = users
            })
            .store(in: &cancellables)
    }
}

이 ViewModel에서는 fetchUsers 메서드를 통해 네트워크 요청을 보내고, 응답 데이터를 users 배열에 저장합니다. 또한, 로딩 상태와 오류 메시지를 관리합니다.

View

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

swift
import SwiftUI

struct UserListView: View {
    @ObservedObject var viewModel: UserViewModel

    var body: some View {
        NavigationView {
            VStack {
                if viewModel.isLoading {
                    ProgressView("Loading...")
                } else if let errorMessage = viewModel.errorMessage {
                    Text("Error: \(errorMessage)")
                        .foregroundColor(.red)
                } else {
                    List(viewModel.users) { user in
                        VStack(alignment: .leading) {
                            Text(user.name)
                                .font(.headline)
                            Text(user.email)
                                .font(.subheadline)
                                .foregroundColor(.gray)
                        }
                    }
                }
            }
            .navigationBarTitle("Users")
            .onAppear {
                viewModel.fetchUsers()
            }
        }
    }
}

이 View에서는 UserViewModel을 구독하고, 로딩 상태, 오류 메시지, 사용자 목록을 각각 다른 UI 요소로 표시합니다. onAppear 수명주기 메서드를 사용하여 View가 나타날 때 데이터를 가져오도록 합니다.

앱의 통합

마지막으로, View와 ViewModel을 통합하여 앱을 완성합니다.

swift
import SwiftUI

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

이 코드에서는 CombineMVVMAppbody 속성에서 UserListView를 생성하고, UserViewModel을 전달하여 앱을 시작합니다.

데이터 바인딩

Combine과 MVVM 패턴을 사용하면 데이터 바인딩을 손쉽게 구현할 수 있습니다. ViewModel에서 데이터의 변경을 처리하고, View는 이를 자동으로 반영합니다. 예를 들어, 사용자가 네트워크 요청을 통해 받아온 사용자 목록이 업데이트되면, View는 이를 실시간으로 반영하여 사용자에게 표시합니다.

비동기 작업 처리

Combine을 사용하면 비동기 작업을 간단하게 처리할 수 있습니다. URLSession.shared.dataTaskPublisher를 사용하여 네트워크 요청을 보내고, sink 연산자를 통해 응답 데이터를 처리합니다. receive(on:) 연산자를 사용하여 메인 스레드에서 데이터를 수신하고, UI를 안전하게 업데이트합니다.

오류 처리

비동기 작업에서는 오류 처리가 매우 중요합니다. Combine의 sinkreceiveCompletion 클로저를 통해 오류를 처리할 수 있습니다. 이 예제에서는 네트워크 요청에서 오류가 발생할 경우 오류 메시지를 사용자에게 표시합니다.

로딩 상태 관리

로딩 상태를 관리하여 사용자에게 현재 데이터를 가져오는 중임을 알려줄 수 있습니다. 이 예제에서는 isLoading 플래그를 사용하여 데이터를 가져오는 동안 로딩 인디케이터를 표시하고, 데이터가 로드되면 목록을 표시합니다.

결론

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