이 글은 Alamofire(ver. 5) Advanced Usage의 request routing이 무엇인지 알아보고, router를 통해 request를 하는 방법을 소개합니다.
Request Routing..?
집에 있는 라우터를 먼저 떠올려보면, 집으로 들어오는 하나의 네트워크 선이 라우터를 통해 여러 컴퓨터에 연결할 수 있고 와이파이도 만들어줍니다. 앱에서의 라우터도 비슷한 느낌이죠. 하나의 앱에선 여러 API 요청이 발생하는데 Reqeust Router을 통해서 모든 요청을 생성할 수 있고, 이를 통해 API 요청 로직을 일관적으로 관리할 수 있습니다. API 요청할 때마다 Reqeust를 정의하는 코드가 여기저기 흩어져서 불안했다면(=It’s me🙋♀️) 라우터를 만들어서 내 관리하에 있다는 안정감을 얻을 수 있을 것입니다.
Alamofire의 Advanced Usage 문서에 Request Routing에 대한 내용이 있으며, 다음과 같이 사용하면 좋다는 것을 어필하고 있다.
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
andURLRequestConvertible
protocols along with theRouter
design pattern are here to help.
대충, 라우터를 디자인하는게 중요한데 URLRequestConvertible
프로토콜이 그걸 가능하게 해준다는 내용.
연습해볼 API
라우터를 만들어보기 전에 API 주소와 데이터 모델을 저장합니다. 실제로 테스트해 볼 수 있는 API 이니 함께 해보시죠! (전체 코드 Git 저장소는 이 글의 맨 아래 있습니다.)
API의 URL은 “https://httpbin.org/get”, “https://httpbin.org/post” 입니다. 따라서 baseURL
과 APIPath
를 아래와 같이 저장합니다.
🔽 URL
let baseURL = "https://httpbin.org"class APIPath{
static let postPractice = "/post"
static let getPractice = "/get"
}
🔽 Parameter: userName
과 age
를 파라미터로 전달할 것이기 때문에, Codable
을 따르는 구조체를 만듭니다.
struct UserInfo: Codable {
var userName: String?
var age: Int?
}
🔽 Response: 응답값으로String
타입의 url
, UserInfo
타입의 json
을 받을 것입니다. 이따 확인해보겠지만 요청했던 URL과 파라미터가 그대로 응답값으로 오는 API 입니다.
struct HttpbinResponse: Decodable {
var url: String?
var json: UserInfo?
}
라우터 생성하기 - Routing Request
코드 작성하기 전에 라우터를 설계해 보겠습니다. 라우터는 Reqeust를 만드는 것이라는 컨셉을 기억하면서 라우터의 역할을 적어보면,
- base URL을 가변적으로 설정할 수 있다. 앱에서 요청하는 API는 인증 API와 서비스 API로 두 종류가 있고, 이 둘의
baseURL
은 다르다고 가정해 본다. 또한, 빌드 환경(개발/스테이징/실서버)에 따라서도baseURL
이 달라진다고 하자. 이처럼 어떤 API에 요청하는지, 빌드 환경은 어떤지에 따라 라우터가baseURL
을 설정해 줄 수 있어야 한다. get
또는post
요청 방식에 따라 파라미터가 추가되도록 한다.get
은 파라미터가 URL 쿼리로 추가되고post
는 body로 추가된다.- URL 인코딩을 적용한다.
- 이 외에도 필요에 따라 header 값을 추가할 수 있다.
이렇게 설계한 라우터를 구현해봅시다.
[코드 노트]
URLRequestConvertible
프로토콜을 따르는 APIRouter
클래스를 정의합니다. URLRequestConvertible
프로토콜은 asURLRequest()
메소드를 구현해야 하는데, 이 메소드에서 앞서 설계한 Request
를 만들어 반환합니다.
1. enum
으로APIType
을 정의합니다. APIType
을 auth
와 service
로 나누고, 케이스에 따른 baseURL
을 저장합니다.
개발환경에 대한 enviroment
변수는 전역에서 접근가능하도록 저장했습니다. ⤵️
enum Enviroment {
case dev
case stage
case real
}
let enviroment: Enviroment = .dev
2. baseURL
과 path
를 연결해서 URLComponent
를 생성하고 퍼센트 인코딩을 합니다.
3. get
방식일때 파라미터를 query parameter
로 추가해주기 위해, Data
타입인 파라미터를 Dictionary
형태로 타입캐스팅을 한 후에 queryItem
으로 추가합니다.
4. 2–4과정으로 baseURL
과 path
, query parameter
로를 연결해서 URL
을 만들었는데요, 이 URL
로 Request
를 생성합니다.
5. post
방식이면 httpBody
를 추가하고, header
에Content-Type
을application/json
으로 설정합니다.
이 외에도 필요한 header 값을 추가할 수 있지만, Authorization
값은 RequestInterceptor
로 인증 흐름을 구현하는 걸 추천드립니다. 관련 글은 여기에 있습니다.
라우터 사용하기
이제 위에서 만든 APIRouter
를 호출할 거예요. 아래와 같이 요청 파라미터를 만듭니다.
var userInfo: UserInfo = {
var userInfo = UserInfo()
userInfo.userName = "yungso"
userInfo.age = 20
return userInfo
}()
Router
에 파라미터는 Data
타입이기 때문에, Data
타입으로 형변환 해주는 extesion
을 아래와 같이 만듭니다.
extension Encodable {
var toData: Data? {
guard let data: Data = try? JSONEncoder().encode(self) else { return nil }
return data
}
}
이제 드디어 get
요청을 해보자구요! APIRouter
로 Request
를 리턴받아서 요청합니다.
제대로 요청이 되었는지 콘솔에 프린트된 내용을 확인해보아요.
[REQUEST]
URL: https://httpbin.org/get?age=20&userName=yungso
Body: nil
[RESPONSE]
URL: Optional("https://httpbin.org/get?age=20&userName=yungso")
Response Data: ...
post
요청은 APIRouter
의 init
파라미터에서 path
와 httpMethod
만 바꿔주면 됩니다. 넘나 편한것..
[REQUEST]
URL: https://httpbin.org/post
Body: {
"age" : 20,
"userName" : "yungso"
}
[RESPONSE]
URL: https://httpbin.org/post
json body: UserInfo(userName: Optional("yungso"), age: Optional(20))
Response Data: ...
마무리
위의 라우터에는 put
방식에서 파라미터 추가하는 내용이 빠져있기 때문에 put
요청이 있다면 이 부분을 꼭 추가해야 합니다. 인터넷에 Alamofire Router로 검색해서 다른 코드들도 살펴보면, get
/post
/put
/delete
방식에 따라 라우터를 따로 만드는 경우도 있었고, APIRouter
를 인스턴스화 하는 부분을immutable value
로 한 파일에 저장해두고 사용하기도 하더라구요. 편의 또는 상황에 맞게 라우터를 커스텀하여 만드는데 이 글이 도움이 되었으면 좋겠습니다~!
🔽 전체 소스코드를 포함한 데모 프로젝트이니, 실행해보며 테스트해보실 수 있습니다.
🔽 Alamofire Advanced Usage에 대해 더 알아보고 싶다면👇👇 click click!