[ swift ] Moya 사용기

sonny·2024년 12월 22일
3

TIL

목록 보기
79/133

Moya는 네트워크 레이어를 캡슐화하고, 코드 가독성을 높이는 데 유용한 라이브러리다.

API 요청을 명확하게 정의해주고, 코드의 재사용성과 유지보수성을 높일 수 있다는 설명을 보고

알라모파이어보다 모야를 써보면 어떨까 싶어서 연습해보았다.

Moya를 사용하여 어제 적었던 API코드(URLSession)의 동일한 기능을 변경해보자!


Moya를 사용한 APIService

import Foundation
import Moya

// 먼저 API 타겟 정의해줘야한다.
enum PostAPI {
    case fetchPosts
}

extension PostAPI: TargetType {
    var baseURL: URL {
        return URL(string: "https://jsonplaceholder.typicode.com")!
    }
    
    var path: String {
        switch self {
        case .fetchPosts:
            return "/posts"
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .fetchPosts:
            return .get
        }
    }
    
    var task: Task {
        switch self {
        case .fetchPosts:
            return .requestPlain
        }
    }
    
    var headers: [String: String]? {
        return ["Content-Type": "application/json"]
    }
}
 
// APIService 정의해주기
class APIService {
    private let provider = MoyaProvider<PostAPI>()

    func fetchPosts(completion: @escaping (Result<[Post], Error>) -> Void) {
        provider.request(.fetchPosts) { result in
            switch result {
            case .success(let response):
                do {
                    let posts = try JSONDecoder().decode([Post].self, from: response.data)
                    completion(.success(posts))
                } catch {
                    completion(.failure(error))
                }
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}

Moya 사용의 주요 구성 요소를 살펴보자

  1. PostAPI 정의

    • Moya에서는 API 요청을 열거형(enum)으로 정의한다.
    • TargetType 프로토콜을 채택하여 API의 baseURL, path, method, task, headers 등을 구성한다.
  2. MoyaProvider

    • MoyaProvider는 요청을 보내는 주요 객체다.
    • 위 코드에서는 provider.request(.fetchPosts)를 호출해 네트워크 요청을 수행한다.
  3. 응답 처리

    • 성공(.success) 시 응답 데이터를 JSONDecoder를 통해 디코딩한다.
    • 실패(.failure) 시 Moya에서 제공하는 에러를 처리한다.

한 눈에 보는 기존 URLSession 코드와 Moya 코드 비교표

URLSessionMoya
URL을 하드코딩하거나 관리해야 함.TargetType으로 API 엔드포인트를 체계적으로 정의.
각 요청마다 URL과 HTTP 메서드를 명시적으로 작성.API에 따라 path, method만 변경.
요청과 응답 처리를 매번 구현해야 함.MoyaProvider로 코드 간소화.

Moya의 장점을 정리하자면,

  • 구조화된 API 요청 관리 : TargetType으로 API를 명확히 정의 가능.
  • 코드 재사용성 : 여러 API 요청을 손쉽게 추가 가능.
  • 유지보수성 향상 : API의 각 요소(baseURL, path, method)를 독립적으로 관리.

확실히Moya로 구현하면 코드가 더 읽기 쉽고 확장이 편할 것 같다.


controller도 moya에 맞게 코드 수정 진행

1. 기존 코드 (URLSession 사용)

func fetchPosts() {
    let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            self.showError(error)
            return
        }
        guard let data = data else {
            let error = NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "No data received"])
            self.showError(error)
            return
        }
        do {
            let posts = try JSONDecoder().decode([Post].self, from: data)
            self.posts = posts
            self.postsView.tableView.reloadData()
        } catch {
            self.showError(error)
        }
    }
    task.resume()
}

2. Moya 코드

func fetchPosts() {
    provider.request(.fetchPosts) { [weak self] result in
        DispatchQueue.main.async { [weak self] in
            switch result {
            case .success(let response):
                do {
                    let posts = try JSONDecoder().decode([Post].self, from: response.data)
                    self?.posts = posts
                    self?.postsView.tableView.reloadData()
                } catch {
                    self?.showError(error)
                }
            case .failure(let error):
                self?.showError(error)
            }
        }
    }
}
private func showError(_ error: Error) {
    print("Error: \(error.localizedDescription)")
}

차이점

1. API 요청 방식

  • 기존 URLSession 코드: URLSession.shared.dataTask(with:)를 사용하여 데이터를 요청하는데, URL을 직접 구성하고 비동기 네트워크 작업을 직접 관리해야 했다.

  • Moya 코드: provider.request(.fetchPosts)를 사용해서 API 요청을 처리한다. provider는 Moya에서 정의한 MoyaProvider 객체인데, 요청할 API를 PostAPI에 정의된 .fetchPosts로 지정했다.
    Moya는 URL, HTTP 메서드, 헤더 APIService파일에서 자동으로 처리해주었고, 내부적으로 URLSession을 사용하여 네트워크 요청을 수행한다.
    그러면 코드가 간결해질 수 있고 API 요청을 처리하는데 필요한 수고가 줄어들 수 있다.

2. 결과 처리 방식

  • 기존 URLSession 코드: URLSession은 데이터를 받은 후에 dataerror를 직접 확인해야 했다. 오류가 발생한 경우에는 처리하고, 성공한 경우 JSONDecoder로 데이터를 디코딩 했엇다.

  • Moya 코드: resultResult 타입인데, Moya는 네트워크 요청을 마친 후 Result.success(response) 또는 Result.failure(error)를 반환한다.
    response 에는 데이터가 포함되어 있기 때문에 response.data로 쉽게 접근할 수 있는데, Moya를 사용하면 응답과 오류를 좀 더 간결하게 처리한다.

3. 에러 처리 방식

  • 기존 URLSession 코드: 네트워크 요청이 실패하면 error를 확인해 직접 처리해야 하고, 데이터가 없으면 오류를 만들어 처리해야 했다.

  • Moya 코드: Moya에서는 이미 Result.failure 형태로 오류를 전달받기 때문에 따로 NSError를 생성할 필요가 없었다.
    Moya는 내부적으로 오류를 처리하고 개발자는 Result.failure(error)만 받으면 되는데, 이 방식이 더 직관적이고 에러 처리를 더 간단하게 해주었다.

4. UI 업데이트

  • 기존 URLSession 코드: UI 업데이트는 URLSession의 비동기 작업 안에서 직접 처리해야 했었다. UI 업데이트가 반드시 메인 스레드에서 이루어져야 하기 때문에 DispatchQueue.main.async를 사용해 UI를 업데이트를 했다.

  • Moya 코드: DispatchQueue.main.async는 동일하게 사용하긴 하지만, Moya는 네트워크 요청을 내부적으로 관리하고 있기 때문에 그 과정이 좀 더 간단했다. provider.request 내부에서 네트워크 요청이 완료된 후 결과를 받아오기만 하면 됐었기 때문이다.

사용을 해보니...

  • 기존 코드 (URLSession)에서는 네트워크 요청을 직접 다뤄야 했고, 에러 처리와 데이터를 처리하는 부분이 좀 더 복잡했던 것 같다.

  • Moya 코드는 네트워크 요청을 추상화하다보니 API 요청을 더 간편하게 할 수 있도록 도와준 것 같다.
    provider.request(.fetchPosts)와 같은 방식으로 API 요청을 보내고, 응답과 에러를 Result 타입으로 처리해서 코드가 더간결해지고 유지보수가 쉬워진다는게 모야의 장점을 확 느낄 수 있던 부분이었다.


음...

기존의 URLSession을 사용한 방식은 코드가 비교적 길고 반복적이고 요청을 보낼 때마다 매번 URL, 메서드, 헤더 등을 설정해야 했고, 응답을 처리하는 부분도 손수 작성해야 했다.

반면에 Moya는 코드길이로 따지면 뭐 비슷하다고 할 순 있지만, 저런 모든 부분을 TargetType을 통해 미리 설정해두기 때문에 API 요청을 보낼 때마다 설정을 반복할 필요 없이 한 줄로 처리할 수 있다는 점이 좋았다.

그리고 Result 타입을 활용해서 응답을 처리하는 부분도 더 간결한 느낌이었고.. 그래서 한 번 익숙해지면 네트워크 관련 코드의 유지보수와 확장성이 매우 용이해질 것 같다는 생각이 들었다.

오늘 내가 Moya를 공부하면서 느낀 가장 큰 이점은 "간편함"이었다.

그리고 Moya를 공부할 때 제일 중요한 포인트는 TargetType 프로토콜을 정의하고 사용하는 방법인데, 이 부분이 Moya의 핵심 구조이기 때문에 꼭 꼭 잘 기억해두자..!

profile
iOS 좋아. swift 좋아.

4개의 댓글

comment-user-thumbnail
2024년 12월 22일

Alamofire보다 좋던가요? base랑 path, header 같은 거 미리 정의하는 거 너무 마음에 드네요. 저렇게 하고 싶어서 Request를 구조체로 만든 적도 있었는데.. 나중에 나도 써볼까 흐음

1개의 답글
comment-user-thumbnail
2024년 12월 23일

모야는 api를 위한 거엿구나 모야

1개의 답글

관련 채용 정보