[iOS] URLSession & Combine을 활용한 네트워크 요청 최적화

iosSwift
avatar
2025.02.19
·
3 min read

네트워크 요청을 처리할 때 URLSession을 기본적으로 사용하지만, 비동기 작업을 더 효율적으로 관리하기 위해 Combine 프레임워크를 활용할 수 있다.

이 글에서는 URLSession을 활용한 네트워크 요청 최적화 방법을 정리한다.


기존 URLSession 방식의 문제점

let task = URLSession.shared.dataTask(with: url) { data, response, error in
    guard let data = data, error == nil else {
        print("Error: \(error?.localizedDescription ?? "Unknown error")")
        return
    }

    do {
        let decodedData = try JSONDecoder().decode(User.self, from: data)
        print("User: \(decodedData)")
    } catch {
        print("Decoding error: \(error)")
    }
}
task.resume()
  1. 콜백 기반 방식이라 코드 가독성이 떨어짐

  2. dataTask는 반복적인 작업이 필요할 때 효율적이지 않음

  3. 에러 처리 로직이 분산되면서 유지보수가 어려워짐

해결 방안

Combine을 활용한 네트워크 요청 최적화

  1. Combine을 사용하여 네트워크 요청을 깔끔하게 처리

    • mapdecode를 사용해 데이터를 직관적으로 변환 가능

    • receive(on: DispatchQueue.main)으로 UI 업데이트를 쉽게 처

    • eraseToAnyPublisher()를 통해 타입 정보를 숨겨 코드 간결화

    import Combine
    import Foundation
    
    struct APIManager {
        static let shared = APIManager()
        
        func fetchUser() -> AnyPublisher<User, Error> {
            let url = URL(string: "https://api.example.com/user")!
            
            return URLSession.shared.dataTaskPublisher(for: url)
                .map { $0.data }  // 응답에서 data 추출
                .decode(type: User.self, decoder: JSONDecoder())  // JSON 디코딩
                .receive(on: DispatchQueue.main)  // UI 업데이트는 메인 스레드에서
                .eraseToAnyPublisher()
        }
    }
  2. ViewModel에서 데이터 요청

    • sink를 사용해 네트워크 요청을 구독(Subscribe)

    • @Published 변수를 사용해 View가 자동으로 업데이트

    • cancellables를 활용해 메모리 관리

    class UserViewModel: ObservableObject {
        @Published var user: User?
        private var cancellables = Set<AnyCancellable>()
    
        func loadUser() {
            APIManager.shared.fetchUser()
                .sink(receiveCompletion: { completion in
                    switch completion {
                    case .failure(let error):
                        print("Error: \(error.localizedDescription)")
                    case .finished:
                        break
                    }
                }, receiveValue: { [weak self] user in
                    self?.user = user
                })
                .store(in: &cancellables)
        }
    }
  3. SwiftUI에서 사용

    • CombineSwiftUI를 결합하면 네트워크 요청이 더 직관적으로 관리 가능

    struct ContentView: View {
        @StateObject var viewModel = UserViewModel()
    
        var body: some View {
            VStack {
                if let user = viewModel.user {
                    Text("Hello, \(user.name)!")
                } else {
                    Text("Loading...")
                }
            }
            .onAppear {
                viewModel.loadUser()
            }
        }
    }






- 컬렉션 아티클