Alamofire+Router

14666 단어 iOSiOS

서버 통신을 할 때 각 서비스마다 비슷한 코드를 작성하게 되었고 이는 효율성 측면에서 좋은 코드라고 할 수 없다. (통신 코드가 비슷하기 때문에 복붙하고 파라미터만 바꾸는 형식)
이에 대해 분기처리를 Router에서 관리하고 하나의 request를 이용해서 여러 endpoint에 대한 네트워크 통신을 할 수 있다.

Alamofire(ver. 5) Advanced Usage를 통해 request routing이 무엇인지 알아보고, router를 통해 request를 하는 방법을 적용해보자.

Request Routing

라우터라고 하면 보통 집으로 들어오는 하나의 네트워크 선에 대해서 라우터를 통해 여러 컴퓨터에 연결할 수 있고 와이파이로도 사용할 수 있다. 이와 같은 개념을 앱에서도 적용할 수 있다.

하나의 앱에서는 여러 API 요청이 발생하는데 Request Router를 통해서 모든 요청을 생성할 수 있고 이를 통해 API 요청 로직을 일관적으로 관리할 수 있다.
만약 API 요청을 할 때마다 Request를 정의하는 코드가 여기저기 있다면 이를 Router를 통해 하나로 모아서 관리할 수 있으므로 보다 안정적인 코드로 만들 수 있다.

위의 Alamofire 공식문서에서도 다음과 같이 라우터에 관한 장점을 말하고 있다.

As apps grow in size, it’s important to adopt common patterns as you build out your network stack. An important part of that design is how to route your requests. The Alamofire URLConvertible and URLRequestConvertible protocols along with the Router design pattern are here to help.

앱의 규모가 커질수록 네트워크 스택을 구축할 때 공통 패턴을 구축하는 것이 중요한데, 이 때 라우트를 어떻게 하는지가 중요한 부분이다. 라우터 설계 패턴과 함께 Alamofire는 URLCovertible 과 URLRequestConvertible 프로토콜을 사용해 네트워크 공통 패턴을 디자인하고 있다.

enum 타입을 통해 router 경로를 지정해주고 네트워크 스택을 구축하는 곳으로 예를 들어 photo와 user를 검색하는 두가지 네트워크 상황에 있어서 search라는 스택 하나에 대해서 그 다음 photo, user 스택을 각각 쌓을 수도 있지만 router로 코드를 작성하게 되면 HTTPMethod .get, .post에 따른 구분이 가능하며 코드 중복이 줄어들고 enum으로 parameter와 path를 묶어서 볼 수 있으므로 가독성 측면에서도 좋다. 이렇게 구축한 경로를 func asURLRequest() throws -> URLRequest를 통해 반환한다.

라우터에 request의 구성 요소를 정의하여 네트워크 스택 구축 시 공통 패턴을 채택할 수 있도록 한다고 되어 있다. 즉 네트워크 요청 부분을 분리(캡슐화)해서 사용하고 있다.

Routing Request

URLRequestConvertible
'HTTP메서드, 헤더, 매개변수와 같은 초기 매개변수들'을 URLReqestConvertible로 캡슐화 하여 값을 전달합니다.
URLRequest는 기본적으로 URLRequestConvertible을 따르므로 요청, 업로드 및 다운로드 메서드에 직접 전달할 수 있습니다.

라우터는 URLRequestConvertible 을 채택하고, Enum 타입으로 작성하면 된다.

enum Router: URLRequestConvertible {
    case get, post
    
    var baseURL: URL {
        return URL(string: "https://httpbin.org")!
    }
    
    var method: HTTPMethod {
        switch self {
        case .get: return .get
        case .post: return .post
        }
    }
    
    var path: String {
        switch self {
        case .get: return "get"
        case .post: return "post"
        }
    }
    
    func asURLRequest() throws -> URLRequest {
        let url = baseURL.appendingPathComponent(path)
        var request = URLRequest(url: url)
        request.method = method
        
        return request
    }
}

Router에서 case 하나를 선택하면 case에 따른 path와 parameter가 같이 오고 asURLRequest() 함수에 의해서 URLRequest 타입으로 만들어 반환한다.

앞서 말했던 photo와 user 검색의 예시를 위의 코드처럼 적용해보면, 다음과 같다.

enum SearchRouter: URLRequestConvertible {
    case SearchPhotos(terms: String)
    case SearchUsers(terms: String)
    
    var baseURL: URL {
        return URL(string: API.BASE_URL + "search")!
    }
    
    var method: HTTPMethod {
        switch self {
        case .SearchPhotos: return .get
        case .SearchUsers: return .get
        }
    }
    
    var path: String {
        switch self {
        case .SearchPhotos: return "/photos"
        case .SearchUsers: return "/users"
        }
    }
    var parameters :[String:String]{
        
        switch self {
        case let .SearchPhotos(terms): return ["query": terms]
        case let .SearchUsers(terms): return ["query": terms]
        }
    }
    
    func asURLRequest() throws -> URLRequest {
        let url = baseURL.appendingPathComponent(path)
        var request = URLRequest(url: url)
        
        request.method = method
        
        request = try URLEncodedFormParameterEncoder().encode(parameters, into: request)
        
        switch self {
        case let .SearchPhoto(parameters):
            request = try URLEncodedFormParameterEncoder().encode(parameters, into: request)
        case let .SearchUser(parameters):
            request = try JSONParameterEncoder().encode(parameters, into: request)
        }
        
        return request
    }
}

좋은 웹페이지 즐겨찾기