지식 · 텍스트, 데이터, 보안
URL 인코딩이란 무엇인가요?
핵심 답변
URL 인코딩, 정식 명칭으로 퍼센트 인코딩은 URL에서 허용되지 않거나 특별한 의미를 갖는 문자를 바이트마다 %와 16진수 두 자리로 바꾸는 방식입니다. 공백은 %20, &는 %26이 되고, ASCII가 아닌 텍스트는 먼저 UTF-8로 변환되므로 "é"는 %C3%A9가 됩니다. 문자열 "a b&c"는 a%20b%26c로 인코딩됩니다.
URL에 인코딩이 필요한 이유
RFC 3986은 URL에 쓸 수 있는 문자를 소수의 ASCII 문자로 제한하며, 그중 일부는 맡은 역할이 있습니다. ?는 쿼리의 시작, &는 매개변수 구분, =는 이름과 값의 구분, #는 프래그먼트의 시작, /는 경로 구분입니다. 값에 이런 문자가 들어가면 URL의 의미가 모호해집니다.
"salt & pepper"를 검색한다고 해 봅시다. ?q=salt & pepper처럼 그대로 쓰면 서버는 값이 salt 인 매개변수 q와, pepper라는 이름의 빈 매개변수를 하나 더 받습니다. ?q=salt%20%26%20pepper로 인코딩하면 하나의 값으로 도착합니다.
어떤 문자를 인코딩할까
| 분류 | 문자 | 규칙 |
|---|---|---|
| 비예약 문자 | A–Z a–z 0–9 - . _ ~ | 인코딩 불필요 |
| 예약 문자(gen-delims) | : / ? # [ ] @ | 값 안에서 쓸 때 인코딩 |
| 예약 문자(sub-delims) | ! $ & ' ( ) * + , ; = | 값 안에서 쓸 때 인코딩 |
| 그 밖의 모든 문자 | 공백, ", <, >, %, ASCII 외 문자 | 항상 인코딩 |
인코딩은 문자가 아니라 바이트 단위로 이루어집니다. ASCII가 아닌 텍스트는 먼저 UTF-8로 변환되고, 각 바이트가 하나의 %XX 묶음이 됩니다.
| 입력 | UTF-8 바이트 | 인코딩 결과 |
|---|---|---|
| 공백 | 20 | %20 |
| & | 26 | %26 |
| % | 25 | %25 |
| é | C3 A9 | %C3%A9 |
| 日本 | E6 97 A5 E6 9C AC | %E6%97%A5%E6%9C%AC |
16진수는 대소문자를 구분하지 않으므로 %c3%a9와 %C3%A9는 같은 뜻이지만, RFC 3986은 일관성을 위해 대문자를 권장합니다.
%20과 +: 공백에 대한 두 가지 규칙
RFC 3986이 정의한 퍼센트 인코딩은 공백을 항상 %20으로 씁니다. HTML 양식 전송은 이와 비슷한 application/x-www-form-urlencoded 형식을 쓰는데, 여기서는 공백이 +가 되고 실제 더하기 기호는 %2B가 됩니다.
/files/my report.pdf같은 경로에는%20을 쓰세요. 경로의+는 그냥 더하기 기호입니다.- 쿼리 문자열에서는 양식 인코딩을 해석하는 서버가
+를 공백으로 읽습니다.%20은 두 규칙 모두에서 이해되므로 확실하지 않을 때 더 안전합니다. +84같은 전화번호처럼 값에 실제+가 들어갈 수 있다면%2B로 보내야 합니다. 그렇지 않으면 공백으로 도착합니다.
JavaScript의 encodeURIComponent와 encodeURI
| 함수 | 인코딩하지 않는 문자 | 용도 |
|---|---|---|
encodeURIComponent() | A–Z a–z 0–9 - _ . ! ~ * ' ( ) | 단일 값: 쿼리 매개변수 하나 또는 경로 세그먼트 하나 |
encodeURI() | 위 문자 + ; , / ? : @ & = + $ # | 이미 구조가 갖춰진 URL 전체 |
URLSearchParams | 양식 인코딩 규칙(공백은 +) | 쿼리 문자열 만들기·읽기 |
encodeURIComponent("a b&c")는 a%20b%26c를 반환하지만, encodeURI("a b&c")는 &를 구분자로 간주해 a%20b&c를 반환합니다. 흔한 버그는 매개변수 값에 encodeURI를 호출해 &, =, #가 그대로 통과하면서 값이 쪼개지는 경우입니다.
new URLSearchParams({ q: "a b&c" }).toString()은 q=a+b%26c를 만듭니다. 표준 서버에서는 두 형식 모두 같은 값으로 디코딩됩니다.
흔한 실수
- 이중 인코딩: 이미 인코딩된 텍스트를 다시 인코딩하면
%자체가%25로 인코딩되어%20이%2520이 됩니다. 각 값은 URL에 넣는 시점에 정확히 한 번만 인코딩하세요. - 컴포넌트용 함수로 URL 전체를 인코딩:
https://가https%3A%2F%2F가 되어 링크가 더 이상 동작하지 않습니다. - 라우팅 전에 디코딩: 경로를 매칭하기 전에
%2F를/로 디코딩하는 서버는 다른 경로를 읽도록 속을 수 있습니다. - 구식 문자 집합 가정: 일부 오래된 시스템은 "é"를
%E9(Latin-1)로 인코딩했습니다. 최신 브라우저와 WHATWG URL Standard는 UTF-8을 씁니다.
자주 묻는 질문
URL에서 공백이 왜 %20인가요?
URL에는 공백을 쓸 수 없으므로 %와 그 바이트 값을 16진수로 쓴 것으로 바꿉니다. 공백의 ASCII 코드는 10진수로 32이고, 16진수로 20입니다.
URL의 %2F는 무슨 뜻인가요?
%2F는 인코딩된 슬래시입니다. 값 안에 /를 넣어도 경로 구분자로 읽히지 않게 합니다.
URL 인코딩과 Base64는 같은 건가요?
아니요. URL 인코딩은 안전하지 않은 문자만 바꾸고 나머지는 읽을 수 있게 남겨 둡니다. Base64는 모든 바이트를 64개 문자로 바꾸며, URL 안전 변형을 쓰지 않으면 표준 문자인 +와 / 자체도 URL 인코딩이 필요합니다.
악센트 문자나 한글도 인코딩해야 하나요?
네, 실제 요청에서는 그렇습니다. 브라우저 주소창에는 읽기 쉽게 표시되지만, 실제로는 日이 %E6%97%A5가 되는 식으로 UTF-8 퍼센트 인코딩 형식으로 전송됩니다.