URL 인코더/디코더

URL·쿼리 문자열을 퍼센트 인코딩·디코딩

텍스트URL 인코딩
모드

'+를 공백으로'를 켜면 디코딩 시 '+'가 공백으로 바뀝니다 — 부분 인코딩 모드는 입력 전체에, 전체 URL 모드는 쿼리 문자열('?' 이후) 부분에만 적용됩니다. Base64 토큰처럼 폼 데이터가 아니면 끄세요. 전체 URL 모드는 주소 구조를 지키려고 %2B·%26·%2F 같은 예약 문자 이스케이프를 풀지 않으므로, 리터럴 '+'가 필요하면 그 값만 부분 인코딩 모드로 디코딩하세요.

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로 바뀝니다. 이미 인코딩된 값은 다시 인코딩하지 마세요.

사용 방법

  1. 쿼리 값이나 경로 한 조각이면 「부분 인코딩」, 완성된 주소면 「전체 URL」을 고릅니다.
  2. 인코딩할 텍스트나 디코딩할 %문자열을 붙여 넣습니다.
  3. 방향 전환 버튼으로 결과를 되돌려 원문과 같은지 확인합니다.

자주 묻는 질문

한글 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 같은 이스케이프는 구조가 바뀌지 않도록 그대로 둡니다. 값만 떼어 내 부분 인코딩 모드로 디코딩하면 +로 돌아옵니다.

출처

기준일:

개인정보와 저장 방식