SwiftUI에서 네트워크 통신 처리: URLSession 활용

작성일 :

SwiftUI에서 네트워크 통신 처리: URLSession 활용

SwiftUI는 사용자 인터페이스(UI)를 빠르고 쉽고 직관적으로 개발할 수 있게 도와주는 프레임워크입니다. 그러나 실용성 있는 애플리케이션을 만들기 위해서는 네트워크를 통해 데이터를 가져오는 것이 필수적입니다. 이 글에서는 SwiftUI에서 URLSession을 사용하여 네트워크 통신을 처리하는 방법을 다룰 것입니다. 우리는 URLSession을 통해 데이터를 가져오고, 이 데이터를 SwiftUI 뷰에 반영하는 방법을 단계별로 알아보겠습니다.

URLSession 소개

URLSession은 URL을 통해 데이터를 전송하고 수신하는 작업을 처리하는 Apple의 프레임워크입니다. iOS, macOS, watchOS 및 tvOS에서 네트워크 데이터를 가져오거나 업로드하는 작업을 처리합니다. 이 프레임워크는 다양한 설정 옵션과 함께 두 가지 주된 방식, 즉 동기와 비동기 작업을 지원합니다. 여기서는 비동기 작업을 주로 다루겠습니다.

기본 설정

먼저 SwiftUI 프로젝트를 생성하고 필요한 구조를 설정해야 합니다. Xcode를 열고 새로운 SwiftUI 프로젝트를 생성합니다. 다음으로는 네트워크 요청을 처리할 모델을 생성해야 합니다.

swift
import SwiftUI
import Combine

struct APIResponse: Codable {
    let title: String
    // 필요한 다른 필드들을 여기에 추가합니다.
}

위의 코드는 APIResponse라는 구조를 정의합니다. 이 구조는 JSON 응답을 Swift 객체로 디코딩하는 데 사용됩니다.

ViewModel 생성

SwiftUI는 MVVM(모델-뷰-뷰모델) 아키텍처를 권장합니다. 따라서 데이터를 관리하고 네트워크 요청을 처리하는 ViewModel을 생성해야 합니다.

swift
class NetworkManager: ObservableObject {
    @Published var apiResponse: [APIResponse] = []
    private var cancellables = Set<AnyCancellable>()

    func fetchData() {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }

        URLSession.shared.dataTaskPublisher(for: url)
            .map { $0.data }
            .decode(type: [APIResponse].self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    print("Error: \(error)")
                case .finished:
                    break
                }
            }, receiveValue: { [weak self] response in
                self?.apiResponse = response
            })
            .store(in: &cancellables)
    }
}

위 코드에서 NetworkManager 클래스는 네트워크 요청을 관리하며, 요청이 완료되면 데이터를 apiResponse 배열에 저장합니다. ObservableObject를 준수하여 SwiftUI 뷰가 데이터 변경을 감지할 수 있도록 합니다.

SwiftUI View에 통합

이제 ViewModel을 SwiftUI 뷰와 통합합니다. @ObservedObject를 사용하여 ViewModel을 뷰에 주입하고 데이터를 표시합니다.

swift
struct ContentView: View {
    @ObservedObject var networkManager = NetworkManager()

    var body: some View {
        NavigationView {
            List(networkManager.apiResponse, id: \ .title) { item in
                VStack(alignment: .leading) {
                    Text(item.title)
                        .font(.headline)
                    // 여기에 다른 필드들을 추가하세요.
                }
            }
            .navigationBarTitle("Posts")
        }
        .onAppear {
            self.networkManager.fetchData()
        }
    }
}

ContentView에서는 networkManager 인스턴스를 사용하여 데이터를 관리합니다. 사용자가 이 뷰를 볼 때마다 fetchData 메서드가 호출되어 데이터를 로드합니다.

에러 처리

네트워크 요청은 항상 성공하지 않을 수 있습니다. 따라서 실제 애플리케이션에서는 오류 상황을 처리해야 합니다. 다음은 간단한 오류 메시지를 추가하는 방법입니다.

swift
class NetworkManager: ObservableObject {
    @Published var apiResponse: [APIResponse] = []
    @Published var errorMessage: String? = nil
    private var cancellables = Set<AnyCancellable>()

    func fetchData() {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { 
            self.errorMessage = "Invalid URL"
            return 
        }

        URLSession.shared.dataTaskPublisher(for: url)
            .map { $0.data }
            .decode(type: [APIResponse].self, decoder: JSONDecoder())
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    self.errorMessage = "Failed to load data: \(error.localizedDescription)"
                case .finished:
                    break
                }
            }, receiveValue: { [weak self] response in
                self?.apiResponse = response
            })
            .store(in: &cancellables)
    }
}

추가된 errorMessageContentView에서 표시할 수 있습니다.

swift
struct ContentView: View {
    @ObservedObject var networkManager = NetworkManager()

    var body: some View {
        NavigationView {
            VStack {
                if let errorMessage = networkManager.errorMessage {
                    Text(errorMessage)
                        .foregroundColor(.red)
                }
                List(networkManager.apiResponse, id: .title) { item in
                    VStack(alignment: .leading) {
                        Text(item.title)
                            .font(.headline)
                        // 여기에 다른 필드들을 추가하세요.
                    }
                }
                .navigationBarTitle("Posts")
            }
        }
        .onAppear {
            self.networkManager.fetchData()
        }
    }
}

이제 네트워크 요청이 실패하면 뷰에 오류 메시지가 표시될 것입니다.

결론

SwiftUI에서 URLSession을 사용하여 네트워크 통신을 처리하는 것은 상대적으로 간단합니다. Combine 프레임워크와 함께 사용하면 비동기 데이터를 쉽게 관리할 수 있습니다. 위의 예제는 단순한 GET 요청이지만, POST, PUT, DELETE 등 다양한 HTTP 메서드를 지원하며, 복잡한 네트워크 통신을 처리하는 데에도 유용합니다. 이 간단한 예제를 통해 네트워크 요청과 응답을 효과적으로 관리하며 SwiftUI 애플리케이션에 통합할 수 있을 것입니다.