[Swift][Network][Library] Alamofire에 대하여(1)

Uno·2021년 9월 25일
0

Alamofire

목록 보기
1/1

Alamofire 공식 Github 페이지 : GitHub - Alamofire/Alamofire: Elegant HTTP Networking in Swift

Alamofire 개요

Alamofire의 어원은 꽃 이름이라고 합니다. TMI
Alamo Fire 라는 꽃은 텍사스 지방에서 핀다고 합니다.

Alamofire 란?

Alamofire provieds an elegant and composable interface to HTTP network request.

Alamofire는 HTTP 네트워크 요청을 도와주는 라이브러리 입니다.

Alamofire는 자체 네트워크 통신을 하는 기능이 있지는 않습니다.
애플에서 제공하는 URL Loading System 위에서 동작합니다. 즉, 사용하기 편하도록 구성했다는 뜻이죠.

그 뜻은 URLSession 과 URLSessionTask를 서브클래싱했다는 뜻과 같습니다. 이렇게도 해석할 수 있을 겁니다. URLSession의 발달과 제공기능에 Alamofire의 기능 한계가 결정된다고도 볼 수 있겠죠. (URLSession에서 불가능하다면, 그것을 통해 만든 Alamofire도 당연히 불가능한 것 처럼요.)

추가로, Alamofire는 네트워킹을 비동기로 처리합니다.
-> 비동기로 처리해주지 않는다면, 개발자가 직접 비동기로 지정해주어야할 수도 있겠지만, 이를 편리하게 알아서 해줬다는 의미입니다.


Alamofire 사용

지금부터는 Request(요청) 에 대해서 어떻게 Alamfire가 구성하고 있는지 알아볼게요.

예시를 먼저 보겠습니다.

import Alamofire // <- 이하 생략

let url = "https://httpbin.org/get"
AF.request(url).response { response in
	print(response)
}

가장 단순한 예시입니다.
url 스트링 값을 request의 파라미터로 전달해줬습니다. 그리고 클로저로, 결과값을 입력받은 이후에, print문으로 처리하고 있죠.

request의 함수 형태를 볼까요?

func request<Parameters: Encodable>(_ convertible: URLConvertible,
                                         method: HTTPMethod = .get,
                                         parameters: Parameters? = nil,
                                         encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
                                         headers: HTTPHeaders? = nil,
                                         interceptor: RequestInterceptor? = nil) -> DataRequest

위 함수가 request 함수의 원형입니다. 여기있는 파라미터들 보면 옵셔널(Optional) 도 있고 초기값(default Value) 가 설정되어 있기도 합니다. 그 뜻은, 모든 파라미터를 작성할 필요는 없다는 뜻이기도 합니다.
(당연히, 자신이 사용하는 환경에 맞는 경우는 생략이 가능하다는 뜻입니다.)

이 메소드는 결론적으로는 DataRequest 를 리턴합니다.
그 전에 개별 구성요소등이랑 요청들을 결합시킵니다. 파라미터로 있는 methodheader 있죠? 그것들을 설정된 값에 따라서 결합하는 거죠.

비유하자면, 물건을 보내기 위해서 택배를 보내는 것과 유사합니다.

  • 물건을 사는 것 or 파는것 인지 결정 : Method
  • (물품 구매하는 것이라면) 누가 보내는지 혹은 기타 정보 : Header

그리고 각 Request 를 어떤식으로 구성할 지 결정합니다. 그것을 RequestInterceptorEncoable 에서 결정합니다.

다시 물건 사는것에 대한 예시를 적용하면 다음과 같습니다.

물건 구매 -> Method를 통해 결정한다.
누가 사는 건지 알려준다. -> Header
물품을 어떻게 구성할지, 상세 구성을 결정한다. -> RequestInterceptor와 Encodable

만약 이 글을 보시는 분이 이전에 Alamofire를 사용하신 분들이면, 이해될만한 정보를 말씀드릴게요.

매개변수 딕셔너리 + ParameterEncoding 을 이용해서 요청하는 방법이 있습니다. 하지만 이 방법은 권장하지 않습니다. Alamofire에서 제거될 예정입니다.

혹시 파라미터에 값을 할당하기 위해 Parameters 딕셔터리 타입으로 생성하신 후 인코딩하여 파라미터로 전달했다면, 그 방법은 이제 없어질 예정이니 주의하세요~!

  • RequestInterceptor
    단어 뜻 그대로 Request를 중간에 낚아 채는 겁니다.

왜? 낚아 채는데??

사용사례를 말씀드리면 좀더 빨리 이해하실 것 같습니다.
Header가 공통으로 사용되는 Rquest가 있다고 칩니다. 그러면 매번 Header를 작성하기 보다는, 그냥 모든 Request에 작성되면 좋겠죠. 그래서 서버에서 다음과 같이 작성하곤 합니다.

public interface RequestInterceptor {
	void apple(RequestTemplate template);
}

코드를 자세히는 몰라도, Request에 관련된 탬플릿을 apply 하고 있죠.

여러 메소드에 대해서 공통의 Header가 있는 경우 사용하면 적절한 도구 입니다.


HTTP Methods

정의된 HTTP 메소드들 입니다.

public struct HTTPMethod: RawRepresentable, Equatable, Hashable {
    public static let connect = HTTPMethod(rawValue: "CONNECT")
    public static let delete = HTTPMethod(rawValue: "DELETE")
    public static let get = HTTPMethod(rawValue: "GET")
    public static let head = HTTPMethod(rawValue: "HEAD")
    public static let options = HTTPMethod(rawValue: "OPTIONS")
    public static let patch = HTTPMethod(rawValue: "PATCH")
    public static let post = HTTPMethod(rawValue: "POST")
    public static let put = HTTPMethod(rawValue: "PUT")
    public static let trace = HTTPMethod(rawValue: "TRACE")

    public let rawValue: String

    public init(rawValue: String) {
        self.rawValue = rawValue
    }
}

사용 예시는 다음과 같습니다.

AF.request("https://httpbin.org/get") // 없는 경우 (get)
AF.request("https://httpbin.org/post", method: .post) // 아래는 모두 명시함
AF.request("https://httpbin.org/put", method: .put)
AF.request("https://httpbin.org/delete", method: .delete)

여기서 주의하실 점이 있습니다.
Get Method를 활용하는데 만약 body에 값을 넣도록 API 명세서에 적혀있다면, 서버 분께 말씀을 드려서 현재 iOS에선 Get메소드에 Body를 넣을 수 없음을 전달해야합니다.
(다른 플랫폼이라고 해서 하진 않을 텐데, 혹시 모르니 …)

Get의 Body를 넣는 것이 불가능한 이유는 URLSession에서 지원하지 않습니다.


파라미터(Parameters) 와 인코더(Encoders)

예제 코드를 보겠습니다.

struct Login: Encodable {
    let email: String
    let password: String
}

let login = Login(email: "test@test.test", password: "testPassword")

AF.request("https://httpbin.org/post",
           method: .post,
           parameters: login,
           encoder: JSONParameterEncoder.default).response { response in
    debugPrint(response)
}

Login 이라는 구조체를 정의했죠. 여기서 Encodable을 채택해야 파라미터로 전달할 수 있습니다.

Encodable 뜻 자체가 코드화 시킬 수 있다는 뜻이죠. 구조체 그대로는 코드화가 불가능한데, 그것을 가능하게 해줍니다. 코드화를 해야 데이터를 주고 받을 수 있는 형태가 되거든요.
(자세한 설명은 다시 글을 작성할 예정입니다.)

그렇게 값을 넣고 싶은 형태로 구조체를 선언한 이후에

let login = Login( …)

이런식으로 인스턴스를 생성하고,

AF.request( ... )

Alamofire 메소드 중 request를 실행하고 있습니다.

메소드는 post
파라미터는 우리가 정의한 login
그 값을 어떻게 인코딩 할 거냐면, JSONParameterEncoder.default 로 한다고 적혀있죠.

그 값에 대한 결과값을 클로저로 받은 이후에 print문으로 찍어보고 있네요.

URLEncodedFormParameterEncoder 에 대해서 좀더 알아볼게요.


URLEncodedFormParameterEncoder

URLEncodedFormParameterEncoder 는 값을 URL 인코딩된 문자열로 인코딩합니다. 그리고 기존의 URL 쿼리 문자열로 설정하거나 추가해서 HTTP body로 설정하죠.

좀 쉽게 쓰자면, 파라미터를 URL로 인코딩할 것인데, 어떤식으로 할지 결정하는 겁니다.

URLEncodedFormParameterEncoder 은 3 가지 열거형 케이스가 있습니다.

  • .methodDependent : 인코딩된 쿼리 문자열의 결과를 .get, .head, .delete 요청에 대해 기존의 문자열에 적용하고, 다른 HTTP 메소드가 있는 요청에는 Body로 설정합니다. (대상은 당연히 파라미터겠죠.)
  • .queryString : 요청할 때 입력한 URL에 문자열로 설정하거나 추가합니다. (쿼리스트링)
  • .httyBody : HTTP의 Body로 설정합니다.

에졔 코드입니다.

let parameters = ["foo": "bar"]

AF.request("https://httpbin.org/get",
			 parameters: parameters) 

AF.request("https://httpbin.org/get",
			  parameters: parameters,
			  encoder: URLEncodedFormParameterEncoder.default)

AF.request("https://httpbin.org/get",
			   parameters: parameters,
 			encoder: URLEncodedFormParameterEncoder(
				destination: .methodDependent)
					  )

-> encoder의 형태를 비교해서 보시면 되겠습니다.

다른 예제 코드입니다.

let parameters: [String: [String]] = [
    "foo": ["bar"],
    "baz": ["a", "b"],
    "qux": ["x", "y", "z"]
]

// All three of these calls are equivalent
AF.request("https://httpbin.org/post", method: .post, parameters: parameters)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder.default)
AF.request("https://httpbin.org/post", method: .post, parameters: parameters, encoder: URLEncodedFormParameterEncoder(destination: .httpBody))

// HTTP body: "qux[]=x&qux[]=y&qux[]=z&baz[]=a&baz[]=b&foo[]=bar"



느낀점

일단 여기까지만 정리하고, 2 편으로 넘기겠습니다.

제가 공식문서 내용을 좀 정리하면서 생략한 부분도 있긴 합니다. 아직 급하게 필요하지 않은 경우나, 너무 디테일한 부분을 생략했습니다.

그 부분은 따로 모아서 다룰 예정입니다. 직접 프로젝트를 만들어서 테스트 해봐야 할 것 같거든요.

2 펀부터는 좀더 프로젝트 위주로 글을 작성해보겠습니다~!







참고자료


profile
iOS & Flutter

0개의 댓글