[Swift] Swift의 URL 인코딩

[Swift] Swift의 URL 인코딩

Feat. 공공데이터 API 인증키 오류

문제 발생 배경

URLComponents와 URLQueryItem에 넣어서 URL요청 했을 때 serviceKey가 틀리다는 응답이 나오는 문제

원인

문제의 원인은 Swift의 URL 디코딩과 Key값 문제

기본적으로 URL 인코딩을 진행할 때 Percent-encoding을 진행 함

대표적인 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 }