알아두면 좋은 내용
어떻게 사용하나요?
- 인코드/디코드 — 입력에
%XX형태가 하나라도 있으면 디코드, 없으면 인코드로 자동 판단합니다(수동 전환 가능). 인코드는 encodeURIComponent·encodeURI 중 고르고, 디코드는 “반복 디코드(최대 5회)”와 “+를 공백으로” 옵션이 있습니다. 한글·이모지 같은 비ASCII 문자는 글자별 UTF-8 바이트 표로 풀어 보여 줍니다. - URL 분해·편집 — scheme·host·port·path·query·fragment(사용자 정보가 있으면 user·password)로 나누고, 쿼리 파라미터를 표에서 추가·수정·삭제하면 URL이 즉시 다시 조립됩니다. 조립할 때 키와 값은 각각 encodeURIComponent로 인코딩됩니다.
- 추적 정리 — 7개 그룹 70개 추적 키 중 URL에 들어 있는 것을 찾아 그룹별·개별로 골라 지웁니다.
- 가이드 — 함수 비교, RFC 3986 문자 분류, 흔한 실수, 추적 키 사전을 도구 안에서 바로 볼 수 있습니다.
입력과 옵션은 이 브라우저에 자동 저장되어 새로고침해도 유지됩니다. 퍼센트 인코딩 자체는 RFC 3986이 정한 규칙이고, 브라우저의 encodeURIComponent는 이 규칙을 ECMAScript 명세대로 구현한 함수입니다.
함수별 인코딩 결과 비교
RFC 3986은 URL에 그대로 써도 되는 비예약 문자(unreserved)를 영문자·숫자와 - . _ ~ 네 기호로 정하고, : / ? # [ ] @(구분자)와 ! $ & ' ( ) * + , ; =(하위 구분자)를 예약 문자로 둡니다. 그런데 자바스크립트의 인코딩 함수들은 이 경계를 조금씩 다르게 그어서 같은 문자도 결과가 달라집니다. 아래 표는 각 함수를 실제로 실행한 결과입니다.
| 문자 | encodeURIComponent | encodeURI | URLSearchParams(폼) | RFC 3986 엄격 | escape() — 사용 금지 |
|---|---|---|---|---|---|
| (공백) | %20 | %20 | + | %20 | %20 |
| ! | ! | ! | %21 | %21 | %21 |
| # | %23 | # | %23 | %23 | %23 |
| % | %25 | %25 | %25 | %25 | %25 |
| & | %26 | & | %26 | %26 | %26 |
| ' | ' | ' | %27 | %27 | %27 |
| * | * | * | * | %2A | * |
| + | %2B | + | %2B | %2B | + |
| / | %2F | / | %2F | %2F | / |
| : | %3A | : | %3A | %3A | %3A |
| = | %3D | = | %3D | %3D | %3D |
| ? | %3F | ? | %3F | %3F | %3F |
| @ | %40 | @ | %40 | %40 | @ |
| ~ | ~ | ~ | %7E | ~ | %7E |
| é | %C3%A9 | %C3%A9 | %C3%A9 | %C3%A9 | %E9 |
| 한 | %ED%95%9C | %ED%95%9C | %ED%95%9C | %ED%95%9C | %uD55C |
| 😀 | %F0%9F%98%80 | %F0%9F%98%80 | %F0%9F%98%80 | %F0%9F%98%80 | %uD83D%uDE00 |
읽는 법: encodeURIComponent는 비예약 문자 외에 ! ' ( ) *도 그대로 둡니다. 브라우저 주소로는 문제없지만, 서명 계산처럼 비예약 문자만 남기도록 요구하는 규격에서는 이 다섯 글자가 서명 불일치를 일으킵니다 — OAuth 1.0(RFC 5849)과 AWS 서명 버전 4가 대표적이며, 이때는 ‘RFC 3986 엄격’ 열처럼 다섯 글자를 추가로 치환해야 합니다. URLSearchParams는 폼 규칙이라 공백을 +로, ~를 %7E로 바꾸고, escape()는 é·한글·이모지에서 표준과 전혀 다른 값을 만듭니다.
한글·이모지가 길어지는 이유 — UTF-8 바이트
ASCII 밖의 문자는 먼저 UTF-8 바이트열로 바뀌고, 각 바이트가 %와 16진수 두 자리로 적힙니다. 그래서 URL 길이는 글자 수가 아니라 바이트 수 × 3으로 늘어납니다.
| 글자 | 코드포인트 | UTF-8 바이트 | URL 인코딩 | 인코딩 후 길이 |
|---|---|---|---|---|
| 한 | U+D55C | 3 (ED 95 9C) | %ED%95%9C | 9자 |
| 국 | U+AD6D | 3 (EA B5 AD) | %EA%B5%AD | 9자 |
| 안 | U+C548 | 3 (EC 95 88) | %EC%95%88 | 9자 |
| 녕 | U+B155 | 3 (EB 85 95) | %EB%85%95 | 9자 |
| 韓 | U+97D3 | 3 (E9 9F 93) | %E9%9F%93 | 9자 |
| é | U+00E9 | 2 (C3 A9) | %C3%A9 | 6자 |
| 😀 | U+1F600 | 4 (F0 9F 98 80) | %F0%9F%98%80 | 12자 |
계산 예: “서울 맛집”는 한글 4글자와 공백 1개, 5글자지만 encodeURIComponent 결과는 %EC%84%9C%EC%9A%B8%20%EB%A7%9B%EC%A7%91로 39자가 됩니다(한글 4 × 9자 + 공백 3자). 검색어·상품명이 들어가는 URL이 금세 수백 자가 되는 이유이고, 로그나 문서에 붙여 넣을 때는 디코드한 형태가 훨씬 읽기 쉽습니다.
URL 길이 한도 — 어디서 잘리나
HTTP 표준은 URL 길이 상한을 정하지 않는 대신, 모든 송수신 측이 최소 8,000옥텟은 처리하도록 권고합니다. 실제로는 요청이 거치는 경로 중 가장 짧은 한도에서 잘리고, 서버마다 돌려주는 코드도 다릅니다.
| 구간 | 기본 한도 | 넘으면 |
|---|---|---|
| RFC 9110 권고 | 최소 8,000옥텟은 지원 | — |
| nginx (large_client_header_buffers 4 8k) | 요청 줄이 버퍼 하나(8KB)를 넘으면 거부 | 414 URI Too Long |
| Apache (LimitRequestLine) | 8,190바이트 | 414 URI Too Long |
| IIS 요청 필터링 | URL 경로 4,096바이트 · 쿼리 문자열 2,048바이트 | 404.14 · 404.15 |
| Chrome | 2MB | 탐색하지 않음 |
| Internet Explorer(지원 종료) | 2,083자 | — 흔히 말하는 2,000자 안전선의 출처 |
CDN·로드밸런서·WAF가 앞에 있으면 그 계층의 한도가 먼저 적용되므로, 긴 URL이 필요한 기능이라면 실제 운영 경로로 한 번 요청해 보고 결정하세요. 필터 조건이 수십 개인 검색처럼 데이터가 큰 요청은 POST 본문으로 옮기는 편이 안전합니다.
흔한 실수와 확인 방법
- 이중 인코딩 — “a b&c”를 두 번 인코딩하면
a%2520b%2526c가 됩니다. 한 번 디코드하면a%20b%26c로 여전히 인코딩이 남고, 반복 디코드를 켜면 2회 만에 원문 “a b&c”로 돌아옵니다. 서버 로그에%25가 보이면 이중 인코딩을 의심하세요. - 쿼리 값에 encodeURI 사용 — 값 속의
&·=·#가 그대로 남아 파라미터가 잘리거나 뒷부분이 fragment로 사라집니다. 값에는 encodeURIComponent를 쓰세요. - +와 공백 혼동 — 폼 형식으로 만든 URL을 decodeURIComponent로 풀면 +가 남고, 반대로 +가 들어간 값(전화번호 +82, 수식 1+1)을 인코딩 없이 붙이면 서버가 공백으로 읽습니다.
- 짝이 맞지 않는 % —
%뒤에는 16진수 두 자리가 와야 합니다. “50% 할인” 같은 원문을 디코드하면 URIError가 나므로, 이런 값은 먼저 인코딩해야 합니다. - escape()/unescape() 사용 — 오래된 코드에 남아 있는 경우가 많습니다. 위 비교표처럼 결과가 표준과 달라 서버에서 한글이 깨집니다.
추적 파라미터 — 이 도구가 지우는 키
광고 클릭·검색 결과·SNS 공유를 거치면 URL 뒤에 유입 경로를 기록하는 파라미터가 붙습니다. 페이지 내용과는 무관하지만 URL을 길게 만들고, 검색어(n_query)나 캠페인 이름이 그대로 드러나며, 같은 페이지가 분석 도구에서 서로 다른 주소로 집계되는 원인이 됩니다. 아래 표는 이 도구의 추적 키 사전 그대로이며, utm_*처럼 별표가 붙은 항목은 그 접두어로 시작하는 모든 키를 뜻합니다.
| 그룹 | 어디서든 추적으로 보는 키 | 해당 플랫폼 주소에서만 |
|---|---|---|
| Google Analytics·Ads | utm_* · gclid · gclsrc · dclid · gbraid · wbraid · _gl · gad · gad_source · gad_campaignid | — |
| Facebook·Meta | fbclid · fb_action_ids · fb_action_types · fb_source · fb_ref · _fbp · _fbc | — |
| 네이버 | n_media · n_query · n_rank · n_ad_group · n_ad · n_keyword · n_campaign_type · n_keyword_id · n_ad_group_id · n_match · n_ad_id · n_keyword_uniq · NaPm | — |
| 카카오·다음 | kakao_share_id · kakao_chat_id · kakao_ref · kakao_referer | taid · pid (kakao.com, daum.net, kakaocorp.com) |
| 쿠팡 | _xts_ · wPcid · wRef | src · spec · addtag · ctag · lptag · srcid · itime · pageType · pageValue (coupang.com) |
| Microsoft·Bing | msclkid | — |
| 기타 마케팅 | igshid · twclid · li_fat_id · yclid · piwik_* · mc_eid · mc_cid · mkt_tok · _branch_match_id · oly_anon_id · oly_enc_id · rb_clickid · ttclid · ScCid · ICID · WT.mc_id · epik · _kx · _hsenc · _hsmi · hsa_* | — |
정리 예: https://shop.example.com/item?id=42&q=a+b&utm_source=naver&utm_medium=cpc&n_media=27758&fbclid=IwAR0abc에서 추적 키 4개를 지우면 https://shop.example.com/item?id=42&q=a%20b가 됩니다. 남은 파라미터는 다시 조립되면서 encodeURIComponent 규칙으로 인코딩되므로, 폼 형식의 q=a+b는 같은 뜻의 q=a%20b로 바뀝니다. 값의 의미는 같지만 원문과 글자가 달라지니 비교할 때 참고하세요.
자주 묻는 질문
encodeURIComponent와 encodeURI는 어떻게 다른가요?
: / ? # @ & = + $ , 같은 URL 구분 문자까지 모두 인코딩합니다. encodeURI는 주소 전체가 들어온다고 보고 이 구분 문자를 그대로 둡니다.그래서 쿼리 값·경로 한 조각에는 encodeURIComponent, 이미 모양이 갖춰진 전체 URL에서 공백·한글만 바꿀 때는 encodeURI를 씁니다. 예:
encodeURIComponent("a&b") = a%26b(값으로 안전), encodeURI("a&b") = a&b(그대로 넣으면 & 뒤가 다른 파라미터로 잘립니다). 어느 쪽인지 애매하면 값 단위로 encodeURIComponent를 쓰는 편이 안전합니다.escape()는 왜 쓰면 안 되나요?
• 0~255 범위 문자는 UTF-8이 아니라 Latin-1 한 바이트로 바꿉니다 —
é → %E9(표준은 %C3%A9)• 그보다 큰 문자는
%uXXXX라는 비표준 형식이 됩니다 — 한 → %uD55C, 이모지는 서로게이트 두 개(%uD83D%uDE00)서버의 URL 디코더는
%u 형식을 모르므로 값이 깨지거나 오류가 납니다. 이 도구는 escape()를 제공하지 않고, 위 비교표에 결과만 참고로 실었습니다.한글 한 글자가 왜 %XX가 세 개인가요?
%XX를 붙입니다. UTF-8은 코드포인트 U+0080~U+07FF를 2바이트, U+0800~U+FFFF를 3바이트, U+10000 이상을 4바이트로 표현합니다. 한글 음절(U+AC00~U+D7A3)과 대부분의 한자는 3바이트 구간이라 %XX 세 개(9자), 이모지는 4바이트라 네 개(12자)가 됩니다. 예: 한(U+D55C) → 바이트 ED 95 9C → %ED%95%9C.공백은 +인가요, %20인가요?
%20이고 +는 그냥 더하기 기호입니다. 반면 HTML 폼 전송 형식(application/x-www-form-urlencoded, WHATWG URL 표준)은 공백을 +로 씁니다 — new URLSearchParams({a: " "}).toString()의 결과가 a=+인 이유입니다.문제는 디코딩입니다.
decodeURIComponent는 +를 공백으로 바꾸지 않으므로, 검색 URL(q=a+b)을 그대로 디코드하면 a+b가 남습니다. 이 도구는 디코드 옵션의 "+를 공백으로"를 켜면 폼 규칙으로 해석하고, 실제 + 기호는 %2B로 들어와 있으므로 디코드 후에도 +로 남습니다. 인코딩 결과는 항상 %20입니다.%2520처럼 이중 인코딩이 생기는 이유는?
%20, 그 %가 다시 %25가 되어 %2520이 됩니다. 프런트엔드가 인코딩한 값을 HTTP 클라이언트 라이브러리가 또 인코딩하거나, OAuth redirect_uri처럼 URL 안에 URL을 넣는 과정에서 흔히 생깁니다. 이 도구의 디코드 탭에서 "반복 디코드"를 켜면 값이 더 바뀌지 않을 때까지 최대 5회 풀어 원문을 확인할 수 있습니다. 근본 해결은 코드에서 인코딩을 값을 URL에 넣는 마지막 한 곳에서만 하도록 정리하는 것입니다.UTM 같은 추적 파라미터를 지워도 페이지가 정상 동작하나요?
utm_*는 Google Analytics 등이 유입 경로를 기록하는 값이고, fbclid·gclid는 광고 클릭 식별자라 페이지 내용과 무관합니다. 다만 일부 사이트는 캠페인 파라미터로 다른 랜딩 화면을 보여 주기도 하므로 정리한 URL을 한 번 열어 보고 공유하세요. 이 도구는 미리 등록한 추적 키만 제거 후보로 고르고, src·pid처럼 다른 사이트에서는 상품 ID 같은 기능용으로 쓰이는 키는 해당 플랫폼 주소(쿠팡·카카오)일 때만 추적으로 분류합니다.네이버 n_media·n_query 같은 파라미터는 무엇인가요?
n_media는 광고가 노출된 매체 ID, n_query는 사용자가 입력한 검색어, n_keyword·n_keyword_id는 광고주가 등록한 키워드와 그 ID, n_rank는 광고 노출 순위, n_ad_group·n_ad는 광고그룹·소재 ID, n_campaign_type은 캠페인 유형입니다. 광고 성과 분석용이라 지워도 페이지 동작에는 영향이 없고, n_query에 검색어가 그대로 들어 있으므로 공유 전에 지우는 편이 좋습니다.URL은 최대 몇 자까지 쓸 수 있나요?
OAuth 콜백 URL을 디코드할 때 주의할 점은?
code(1회용 인가 코드), state(CSRF 방지용 임의 값), 때로는 access_token이 들어 있습니다. 디코드해서 확인하는 것은 문제없지만 값을 수정하거나 남에게 공유하면 안 됩니다 — state를 바꾸면 검증이 실패하고, 토큰이 노출되면 계정이 탈취될 수 있습니다. 이 도구의 처리는 모두 브라우저 안에서 이뤄지지만 입력값은 편의를 위해 이 브라우저의 localStorage(youtil_url_encode_v1)에 저장되므로, 공용 PC에서 토큰이 든 URL을 다뤘다면 입력을 지우거나 DevTools → Application → Local Storage에서 해당 키를 삭제하세요.참고 자료
- RFC 3986 URI Generic Syntax(새 창)rfc-editor.org
- WHATWG URL Standard(새 창)url.spec.whatwg.org
- ECMAScript encodeURIComponent(새 창)tc39.es
- RFC 9110 §4.1 URI 길이(새 창)rfc-editor.org
- 계산 기준
- RFC 3986(URI 문법)·WHATWG URL 표준(폼 인코딩)·ECMAScript encodeURIComponent 정의 · URL 길이는 RFC 9110 권고와 서버 기본 설정
- 오류 제보
- 결과가 다르면 contact@youtil.kr로 입력 조건을 알려 주세요. 평일 기준 1~3일 안에 답변합니다.