Moya
는 네트워크 레이어를 캡슐화하고, 코드 가독성을 높이는 데 유용한 라이브러리다.
API 요청을 명확하게 정의해주고, 코드의 재사용성과 유지보수성을 높일 수 있다는 설명을 보고
알라모파이어보다 모야를 써보면 어떨까 싶어서 연습해보았다.
Moya
를 사용하여 어제 적었던 API코드(URLSession)의 동일한 기능을 변경해보자!
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))
}
}
}
}
PostAPI
정의
Moya
에서는 API 요청을 열거형(enum
)으로 정의한다. TargetType
프로토콜을 채택하여 API의 baseURL
, path
, method
, task
, headers
등을 구성한다. MoyaProvider
MoyaProvider
는 요청을 보내는 주요 객체다. provider.request(.fetchPosts)
를 호출해 네트워크 요청을 수행한다.응답 처리
.success
) 시 응답 데이터를 JSONDecoder
를 통해 디코딩한다. .failure
) 시 Moya에서 제공하는 에러를 처리한다.URLSession
코드와 Moya 코드 비교표URLSession | Moya |
---|---|
URL을 하드코딩하거나 관리해야 함. | TargetType 으로 API 엔드포인트를 체계적으로 정의. |
각 요청마다 URL과 HTTP 메서드를 명시적으로 작성. | API에 따라 path , method 만 변경. |
요청과 응답 처리를 매번 구현해야 함. | MoyaProvider 로 코드 간소화. |
TargetType
으로 API를 명확히 정의 가능. baseURL
, path
, method
)를 독립적으로 관리. 확실히Moya
로 구현하면 코드가 더 읽기 쉽고 확장이 편할 것 같다.
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()
}
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)")
}
기존 URLSession 코드: URLSession.shared.dataTask(with:)
를 사용하여 데이터를 요청하는데, URL을 직접 구성하고 비동기 네트워크 작업을 직접 관리해야 했다.
Moya 코드: provider.request(.fetchPosts)
를 사용해서 API 요청을 처리한다. provider
는 Moya에서 정의한 MoyaProvider
객체인데, 요청할 API를 PostAPI
에 정의된 .fetchPosts
로 지정했다.
Moya는 URL, HTTP 메서드, 헤더 APIService파일에서 자동으로 처리해주었고, 내부적으로 URLSession
을 사용하여 네트워크 요청을 수행한다.
그러면 코드가 간결해질 수 있고 API 요청을 처리하는데 필요한 수고가 줄어들 수 있다.
기존 URLSession 코드: URLSession
은 데이터를 받은 후에 data
와 error
를 직접 확인해야 했다. 오류가 발생한 경우에는 처리하고, 성공한 경우 JSONDecoder
로 데이터를 디코딩 했엇다.
Moya 코드: result
는 Result
타입인데, Moya는 네트워크 요청을 마친 후 Result.success(response)
또는 Result.failure(error)
를 반환한다.
response
에는 데이터가 포함되어 있기 때문에 response.data
로 쉽게 접근할 수 있는데, Moya를 사용하면 응답과 오류를 좀 더 간결하게 처리한다.
기존 URLSession 코드: 네트워크 요청이 실패하면 error
를 확인해 직접 처리해야 하고, 데이터가 없으면 오류를 만들어 처리해야 했다.
Moya 코드: Moya에서는 이미 Result.failure
형태로 오류를 전달받기 때문에 따로 NSError
를 생성할 필요가 없었다.
Moya는 내부적으로 오류를 처리하고 개발자는 Result.failure(error)
만 받으면 되는데, 이 방식이 더 직관적이고 에러 처리를 더 간단하게 해주었다.
기존 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의 핵심 구조이기 때문에 꼭 꼭 잘 기억해두자..!
Alamofire보다 좋던가요? base랑 path, header 같은 거 미리 정의하는 거 너무 마음에 드네요. 저렇게 하고 싶어서 Request를 구조체로 만든 적도 있었는데.. 나중에 나도 써볼까 흐음