[iOS] Moya , Alamofire , URLSession 비교

김상우·2022년 5월 3일
6
  • shout out to Ace

개념 리마인드

  • 블로그에 정리했던 글

URLSession : https://velog.io/@heyksw/iOS-URLSession
Alamofire : https://velog.io/@heyksw/iOS-Alamofire
URLSession 토이 플젝 : https://velog.io/@heyksw/아이폰-앱-2-현재-날씨는


  • URLSession
    • iOS 앱에서 서버와 통신하기 위해 Apple 이 제공하는 네트워킹 API
    • 로우 레벨의 코드를 작성할 수 있고, 가장 기본적인 방법
    • Alamofire 의 기반이 되는 API
    • 특정 URL을 사용해서 데이터를 다운로드, 업로드하기 위한 API

  • Alamofire
    • 네트워킹 오픈소스 라이브러리
    • URLSession 에 기반한 라이브러리
    • 코드의 간소화 및 가독성 측면에서 보다 성능이 개선되었다
    • 유지보수와 유닛 테스트가 힘들다는 단점이 있다

  • Moya
    • 네트워킹 오픈소스 프레임워크
    • 추상화 된 정도 : URLSession < Alamofire < Moya
    • Network Layer 를 템플릿화 해서 재사용성을 높인다
    • 열거형 enum 을 사용해서 안전하고 정돈된 방식으로 캡슐화한다

  • 추상화란
    • 객체 지향 프로그래밍의 특징(추상화, 캡슐화, 은닉화, 상속성, 다형성) 중 하나
    • 객체들의 공통적인 부분을 뽑아내서 따로 구현해놓은 것
    • 공통적인 부분들만 추려놓았기 때문에 구체적인 정보를 담아두지 못했고, 추상적인 정보가 모였기 때문에 '추상화'라고 부른다

같은 예제를 통한 3개 비교

  • 날씨 데이터를 가져와 사용하는 코드를 3가지 방법으로 작성해보려 한다.
    1. URLSession
    2. Alamofire
    3. Moya

날씨 데이터 API : https://openweathermap.org/api

-> 도시 이름을 입력하면, 현재 그 도시의 날씨를 출력해주는 앱


-> PostMan 으로 확인한 json 데이터의 모습


1. URLSession 으로 코드 작성

  • URLSession LifeCycle


  • ViewController.swift

// API에서 현재 날씨 가져오기
func getCurrentWeatherByURLSession(cityName: String) {
    // 날씨 데이서 API url 연결
    guard let url = URL(string: url_seoul) else { return }
    
    // 1. Session Configuration 설정 + 생성
    let session = URLSession(configuration: .default)
    
    // 2,3. 사용할 Task 결정하고 Complete Handler 작성
    session.dataTask(with: url) { [weak self]
        // response : http 헤더 및 상태 코드 메타 데이터
        // error : 요청 실패 에러 객체. 성공하면 nil.
        data, response, error in
        // http status 가 200번대면 성공
        let successRange = (200..<300)
        guard let data = data, error == nil else { return }
        
        let decoder = JSONDecoder()
        
        // response 를 HTTPURLResponse 형태로 다운 캐스팅
        if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
            guard let weatherInformation = try? decoder.decode(WeatherInformation.self, from: data) else { return }
            
            // UI 작업은 메인 쓰레드에서 작업
            DispatchQueue.main.async {
                self?.weatherStackView.isHidden = false
                self?.configureView(weatherInformation: weatherInformation)
            }
        }
        else {
            guard let errorMessage = try? decoder.decode(ErrorMessage.self, from: data) else { return }
            
            DispatchQueue.main.async {
                self?.showAlert(message: errorMessage.message)
            }
        }
    }.resume()	// 4. 해당 Task 를 실행
    
}

2. Alamofire 로 코드 작성

  • ViewController.swift
import Alamofire

...

func getCurrentWeahterByAlamofire(cityName: String) {
    guard let url = URL(string: url_seoul) else { return }
    
    AF.request(url, method: .get, parameters: [:])
        .responseData(completionHandler: { response in
            let decoder = JSONDecoder()
            switch response.result {
            case let .success(data):
                do {
                    let weatherInformation = try decoder.decode(WeatherInformation.self, from: data)
                    
                    DispatchQueue.main.async {
                        self.weatherStackView.isHidden = false
                        self.configureView(weatherInformation: weatherInformation)
                    }
                } catch {
                    debugPrint(error)
                }
            case let .failure(error):
                debugPrint(error)
            }
        })
}
  • URLSession 에서 해줘야 했던 HTTPURLResponse 타입 캐스팅 과정이 생략 되었다
  • 코드 메타 데이터로 성공여부를 파악하지 않아도 된다

3. Moya 로 코드 작성

  • Moya 사용 플로우
    • MoyaProvider : API 의 request 를 처리해주는 관리자
    • TargetType: MoyaProvider 에서 필요한 규격을 정의하는 프로토콜.
      6개의 프로퍼티를 기입해줘야 한다.

  • WeatherAPI.swift - 규격 세팅
import Foundation
import Moya

enum WeatherAPI {
    case getWeather
    // case setWeather
}

// TargetType : MoyaProvider에 필요한 규격을 정의하는데 사용되는 프로토콜
extension WeatherAPI: TargetType {
    // 서버의 base URL, end point 도메인
    var baseURL: URL {
        return URL(string: "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid=1290787ef5eb584060b11b43e201a9fc")!
    }
    
    // 도메인 뒤에 추가 될 path (/users, /documents, ...)
    var path: String {
        switch self {
        case .getWeather:
            return ""
        }
    }
    
    // HTTP 메소드 (GET, POST, ...)
    var method: Moya.Method {
        switch self {
        case .getWeather:
            return .get
        }
  		//case .setWeather:
  		//	return .post
    }
    
    // 테스트용 Mock Data
    var sampleData: Data {
        return Data()
    }
    
    // request에 사용될 파라미터
    // .requestPlain : 파라미터 없을 때
    // .requestParameters(parameter: ,encoding: )
    var task: Task {
        switch self {
        case .getWeather:
            return .requestPlain
        }
    }
    
    // HTTP header 에 대한 배경지식 필요
    var headers: [String : String]? {
        return ["Content-type": "application/json"]
    }
}

  • ViewController.swift
import Moya

...

func getCurrentWeatherByMoya(cityName: String) {
  
    let moyaProvider = MoyaProvider<WeatherAPI>()
    moyaProvider.request(.getWeather) { (result) in
        switch result {
        case let .success(response):
            guard let result = try? response.map(WeatherInformation.self) else { return }
            DispatchQueue.main.async {
                self.weatherStackView.isHidden = false
                self.configureView(weatherInformation: result)
            }
        case let .failure(error):
            print(error.localizedDescription)
        }
  
    }  
}

Moya 느낀점

  1. url 주소값과 request 들을 외부에서 정의한다.
  1. 데이터를 사용하는 곳에서 좀 더 request / response 에만 집중한 코드를 작성할 수 있다.
  1. 한 API 내에서 코드를 재사용하기가 좋다.
    • Alamofire : 일일히 request 하고 메서드를 작성
    • Moya : TargetType 에서 HTTP 메서드를 여러 개 작성해서 재활용 할 수 있다 (열거형)

Reference

https://github.com/Moya
https://roniruny.tistory.com/150

profile
안녕하세요, iOS 와 알고리즘에 대한 글을 씁니다.

1개의 댓글

comment-user-thumbnail
2023년 7월 20일

안녕하세요! 포스팅 잘 보고있습니다 :)
혹시 Alamofire가 어떤 면에서 유지보수랑 단위테스트가 어려운 것인지 구체적인 이유를 여쭤봐도 될까요? 저도 둘다 써봤는데도 확 다가오지를 않아서 여쭤봅니당

답글 달기