• Feed
  • Explore
  • Ranking
/
/
    📱 iOS

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

    iosSwift
    지
    지성
    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을 사용하여 네트워크 요청을 깔끔하게 처리

      • 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()
          }
      }
    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에서 사용

      • 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()
              }
          }
      }






    - 컬렉션 아티클