URL에 인코딩이 필요한 이유
URL에는 제한된 ASCII 문자만 들어갈 수 있고, 그중 몇몇은 구조적 의미를 지닙니다. ?는 질의 문자열을 시작하고, &는 매개변수를 나누며, /는 경로를 가르고, #은 프래그먼트를 엽니다. 전달하려는 값 안에 그런 문자가 들어 있으면, "이건 데이터"와 "이건 문법"을 구별해 줄 무언가가 필요합니다.
퍼센트 인코딩이 그 장치입니다. 문자를 % 뒤에 그 바이트 값을 16진수로 붙인 형태로 바꿉니다. 공백은 %20, 앰퍼샌드는 %26, 슬래시는 %2F가 됩니다.
문자는 세 부류로 나뉩니다
- 비예약 문자: 영문자, 숫자, 그리고
- . _ ~. 인코딩이 필요 없고, 해도 합법이지만 의미가 없습니다. - 예약 문자:
: / ? # [ ] @ ! $ & ' ( ) * + , ; =. 구조적 역할을 맡습니다. 값 안에 들어 있을 때는 인코딩해야 하고, 구조로 쓰일 때는 그대로 두어야 합니다. - 그 밖의 전부: 공백, 악센트 문자, 한중일 문자, 이모지. 언제나 인코딩됩니다.
가운데 부류가 이 도구에 모드가 둘인 이유의 전부입니다.
전체 URL이냐 개별 구성요소냐
구성요소 모드는 예약 문자를 인코딩합니다. URL에 들어갈 하나의 값, 검색어, 매개변수, 파일명, 에 쓰세요. 여기에 URL 전체를 넣으면 URL이 망가집니다. URL을 URL로 만들어 주는 ://와 ?와 &가 전부 인코딩되기 때문입니다.
전체 URL 모드는 문법 문자를 보존하고 문자 그대로 들어갈 수 없는 것만 인코딩합니다. 공백이나 비ASCII 문자가 들어간 완전한 주소에 쓰세요. 다만 매개변수 값 안의 앰퍼샌드는 인코딩하지 않는데, 이는 한계가 아니라 실제 고장 지점입니다.
전형적인 버그가 있습니다. coffee & tea라는 검색어를 전체 URL 모드로 인코딩하면 ?q=coffee%20&%20tea가 되고, 받는 서버는 이를 q=coffee 와 빈 tea라는 두 개의 매개변수로 읽습니다. 각 값을 구성요소 모드로 인코딩한 뒤, 인코딩된 조각들로 URL을 조립하세요.
공백 문제
공백에는 인코딩이 두 가지 있고 서로 바꿔 쓸 수 없습니다. URL 경로에서 공백은 %20입니다. 질의 문자열에서는 오래된 HTML 폼 인코딩이 이를 +로 표현하며, 그러면 진짜 더하기 기호에는 %2B가 필요해집니다.
검색어의 더하기 기호가 이따금 사라지는 이유가 이것입니다. 문자 그대로 전송되었고 받는 쪽이 공백으로 해독한 것이죠. 더하기를 %2B로 퍼센트 인코딩해야 보존됩니다. 요즘 프레임워크는 대개 알아서 처리하지만, 손으로 만든 질의 문자열은 이걸 자주 틀립니다.
유니코드는 먼저 UTF-8을 거칩니다
퍼센트 인코딩은 문자가 아니라 바이트에 작용합니다. 그래서 ASCII 밖의 것은 먼저 UTF-8로 변환되고 나온 바이트마다 따로 인코딩됩니다. 악센트 문자 하나는 두 개의 이스케이프가 되고, 대부분의 한중일 문자는 세 개, 이모지는 네 개가 됩니다.
한글이나 중국어가 든 URL을 인코딩하면 원본보다 훨씬 길어 보이는 이유입니다. 비효율이 아니라 바이트당 이스케이프 하나이고, 그 문자들이 원래 여러 바이트일 뿐입니다.
도메인 이름은 다르게 동작합니다. 퍼센트 인코딩을 담을 수 없으므로 국제화 도메인은 퓨니코드를 써서 비ASCII 이름을 xn--로 시작하는 ASCII 형태로 바꿉니다. 인코딩은 호스트가 아니라 경로와 질의에 적용됩니다.
모든 처리는 표준 encodeURI와 encodeURIComponent 함수를 써서 브라우저에서 이루어지므로, 붙여넣은 내용은 전송되거나 저장되지 않습니다.