티스토리 뷰
현재 플젝에 데이터 가져오는 URL이 하드코딩 되어있어서 이걸 내일 리팩토링 하려고 공부해보았다 (사실 구 RayWenderwich의 URLSession 강의 따라하다가 딴길로 샘).
다음과 같은 URL이 있다고 해보자.
https://itunes.apple.com/search?media=music&entity=song&term=Muse
https
는 schemeitunes.apple.com
은 host/search
는 path?
뒤부터 나오는 media=music&entity=song&term=Muse
는 query다.
URL을 만들 때마다 위처럼 하드코딩을 해줄수도 있겠지.. 그치만 그런 멍청한 방법은 당연히 쓰고 싶지 않다. 그럼 얘를 어떻게 쪼갤까? 할 때 URLComponents, URLQueryItem을 이용할 수 있다.
URLComponents
URLComponents는 URL을 구성하는 요소들로 URL을 파싱하거나, 그 요소들로부터 URL을 만들어주는 구조체이다.init
할 수 있는 방법이 총 네 개임.
- init()
- 얘는 그냥 init만 하는 친구. 만약 얘로 위의 URL을 만든다 치면 아래와 같이 만들 수 있다.
init?
이 아니기 때문에 옵셔널 바인딩 안 해도 됨. - 구조체이기 때문에,
let
으로 선언해버리면 얘의 프로퍼티인scheme
,host
등등에 값을 할당할 수 없으니까,var
로 선언해야 한다.
- 얘는 그냥 init만 하는 친구. 만약 얘로 위의 URL을 만든다 치면 아래와 같이 만들 수 있다.
var components = URLComponents()
components.scheme = "https"
components.host = "itunes.apple.com"
components.path = "/search"
components.queryItems = [URLQueryItem(name: "media", value: "music"),
URLQueryItem(name: "entity", value: "song"),
URLQueryItem(name: "term", value: "Muse")]
let url = components.url
init(from: Decoder)
- 이건.. 디코더로 파싱한 것을 넣어서 init 하는 것 같다. 근데 얘는 잘 안 쓰나봄. 예제가 없다;
init?(from: String)
- 이건 넣어준 문자열 가지고 init 하는 거.
init?
이라서 생성되는URLComponents
의 인스턴스가 옵셔널이다. 사용하려면 옵셔널 바인딩 해줘야 함.
- 이건 넣어준 문자열 가지고 init 하는 거.
let baseUrl: String = "https://itunes.apple.com"
func fetchSearchResults(term: String) {
var components = URLComponents(string: baseUrl)
components?.path = "/search"
components?.queryItems = [
URLQueryItem(name: "media", value: "music"),
URLQueryItem(name: "entity", value: "song"),
URLQueryItem(name: "term", value: "\(term)")
]
guard let url = components?.url else { print("error") return }
}
fetchSearchResults(term: "Muse")
init?(url: URL, resolvingAgainstBaseURL: Bool)
resolvingAgainstBaseURL
: URLComponents가 absolute URL을 써야 하는지를 알려준다.true
면 URLComponents가 absolute URL을 갖게 된다.- 비교 대상이 될 base URL과 base URL을 기반으로 만든 URL을 정의해주고, base URL을 기반으로 만든 URL(
url
)을 매개변수로 넣어서 init 해준다.
let base = URL(string: "https://itunes.apple.com")
let url = URL(string: "/search", relativeTo: base)
let url2 = URL(string: "/search")
let absoluteComponents = URLComponents(url: url!, resolvingAgainstBaseURL: true)
print(absoluteComponents?.string)
// Optional("https://itunes.apple.com/search")
let aa = URLComponents(url: url2!, resolvingAgainstBaseURL: true) print(aa?.string)
// Optional("/search")
let relativeComponents = URLComponents(url: url!, resolvingAgainstBaseURL: false)
print(relativeComponents?.string)
// Optional("/search")
let bb = URLComponents(url: url2!, resolvingAgainstBaseURL: false)
print(bb?.string)
// Optional("/search")
맨 위의 그냥 init()
한 예제에서 볼 수 있듯, URLComponents
는 URL의 구성요소들(scheme, host, query, ...)를 모두 프로퍼티로 갖고 있다.
var scheme: String?
var user: String?
var host: String?
var port: Int?
var path: String
var query: String?
var queryItems: [URLQueryItem]?
var fragment: String?
var password: String?
타입이 String
또는 String?
인 애들은 .append
도 가능하다.
var components = URLComponents(string: "https://itunes.apple.com")
components?.path.append("/search")
let media = URLQueryItem(name: "media", value: "music")
let entity = URLQueryItem(name: "entity", value: "song")
let term = URLQueryItem(name: "term", value: "Muse")
components?.queryItems = [media, entity, term]
guard let url = components?.url else { return }
쿼리의 경우 query
를 사용할 수도 있고, queryItems
를 사용할 수도 있다.
- query를 사용한다면
components?.query = "media=music&entity=song&term=Muse"
와 같이 문자열로 직접 쿼리 전체를 넣어줄 수 있다.var query: String? { get set }
으로 정의되어 있고, 만약 percent encoding이 포함되어 있으면 getter에서 percent encoding을 다 빼버린다고 한다.- 이 프로퍼티를 설정하면, subcomponent나 component string이 percent encoding 되어있지 않으며, percent encoding을 추가할 것이라는 것을 의미한다.
Percent-Encoding/Decoding이란?
"https://example.com/music/bands/AC/DC"와 같은 URL을 만들고 싶을 때, /는 path를 구분하는 역할이 있는데, AC/DC를 쓰고 싶으면 어떻게 써야할까? (비슷한 예시로 쿼리에 쓰이는 ? 같은 것들이 있겠다.)
이럴 때 문자로 쓰고 싶은/를 인코딩해서 path를 나누는 /와 구분해야 하는데, URL은 Percent-Encoding 방식을 사용한다. %XX와 같은 형태로 표현하는데, XX에는 넣고 싶은 문자의 ASCII value를 16진수로 넣는다. 예를 들어 /를 인코딩 하는 경우, /의 ASCII value는 16진수로 표현하면 2F이기 때문에 %2F로 표현한다. 그래서 위의 URL을 Percent-Encoding하면 "https://example.com/music/bands/AC%2FDC"가 된다.
인코딩 하는 방법은 여기랑 여기를 참조해보자.
queryItems
를 사용한다면var queryItems: [URLQueryItem]? { get set }
으로 정의되어 있고,
배열 안의URLQueryItem의 순서 = 쿼리 문자열에 들어갈 순서 이다.- 각 URLQueryItem의 name은 unique하다고 보장할 수 없다 (중복으로 등장할 수 있다).
URLComponents
가 빈 query component를 가지고 있다면 빈 배열을 반환하고,
query component를 하나도 가지고 있지 않다면nil
을 반환한다.
URLQueryItem
URL의 Query 부분의 한 쌍의 name-value pair. 얘도 구조체로 구현되어 있다.init(name: String, value: String?)
이라서, name
과 value
는 둘 다 문자열 타입을 넣어주면 된다.
참고링크
Apple Developer - URLComponents
RayWenderwich URLSession Tutorial
Swift by Sundell - Constructing URLs in Swift
Stackoverflow - resolvingAgainstBaseURL
Percent-Encoding and Decoding
URL 처리 방법
'Swift-iOS > Swift' 카테고리의 다른 글
[Swift] Class vs Struct (feat. 메모리 구조, Array, CoW) (1) | 2022.09.22 |
---|
- Total
- Today
- Yesterday
- 참조 타입
- Cow
- ssh-add
- URLComponents
- SWIFT
- 값 타입
- 어플
- 야곰아카데미
- multipart/form-data
- 앱개발
- OSI
- ssh-configure
- HTTP message
- ssh-agent
- Endpoint
- URL
- 네트워크
- SSH
- TCP
- Github
- copy on write
- JSON
- 메모리 구조
- 스택
- HTTP Methods
- IOS
- 코딩
- 커리어스타터캠프
- 부트캠프
- URLQueryItem
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |