네트워크 요청을 처리할 때 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()
콜백 기반 방식이라 코드 가독성이 떨어짐
dataTask는 반복적인 작업이 필요할 때 효율적이지 않음
에러 처리 로직이 분산되면서 유지보수가 어려워짐
해결 방안
Combine
을 활용한 네트워크 요청 최적화
Combine
을 사용하여 네트워크 요청을 깔끔하게 처리map
과decode
를 사용해 데이터를 직관적으로 변환 가능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() } }
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) } }
SwiftUI
에서 사용Combine
과SwiftUI
를 결합하면 네트워크 요청이 더 직관적으로 관리 가능
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() } } }