기본적으로 URLSession
이든 Alamofire
든 moya
든 url을 가지고 놀아야한다는건 동일합니다
하지만 지금까지 대부분 url을 그대로 복사해서 사용해왔습니다(저도그랬고 아마 대부분 그렇게 사용 했을겁니다)
연결해야할 API가 많아봐야 3개정도였어서 그랬을 수도 있겠지만 이는 하드코딩
이라는 느낌을 지울수가없습니다. 재사용하기 어려운 코드가 되고 추후에는 확장성이 전혀없는 코드이기도 합니다 그런의미에서 url다루기에서는 매번 url을 복붙만하는 방법대신 URLComponents
객체를 사용하는 방법에대해 이야기해보려합니다.
우선 그전에 우리는 url의 설계구조에대해 알 필요가 있습니다(저는 비전공자라 공부까지는 안해봤던부분이라 넣는거니 알고있으면 그냥 넘기면 됩니다)
url은 이렇게 enum처럼 각각의 케이스가 나누어져 있습니다 아래의 port가 존재하는 경우는 우선 생각하지말고 우리가 매번보는 url인 위에부분을 보면
크게 scheme
, host
, path
, query
로 나눠져있습니다.
URLComponents
객체는 구조체입니다 애플에서 실제로 구현해놓은 URLComponents
구조체의 구조를 보겠습니다.
우선 해당 구조체를 이니셜하는 방식이 총 3가지로 구현이 되어있습니다
첫번째, 그냥 빈 구조체를 init한다
두번째, Url을 넣어서 구조체를 init한다
세번째, string값을 넣어서 구조체를 init한다
사실 어떤걸써도 상관없는데 우선 저희는 가장기본인 첫번째 방식으로 코드를 구현해보겠습니다
Apple에선 itunes의 api를 제공하는데요 api연습을할때나 test할때 자주쓰는 api니 한번쯤 구현해보면 좋을거같아요
"https://itunes.apple.com/search?media=movie&term=movie"
제가 url객체
로 만들어야할 string은 위와같은 문자열이라고 가정해보겟습니다
처음부터 차근차근 해보죠 우선 객체를 생성합니다
var urlComponent = URLComponents()
말그대로 변수에다가 URLComponents객체를 생성해서 담아줍니다
그리고나서 우리가 위에서 봤던 URL을 구성하는 구조를 구성하면됩니다. 지금 현재 빈객체에 다가 각각의 변수에 어떤 값들을 넣어주면됩니다. URLComponents의 구조체를 보면 각각의 변수들이 옵셔널로 선언이 되어있는걸 확인할 수 있습니다. 그러니까 우리는 빈객체에 nil로 할당되어있는 변수들에다가 값들을 바꿔주면되겠죠?
한번 해보죠 우리가 데이터를 받아올 itunes url에서 각 케이스의 데이터를 넣어줍시다
urlComponent.scheme = "https"
urlComponent.host = "itunes.apple.com"
urlComponent.path = "/search"
urlComponent.query = "media=movie&term=movie"
이렇게 하면 URLComponents객체에 옵셔널로 되어있던 변수들에 새 값들이 할당이 되었습니다. 그렇다면 우리는 이 URLComponents객체에서 실제 URL객체를 받아와야합니다
다행히도 URLComponents네는 URL타입을 리턴해주는(옵셔널로) 계산속성이 존재합니다. 물론 옵셔널이니 한번 걸러주는 언래핑이 필요할겁니다
if let url = urlComponent.url {
print(url)
}
/// 결과 : https://itunes.apple.com/search?media=movie&term=movie
이런식으로 URLComponent가 바뀌면 뭐가 좋을까요?
한번 고민을 해봅시다. 아무리 좋아보이는 기술이라도 뭐가좋은지를 모르면 이런방식을 쓰는게 큰 의미가 없어집니다.
제가 배달의민족 클디서 세미나를 할 당시에 baseUrl
이 바뀌었던 경우가 있었습니다. 근데 제가 그때 url자체를 변수로 받아놓은 상황이어서 모든 api를 다시 복붙해서 바꿔야했었어요. 사실 path나 query나 scheme이 바뀌는 않았죠. host만 바뀐상황인데 다시 모든 url을 지우고 붙여넣었어야 했습니다. 하지만 이런식으로 URLComponents를 사용하게되면 host변수에 바뀐 새로운값만 할당
하면됩니다.
두번째 장점이라고 하면 사실 url에서 /
하나만 잘못 쳐도 URL객체가 생성되지 않습니다. 그런데 보통 우리가 url을 언래핑할때
guard let url = url else { return }
이런식으로 guard문으로 벗기고 error핸들링을 안하고 그냥 return으로 함수를 끝내버리면 url이 생성되지 않았기때문에 아래있는 실행문이 실행되지 않기 때문에 어디서 문제가 생긴건지 모를때가 많아요. 이때부터 에러가 어디서 발생했는지 코드를 뒤지는 일이 시작됩니다…하지만 그때 사실 /
하나 안써서 문제가 발생하더라도 그걸 찾기는 더 어렵습니다.
하지만 URLComponents를 사용하면 적어도 /
때문에 문제생길 확률이 줄어들죠
장점은 이정도로 마무리하고 실제로 사용을 하는 방식중에 query를 사용하는 방식에대해 배워보겠습니다
?media=movie&term=movie
이런식으로 URL에서 ?뒤에 key와 value로 이루어진 녀석을 query라고합니다 그리고 각 query는 &로 연결됩니다. 여기서는 media라는 key의 value는 movie가 되겠네요
위의 예제에서는 아얘 이 query가 string으로 들어갔죠 근데 이친구는 명백히 key와 value로 이루어져있기때문에 하드코딩을 하는건 그렇게 좋은 방법이 아닐 수 있습니다
그걸 알았는지 애플쪽에서도 이러한 query요소들(query item이라고 부르더군요)을 설정하고 넣는 방법을 제공하고있습니다. 바로 URLQueryItem이라는 녀석인데요.
어떻게 생긴녀석인지 간단히 봅시다
보면 이니셜라이저가 하나뿐인걸보니 name이랑 value를 꼭 넣어야하나봅니다. 여기서 name은 key, value는 value라고 생각하면 편합니다.
그러면 우리는 itunes api url에서 query가 두개있으니 두개를 만들면 되겠다는 생각을 하면됩니다
그럼 두개니까? 만들어보죠
enum MediaType {
case movie
case podcast
case music
case musicVideo
case tvShow
var queryValue: String {
return "\(self)"
}
}
그전에 저는 하드코딩하는게 싫어서 이런 queryValue를 return해주는 enum을 하나 만들었습니다
let term = URLQueryItem(name: "term", value: mediaType.queryValue)
let media = URLQueryItem(name: "media", value: mediaType.queryValue)
그리고 이 URLQueryItem을 URLComponents객체에 있는 변수에 넣어줘야하는데 분명히 아까 봤을때는 query라는 변수는 string이었단 말이죠…? 그래서 좀 더 살펴보니
URLComponents안에 queryItems라는 변수가있네요? 심지어 URLQueryItem을 list로 넣을수있는? 그러면 이것도 넣어버립시다
let querys = [term, media]
components?.queryItems = querys
그리고 아까처럼 url을 받아봅시다
if let url = urlComponent.url {
print(url)
print(type(of: url))
}
/// 결과
/// https://itunes.apple.com/search?media=movie&term=movie
/// URL
이렇게하면 /
도 신경안써도되고 ?
도 신경안써도 되고 =
도신경안써도 되고 &
도 신경쓸필요가없습니다
이렇게 해서 URL다루는법에 대한 새로운 방법에 대해 배워봤습니다
if let url = URL(string: "https://itunes.apple.com/search?media=movie&term=movie") {
print(url)
print(type(of: url))
}
기존에 쓰던 방식이 더 짧고 간결합니다 사실은?
var urlComponent = URLComponents()
urlComponent.scheme = "https"
urlComponent.host = "itunes.apple.com"
urlComponent.path = "/search"
let term = URLQueryItem(name: "term", value: "movie")
let media = URLQueryItem(name: "media", value: "movie")
let querys = [term, media]
urlComponents.queryItems = querys
if let url = urlComponent.url {
print(url)
print(type(of: url))
}
같은 url이라고 했을때 URLComponents를 쓰는게 훨씬 코드도 길지만 블록처럼 각각의 값을 끼워맞춰서 하나의 url을 구성했다는건 혹여나 변화가 생겼을때 갈아끼울수있다는 장점이 되기도 합니다. 그리고 다른사람의 코드가 아래처럼 작성이 되어있다면 훨씬 url을 파악하기가 쉽겠죠?
이렇게 URL을 만드는 새로운 방식에 대해 알아봤습니다 다음시간엔 URLSession에 대해 알아보겠습니다