URL 인코더/디코더
URL·쿼리 문자열을 퍼센트 인코딩·디코딩
처리 중…
0.0
멈춘 것이 아닙니다. 계산이 끝나면 결과가 표시됩니다.
URL 인코더/디코더 소개
URL에는 쓸 수 있는 문자가 정해져 있습니다. 한글, 공백, 그리고 URL 안에서 구조를 나타내는 ? & = # / 같은 문자를 값으로 넣으려면 %와 16진수 두 자리로 바꿔 적어야 하는데, 이것이 퍼센트 인코딩(URL 인코딩)입니다. 「한글」은 UTF-8 6바이트라 %ED%95%9C%EA%B8%80이 됩니다.
이 도구에는 모드가 두 개 있습니다. 「부분 인코딩」은 JavaScript의 encodeURIComponent로 쿼리 값 같은 조각 하나를 통째로 인코딩하고, 「전체 URL」은 encodeURI로 이미 완성된 주소에서 구조 문자는 남기고 나머지만 인코딩합니다. 어느 쪽을 고르느냐에 따라 결과가 크게 달라집니다.
RFC 3986이 나누는 세 종류의 문자
URL 문법을 정한 RFC 3986은 문자를 셋으로 나눕니다. 비예약 문자(영문자, 숫자, - . _ ~)는 언제나 그대로 써도 됩니다. 예약 문자는 구조를 표시하는 용도로 쓰이는데, 경로와 쿼리 등 큰 구획을 나누는 gen-delims(: / ? # [ ] @)와 구획 안에서 쓰이는 sub-delims(! $ & ' ( ) * + , ; =)가 있습니다. 나머지(공백, 한글, %, " 등)는 반드시 인코딩해야 합니다.
핵심은 예약 문자를 「구조」로 쓸 때는 그대로 두고, 「데이터」로 쓸 때는 인코딩해야 한다는 점입니다. 검색어가 「a&b」라면 ?q=a&b는 q=a와 b라는 두 파라미터로 읽히고, ?q=a%26b라고 써야 값 하나로 전달됩니다.
encodeURI와 encodeURIComponent의 차이
encodeURIComponent(부분 인코딩)는 영문자·숫자와 - _ . ! ~ * ' ( ) 만 남기고 모두 인코딩합니다. encodeURI(전체 URL)는 여기에 ; , / ? : @ & = + $ # 까지 남깁니다. 그래서 주소 전체를 부분 인코딩에 넣으면 https%3A%2F%2F… 처럼 링크로 쓸 수 없는 문자열이 되고, 반대로 쿼리 값을 전체 URL 모드로 인코딩하면 &나 +가 살아남아 값이 쪼개지거나 바뀝니다.
실제로 자주 틀리는 경우가 검색어 「C++ & Java」입니다. 주소 전체를 전체 URL 모드로 인코딩하면 ?q=C++%20&%20Java가 되어, 서버는 q를 「C 」(+가 공백으로 바뀜)로, 나머지를 별도 파라미터로 읽습니다. 값만 부분 인코딩하면 C%2B%2B%20%26%20Java가 되어 온전히 전달됩니다. 원칙은 「주소의 뼈대는 직접 조립하고, 끼워 넣는 값마다 부분 인코딩」입니다.
encodeURIComponent가 남기는 ! ' ( ) *는 RFC 3986에서 sub-delims에 속합니다. 대부분의 서버에서는 문제가 없지만, OAuth 1.0 서명처럼 RFC 3986 규칙을 엄격히 요구하는 곳에서는 이 다섯 글자도 %21 %27 %28 %29 %2A로 바꿔야 서명이 맞습니다.
공백은 %20인가 +인가
둘 다 맞지만 쓰이는 곳이 다릅니다. RFC 3986의 퍼센트 인코딩에서 공백은 %20입니다. + 를 공백으로 보는 규칙은 HTML 폼 전송 형식(application/x-www-form-urlencoded)에서 온 것으로, 브라우저가 폼을 GET으로 보낼 때 쿼리 문자열의 공백을 +로 적습니다. JavaScript의 URLSearchParams도 같은 규칙이라 q=「a b+c&d」를 q=a+b%2Bc%26d로 만듭니다.
이 도구의 인코딩은 encodeURIComponent를 따르므로 공백을 %20으로 적습니다(서버는 둘 다 공백으로 읽습니다). 디코딩할 때는 폼 규칙에 맞춰 +를 공백으로 바꿉니다. 부분 인코딩 모드에서는 입력 전체의 +를, 전체 URL 모드에서는 ? 뒤 쿼리 부분의 +만 바꾸고 경로의 +는 그대로 둡니다. 경로에서 +는 그냥 더하기 기호이기 때문입니다. 그래서 Base64 토큰처럼 +가 들어 있는 값이 인코딩 없이 쿼리에 들어갔다면, 받는 쪽에서는 이미 공백으로 바뀌어 있습니다. 반대로 폼 데이터가 아닌 문자열(Base64 토큰 등)을 디코딩할 때는 「+를 공백으로」 옵션을 끄면 +가 그대로 남습니다.
디코딩이 실패하는 경우
%뒤에 16진수 두 자리가 오지 않거나(예: 「100%」), 바이트를 모아도 올바른 UTF-8이 되지 않으면 오류가 납니다. 흔한 원인은 옛 한국어 사이트가 EUC-KR로 인코딩한 주소입니다. 「한글」은 EUC-KR로 %C7%D1%B1%DB인데, UTF-8로는 풀리지 않으므로 이 도구는 레거시 인코딩일 수 있다고 알리고, EUC-KR·Shift_JIS로 풀어 본 후보(EUC-KR: 한글)를 오류 아래에 함께 보여 줍니다. 또 이미 인코딩된 문자열을 한 번 더 인코딩하면 %가 %25로 바뀌어 a%20b가 a%2520b가 됩니다. 디코딩 결과에 %20 같은 게 남아 있다면 이중 인코딩을 의심하세요.
예시
쿼리 값 하나(부분 인코딩)
- 입력
a b&c=d- 결과
a%20b%26c%3Dd
&와 =가 인코딩되어 값 하나로 남습니다. 전체 URL 모드라면 a%20b&c=d가 되어 파라미터가 둘로 갈라집니다.
한글 주소(전체 URL)
- 입력
https://example.com/검색?q=a b&x=1#top- 결과
https://example.com/%EA%B2%80%EC%83%89?q=a%20b&x=1#top
:// ? & = # 는 구조로 남고, 한글과 공백만 바뀝니다. 같은 입력을 부분 인코딩하면 https%3A%2F%2F…로 시작해 링크로 쓸 수 없습니다.
+와 공백
- 입력
a+b%2Bc (부분 모드 디코딩)- 결과
a b+c
날 것의 +는 폼 규칙에 따라 공백이 되고, %2B로 적힌 것만 더하기 기호로 돌아옵니다.
이중 인코딩
- 입력
a%20b (인코딩)- 결과
a%2520b
%가 %25로 바뀝니다. 이미 인코딩된 값은 다시 인코딩하지 마세요.
사용 방법
- 쿼리 값이나 경로 한 조각이면 「부분 인코딩」, 완성된 주소면 「전체 URL」을 고릅니다.
- 인코딩할 텍스트나 디코딩할 %문자열을 붙여 넣습니다.
- 방향 전환 버튼으로 결과를 되돌려 원문과 같은지 확인합니다.
자주 묻는 질문
한글 URL을 복사하면 왜 %EC%… 로 바뀌나요?
주소창은 읽기 좋게 한글로 보여 주지만, 실제로 전송되는 URL은 UTF-8 바이트를 퍼센트 인코딩한 형태이기 때문입니다. 복사할 때 그 실제 형태가 붙여집니다. 이 도구로 디코딩하면 다시 한글로 볼 수 있습니다.
디코딩하니 오류가 납니다.
「형식이 올바르지 않다」면 % 뒤에 16진수 두 자리가 없는 경우입니다. 「UTF-8로 풀리지 않는 바이트」라면 EUC-KR처럼 UTF-8이 아닌 인코딩의 주소일 가능성이 큽니다. 이때 이 도구는 EUC-KR·Shift_JIS로 풀어 본 후보를 함께 보여 주니, 뜻이 통하는 쪽을 고르세요.
공백은 %20과 + 중 무엇을 써야 하나요?
경로에는 %20을 써야 합니다. 쿼리 문자열에서는 둘 다 공백으로 읽히는 경우가 대부분이지만, %20이 어디서나 안전합니다.
전체 URL 모드에서 %2B가 +로 바뀌지 않아요.
전체 URL 모드는 decodeURI를 쓰는데, decodeURI는 예약 문자(+ & = ? # 등)를 나타내는 %2B, %26 같은 이스케이프는 구조가 바뀌지 않도록 그대로 둡니다. 값만 떼어 내 부분 인코딩 모드로 디코딩하면 +로 돌아옵니다.
출처
- RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax
- WHATWG URL Standard — application/x-www-form-urlencoded
- MDN — encodeURIComponent()
- MDN — encodeURI()
기준일: