문제 발생 배경
URLComponents와 URLQueryItem에 넣어서 URL요청 했을 때 serviceKey가 틀리다는 응답이 나오는 문제
원인
문제의 원인은 Swift의 URL 디코딩과 Key값 문제
기본적으로 URL 인코딩을 진행할 때 Percent-encoding
을 진행 함
대표적인 Percent-encoding
예시 문자들
[":", "/", "?", "#", "[", "]", "@", "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=", "%"]
하지만 Key에 특수문자가 포함되어 있다면?? 특히 %가 포함되어 있다면??
var urlStrings = [":", "/", "?", "#", "[", "]", "@", "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=", "%"]
for ele in urlStrings {
var components = URLComponents(string: "https://example.com")
components?.queryItems = [URLQueryItem(name: "key", value: ele)]
print(components!.url!, ele)
}
원래문자 | Percent Encoding 문자
https://example.com?key=: :
https://example.com?key=/ /
https://example.com?key=? ?
https://example.com?key=%23 #
https://example.com?key=%5B [
https://example.com?key=%5D ]
https://example.com?key=@ @
https://example.com?key=! !
https://example.com?key=$ $
https://example.com?key=%26 &
https://example.com?key=' '
https://example.com?key=( (
https://example.com?key=) )
https://example.com?key=* *
https://example.com?key=+ +
https://example.com?key=, ,
https://example.com?key=; ;
https://example.com?key=%3D =
https://example.com?key=%25 %
보면 Swift의 URL 모든 문자에 대해서 Percent-encoding
하고 있지 않음.
왜? 생각해보니 각 문자는 URL에서 고유 역할을 하고 있었음
예를 들어 /
는 URLPath를 지정하는 특수 문자
하지만 고약하게도 공공데이터 API 인증키에는 /, +
가 들어있었음
위 문자는 Swift에서는 Encodign 해주지 않음...
해결책
Swift String에서 addingPercentEncoding(withAllowedCharacters: CharacterSet)
함수를 이용하면 됨
여기서 파라미터로 받는 CharacterSet은 여러개가 있지만 대표적인거 몇 개만 소개함urlUserAllowed (15개)
! $ & \ ( ) * + , - . ; = _ ~
urlPasswordAllowed (15개)
! $ & \ ( ) * + - . ; = _ ~
urlPathAllowed (17개)
! $ & \ ( ) * + - . / : = @ _ ~
urlHostAllowed (18개)
! $ & \ ( ) * + - . : ; = [ ] _ ~
urlFragmentAllowed (19개)
! $ & \ ( ) * + - . / : ; = ? @ _ ~
urlQueryAllowed (19개)
! $ & \ ( ) * + - . / : ; = ? @ _ ~
[위 특수문자 + 영어 알파벳 + 숫자] 를 제외하고는 모두 Percent-Encoding
한다는 뜻이다.
공공데이터 API 인증키에 특수문자가 들어간 경우는 .alphanumerics
을 사용하여 Encoding 하여 해결했음 -> 나름 하드코딩?
guard var urlString = urlComponents?.string else { throw NetworkError.URLComponentsError }
urlString += "?"
for queryItem in queryItems {
guard let value = queryItem.value?.addingPercentEncoding(withAllowedCharacters: .alphanumerics) else { throw NetworkError.QueryItemError }
urlString += queryItem.name + "=" + value + "&"
}
guard let url = URL(string: urlString) else { throw NetworkError.URLError }