퍼센트 인코딩 검사기

퍼센트 인코딩은 규칙 하나가 아니라 여러 개이며, 약 열두 개의 문자에서 서로 어긋납니다. 이 페이지는 같은 텍스트를 encodeURIComponent, encodeURI, application/x-www-form-urlencoded 직렬화로 나란히 통과시키고, 어긋나는 문자를 표시하며, 문자마다 한 행씩 UTF-8 바이트와 RFC 3986 분류를 인쇄하고, 문자열이 몇 번 인코딩되었는지 말해 줍니다. 디코딩 쪽은 문자열을 엄격 또는 관대 규칙으로 읽고, 조용히 틀린 결과를 돌려주는 대신 어느 이스케이프가 어디서 깨졌는지 알려 줍니다.
모든 처리는 브라우저에서 로컬로 이루어집니다 — 데이터는 어떤 서버로도 업로드되지 않습니다.

퍼센트 인코딩이 뭔지 모르겠나요? 쉬운 말로 풀어 쓴 퍼센트 인코딩 설명 — 무엇인지, 공백이 %20 또는 +가 되는 이유, URL에 %25이 보일 때의 의미.
이 도구가 하는 일:
이스케이프 읽는 법:
encodeURIComponent
encodeURI
폼 (application/x-www-form-urlencoded)
입력 상자에서 Ctrl+Enter는 현재 모드를 실행합니다.
모드를 고르고 실행 버튼을 누르세요. 인코딩 & 검사는 텍스트의 세 가지 인코딩과 문자별 행을 보여 주고, 디코딩은 이스케이프를 다시 읽고 레이어를 셉니다. 자체 테스트 버튼은 문서화된 모든 샘플, 인쇄 가능 ASCII 전체 문자, 이 페이지 데이터 파일의 레이어 체인을 다시 실행하고 여기에 개수를 보고합니다.
인코더들이 서로 다르게 처리하는 부분:아래 문자만 세 방식이 다르게 취급합니다. 나머지는 셋 모두를 거쳐 변함없이 통과하거나, 셋 모두 같게 이스케이프됩니다.
모든 문자, 한 행씩:문자가 변환되는 UTF-8 바이트, RFC 3986이 매기는 클래스, 각 문맥이 쓰는 이스케이프. 네 문맥 열은 WHATWG URL 퍼센트-인코드 집합으로, 브라우저가 경로·쿼리·프래그먼트를 만들 때 실제로 쓰는 것입니다. 음영 행은 세 인코더가 다르게 취급하는 문자입니다.
이 페이지가 URL의 각 부분에 대해 이스케이프하는 정확한 집합:인코더 자체에서 출력하므로 이 표와 위 열을 채우는 코드가 어긋날 수 없습니다. 모든 집합은 C0 제어 문자(U+0000–U+001F)와 U+007E를 넘는 모든 코드 포인트도 이스케이프합니다. 그래서 비 ASCII 텍스트는 모든 문맥에서 항상 이스케이프됩니다.
이 집합들은 WHATWG 퍼센트-인코드 집합에서 파생되었으며, 손으로 조립할 때 안전해지는 두 곳에서 의도적으로 다릅니다. 쿼리 안에서는 해시를 이스케이프합니다. 원시 해시가 프래그먼트를 시작하기 때문입니다. 컴포넌트 집합은 encodeURIComponent가 이스케이프하는 문자(아포스트로피 포함)에 정확히 고정되고, 폼 집합은 URLSearchParams가 이스케이프하는 것에 고정됩니다. 따라서 위 표의 두 열은 재구현이 아니라 플랫폼 자신의 동작입니다. 그 목록은 사람들이 놀라는 결과도 설명합니다. 퍼센트 기호는 encodeURIComponent와 encodeURI가 이스케이프하지만 WHATWG 문맥 집합 어디에도 없어서, 문자별 표의 경로·쿼리·프래그먼트 열은 이를 그대로 둡니다. 손으로 조립한 URL의 원시 퍼센트 기호는 여전히 위험입니다. 표준은 입력이 먼저 검증되었다고 가정하기 때문입니다. 쿼리 안의 %41은 수많은 서버 프레임워크에게 하나의 A이며, 이 페이지의 엄격한 읽기도 고립된 %를 깨진 이스케이프로 취급합니다. 직접 %25로 이스케이프하는 것이 더 안전한 습관이며, 문자별 표에 두 표기가 나란히 있습니다.
이 문자열이 몇 번 인코딩되었는지:디코딩 한 번이 한 레이어입니다. 두 번 인코딩된 값은%25(한 번 인코딩이라면 남았을%20에서 시작하고, 세 번 인코딩된 값은%2525.
URL이 그대로 실을 수 있는 문자들:RFC 3986은 ASCII 범위를 세 그룹으로 나누며, 이스케이프 규칙은 그 분할을 기준으로만 이해됩니다.
예약 문자는 금지된 것이 아니라 이미 쓰이는 것입니다. URL 파서에게 의미가 있습니다. 목록에서 따라 나오는 규칙은 짧습니다 — 예약 문자를 데이터로 쓰려면 이스케이프하고, 비예약 문자는 결코 이스케이프할 필요가 없습니다.
세 방식과 각각이 그대로 두는 것:
목록은 각 방식이 ASCII 범위에서 이스케이프하지 않는 정확한 집합입니다. 자체 테스트가 인쇄 가능 ASCII 전체 문자를 이 기준으로 검사하므로, 표가 설명하는 인코더와 어긋날 수 없습니다.
이 페이지가 받아들이는 입력 크기와 그 이유:퍼센트 인코딩은 텍스트를 한 번 훑는 단일 패스라서, 이 사이트의 진법 변환기와 달리 비용이 제곱이 아니라 직선으로 자랍니다. 산술 자체가 문제가 되는 길이는 없습니다. 아래 상한이 있는 이유는 문자별 표를 문서 안에 그리기 때문이며, 백만 행의 표야말로 브라우저를 멈춥니다.
퍼센트 인코딩이란?

퍼센트 인코딩 — URL 인코딩이라고도 하고 오래된 문서에서는 이스케이핑이라고도 함 — 은 URL이 담을 수 없는 문자를 실을 수 있게 해 주는 규칙입니다. 퍼센트 이스케이프는 ASCII 문자 세 개입니다. 퍼센트 기호 하나와 16진수 두 자리이며, 바이트 하나를 나타냅니다. %20은 바이트 0x20, 즉 공백입니다. %2F는 바이트 0x2F, 즉 슬래시입니다. 그 외에는 아무것도 관여하지 않습니다. 이스케이프는 문자가 아니라 바이트를 이름 짓습니다.

바이트를 이름 붙이기 때문에 텍스트를 먼저 바이트로 바꿔야 하며, 바이트 인코딩은 UTF-8입니다. 긴 이스케이프의 출처가 그것입니다. 한자 하나는 UTF-8 3바이트라 이스케이프 세 개이고, 이모지 하나는 4바이트에 ASCII 문자 12개입니다.

30초 요약: 공백은 URL 값으로 %20이지만 폼 데이터에서는 +입니다. #은 %23, 리터럴 퍼센트 기호는 %25입니다. 이 페이지의 세 인코더는 글자, 숫자, -_.에는 동의하고 약 열두 개의 문장 부호 — 물결표 포함 — 에서 어긋납니다. 그래서 같은 텍스트에 encodeURIComponent와 encodeURI가 다른 문자열을 만들고, 둘 중 잘못된 쪽이 값을 쿼리 파라미터 둘로 쪼갤 수 있습니다.
URL에 왜 필요한가

URI는 ASCII 문자의 나열로 정의됩니다(RFC 3986 2.1절). 그 ASCII 범위 안에서 일부 문자는 예약되어 있습니다. URL의 구조를 만드는 문장 부호들입니다. 콜론과 슬래시 둘은 스킴을 구분하고, @ 기호는 사용자 정보를 호스트와 구분하고, 물음표는 쿼리를 시작하고, 해시는 프래그먼트를 시작하고, 앰퍼샌드는 쿼리 파라미터를 구분하고, 퍼센트 기호는 이스케이프를 시작합니다.

그러면 문제 하나와 허점 하나가 생깁니다. 문제: 그런 문자 중 하나를 스스로 포함하는 값 — "a&b"를 담은 검색어, 슬래시를 담은 파일 이름, 콜론을 담은 비밀번호 — 이 지금 그대로라면 자신이 실리는 URL의 구조를 바꿔 버립니다. 허점: 퍼센트 기호 자체라서, 인코더는 자기 알파벳을 그냥 확장하면 됩니다. %는 %25로 쓰이고, 뒤의 16진수 두 자리는 평범한 비예약 문자입니다.

실제 예시

이 페이지가 실행하는 세 인코더의 정확한 출력이며, 서로 다르게 동작하는 입력을 골랐습니다. 페이지는 이를 눈앞에서 다시 계산하고, 자체 테스트가 모든 행을 플랫폼 자체 구현과 다시 대조합니다.

세 방식과 각각이 맞는 때

대부분의 버그를 막는 짧은 요약: 하나의 파라미터 값에 들어가는 모든 것은 encodeURIComponent를 거칩니다(또는 폼 직렬화 도구를 거칩니다. 다른 안전 집합으로 같은 일을 합니다). 그리고 이미 조립된 완전한 URL은 이스케이프가 필요하면 encodeURI를 거칩니다. 이 둘 중 잘못된 것을 집는 것이 웹 코드에서 가장 흔한 퍼센트 인코딩 실수입니다.

URL이 실을 수 있는 문자, 전체 목록
플러스 기호 두 개, 의미 두 가지

공백은 인코딩이 텍스트의 행선지에 따라 달라지는 유일한 문자이며, 그 이유가 플러스 기호입니다.

함정은 그 표의 한가운데 있습니다. 폼 데이터에서 플러스 기호는 공백을 뜻하므로, 진짜 플러스 기호를 포함하는 값 — +44 20 7946 0958로 쓴 전화번호, 산술식, 플러스 태그가 붙은 이메일 주소 — 은 %2B로 이동해야 합니다. 리터럴 플러스 기호로 이동하면 수신자가 공백으로 읽어서 +44가 " 44"로 도착합니다.

두 번 인코딩됨, 식별 방법

이중 인코딩은 이미 이스케이프된 문자열이 다시 이스케이프될 때 일어납니다. 첫 이스케이프의 퍼센트 기호도 다른 문자와 같은 문자이므로 %25가 되고, 그 아래 레이어는 보이는 텍스트로 살아남습니다.

식별은 세기의 문제입니다. 문자열을 한 번 디코딩하고 결과를 보세요. 의도적으로 보이는 이스케이프 — 유효한 %XX 또는 공백 자리의 폼 플러스 기호 — 가 아직 있으면 한 레이어가 더 있습니다. %25 뒤에 16진수 두 자리가 오면 다음 레이어는 확실히 이스케이프입니다.

두 가지 실용적 참고. 첫째, 두 번 디코딩은 무해하지 않습니다. 보안 필터와 그 뒤의 코드가 서로 다른 문자열을 보게 만드는 잘 알려진 방법이라, 두 번째 디코딩 전에 도는 필터는 잘못된 텍스트를 필터링하는 것입니다. 둘째, 여기의 레이어 수는 문자열의 성질이지 의도에 대한 보증이 아닙니다. 정상적인 값이 한 레이어일 수 있고 깨진 값이 두 레이어일 수 있습니다.

이 페이지가 잡아내려고 만들어진 실수들
  • encodeURI로 인코딩된 값. 구분자가 살아남아, & 또는 =를 포함한 값이 반대편에서 파라미터 여러 개로 쪼개집니다. 입력 a&b=c에 대해 이 페이지 상단의 두 열을 비교하세요. 컴포넌트 열은 두 문자 모두 이스케이프하고 encodeURI 열은 유지합니다.
  • 폼 데이터로 보내진 리터럴 플러스 기호. 공백으로 도착합니다. %2B를 사용하세요.
  • "안전하라고" 두 번 이스케이프된 값. 더 안전하지 않습니다. 수신자가 한 번 디코딩하고도 퍼센트 이스케이프가 남은 문자열을 얻으며, 이는 잘못된 문자 집합과 같은 실패를 한 레이어 아래에서 일으킵니다.
  • 인코딩된 적 없는 것을 디코딩. 텍스트의 리터럴 퍼센트 기호 — 100% sure — 는 이스케이프가 아닙니다. 엄격한 읽기에서는 이 페이지가 보고하는 위치의 깨진 이스케이프이고, 관대한 읽기에서는 살아남습니다. 그래서 이 페이지의 기본값이 관대이고 그 반대가 아닙니다.
  • 이스케이프가 대문자여야 한다고 가정. 디코딩은 %2f와 %2F를 같은 바이트로 취급하며 둘 다 유효합니다. 대문자는 RFC 3986이 권장하는 정규 표기이자 다른 도구들이 만들어 내는 표기일 뿐입니다.
  • 퍼센트 인코딩이 텍스트를 안전하게 만든다고 가정. 퍼센트 인코딩은 URL 안에서 텍스트를 모호하지 않게 만들 뿐입니다. 인젝션을 막지 않고, 암호화가 아니며, 살균 도구도 아닙니다. 이스케이프는 텍스트가 들어갈 문맥을 위해, 조립 직전 마지막 단계여야 합니다.
빠른 답변

"URL에서 플러스 기호는 공백인가요?"

폼 데이터에서만, 그리고 폼 제출의 쿼리 부분에서만 그렇습니다. 경로, 프래그먼트, 일반 링크의 쿼리에서 플러스 기호는 문자 그대로의 플러스입니다. 그래서 이 페이지는 추측 대신 스위치를 두었습니다.

"왜 내 링크에 %25이 보이나요?"

퍼센트 기호가 인코딩되었기 때문입니다. 리터럴 퍼센트 기호가 인코더를 거쳤거나 — 100% sure가 100%25%20sure가 됩니다 — 이미 이스케이프를 포함한 문자열이 다시 인코딩된 것입니다. 문자열을 한 번 디코딩하고 결과를 보세요: %XX가 아직 있으면 한 레이어가 더 있습니다.

"쿼리 값 안의 슬래시가 %2F인지 /인지는 중요한가요?"

URL 파서에게는 아니오: 둘 다 슬래시로 서버에 도달하며 WHATWG 쿼리 집합은 슬래시를 그대로 둡니다. 상호 운용성에는 때론 예: 수많은 서버 프레임워크가 원시 쿼리 문자열에 자체 디코딩을 돌리고, 그중 일부는 이스케이프된 적 없는 문자에 대해서도 서로 어긋납니다. 값이 직렬화 도구가 아니라 손으로 조립될 때, 예약 문자를 명시적으로 이스케이프하는 것이 더 안전한 습관입니다.

"왜 폼 열은 다른 둘이 그대로 두는 물결표를 이스케이프하나요?"

폼 인코더의 안전 집합이 더 좁기 때문입니다: 문자, 숫자, -_.*뿐입니다. WHATWG 폼 인코더는 RFC 3986이 ~를 비예약으로 분류했어도 ~ ! ' ( )를 이스케이프합니다. 두 표기는 같은 값으로 디코딩되며, 차이는 문자열을 바이트 단위로 비교할 때만 중요합니다.

"같은 문자가 브라우저 주소 표시줄에 이스케이프 없이 보일 수 있나요?"

네, 그리고 그것은 URL이 아니라 주소 표시줄입니다. 브라우저는 URL을 읽기 쉬운 형태로 보여 주고 보낼 때 인코딩합니다. 링크의 中文은 전송 시 %E4%B8%AD%E6%96%87가 됩니다. 주소 표시줄에서 URL을 복사하면 브라우저에 따라 둘 중 어느 쪽이든 얻을 수 있습니다.

"여기 있는 이스케이프는 모두 유효해 보이는데 나오는 문자가 예상과 다릅니다. 왜죠?"

"잘못 디코딩됨"이라는 말 뒤에는 서로 다른 두 실패가 숨어 있고, 이 페이지는 이를 다르게 보고합니다. 첫째는 깨진 이스케이프입니다. 16진수 두 자리가 뒤따르지 않는 퍼센트 기호로, 끝의 100%나 %zz 같은 것입니다. 구문 문제라서 엄격한 읽기는 거부하고 위치를 말하며, 관대한 읽기는 리터럴 텍스트로 유지하고 나열합니다. 둘째는 바이트 자체는 완전히 유효하지만 그 바이트들이 유효한 UTF-8 문자를 이루지 못하는 이스케이프입니다. 단독 %FF나 중간에 끊긴 %E4%B8 같은 것입니다. 구문 문제가 아니므로 두 읽기 모두 텍스트를 돌려주고, 디코딩할 수 없는 바이트를 대체 문자 U+FFFD("�")로 바꾸며 그렇다고 경고를 남깁니다. 그 경고가 유용한 부분입니다. 대체 문자로 도착한 텍스트는 대개 보낸 쪽이 다른 문자 집합을 썼거나 문자열이 한 번 더 이스케이프되었다는 뜻입니다. 퍼센트 인코딩은 바이트를 이름 짓고, 이 페이지는 URL 표준처럼 그 바이트를 항상 UTF-8로 읽습니다.

"이 페이지가 하지 않는 일은?"

URL을 스킴, 호스트, 경로, 쿼리로 쪼개지 않습니다 — 그것은 URL 파서의 일입니다. 링크가 열어도 안전한지 검사하지 않고, 무엇에도 접속하지 않으며, 이스케이프 뒤의 바이트 문자 집합을 추측하지 않습니다. 항상 UTF-8로 읽으며, 그것이 URL 표준의 요구입니다.

"디코딩은 어떤 대체 문자를 쓰며, 그것이 맞다는 걸 어떻게 알죠?"

자체 테스트를 누르세요. 인쇄 가능 ASCII 전체 문자에 대해 페이지 자신의 인코더를 플랫폼의 encodeURIComponent, encodeURI, URLSearchParams와 비교하고, 위 표의 모든 예시를 다시 실행하고, 기록된 레이어 체인을 다시 확인한 뒤 검사 수를 여기에 보고합니다.

표준과 범위

퍼센트 인코딩은 RFC 3986 2.1절에 규정되어 있고, 예약·비예약 문자 정의는 2.2절과 2.3절, 대소문자 규칙은 6.2.2.1절에 있습니다. 브라우저가 URL을 만들 때 실제로 쓰는 집합은 WHATWG URL 표준에 있으며, 문맥별로 나뉩니다. 프래그먼트용, 쿼리용, 특수 쿼리용, 경로용, 사용자 정보용 집합과 단일 컴포넌트에 쓰이는 더 좁은 집합입니다. 플러스 기호를 포함한 폼 제출은 HTML 표준의 application/x-www-form-urlencoded에 있습니다. encodeURI와 encodeURIComponent 두 함수는 ECMAScript 자체의 일부입니다.

이 페이지는 경로·쿼리·프래그먼트 열을 자체 문맥별 집합으로 채우고, 모든 문맥의 정확한 목록을 결과 옆에 인쇄합니다. 그 집합들은 WHATWG 집합에서 파생되었으며 두 곳에서 의도적으로 다릅니다. 쿼리 안에서 해시를 이스케이프합니다(원시 해시가 프래그먼트를 시작하기 때문). 그리고 컴포넌트 집합은 encodeURIComponent가 이스케이프하는 문자에 정확히 고정됩니다. 플랫폼 함수 자체가 정답의 정의인 곳 — encodeURIComponent, encodeURI, 폼 직렬화 — 에서는 그 함수를 직접 호출하므로, 그 열들은 재구현이 아니라 브라우저 자신의 동작입니다.

페이지 문구는 이 사이트를 위해 쓰였습니다. 위 표의 모든 값은 로드 시점에 페이지 자신의 코드가 만들고 이 사이트의 테스트 도구에서 플랫폼 구현과 대조 검증되었습니다. 자체 테스트가 그 비교를 당신의 브라우저에서 반복합니다.