본문으로 바로가기
URL 인코딩
쿼리 값·한글·이모지 퍼센트 인코딩/디코딩. component·full.
블로그

URL 인코딩 실전: 값·전체 주소·+ 차이

값 하나는 component, 이미 구성된 URL은 full. 쿼리의 +가 공백이 되는 이유와 문자 그대로 +를 보존하는 방법을 실제 예제로 확인합니다.

URL 값 직접 비교하기 →
이 글의 3가지 포인트

2026-09-28 정정: 이전 설명의 “일반 URL에서는 +가 언제나 더하기”는 쿼리 파서에 적용되지 않습니다.

  1. ①
    값 하나는 component a=1&b=2 → a%3D1%26b%3D2. URLSearchParams.set을 쓰면 값 인코딩을 직접 조합하지 않아도 됩니다.
  2. ②
    쿼리에서 +는 주의 URLSearchParams("q=a+b").get("q")는 "a b"입니다. 문자 그대로 +를 넣을 때는 %2B가 필요합니다.
  3. ③
    입력과 주소는 다릅니다 이 도구의 변환 입력은 브라우저에서 처리되지만, 그 값을 URL에 실어 방문·공유하면 요청·기록에 남을 수 있습니다.
노트북 옆 종이 카드에 'URL 깨짐의 진짜 범인'이라고 적힌 URL 인코딩 안내 표지.

검색 URL의 값에 &가 들어가 다른 파라미터로 잘리거나, A+B를 다시 읽었더니 A B가 되는 경우가 있습니다. 첫 문제는 어디를 인코딩할지, 둘째는 쿼리를 어떤 방식으로 읽는지에 달렸습니다. 아래 예시를 사이트 도구와 브라우저 API로 직접 비교해 보세요.

component냐 full이냐, 먼저 범위를 정하라

자바스크립트에는 URL 인코딩 함수가 둘입니다. 차이는 예약문자를 건드리느냐입니다.

encodeURIComponent(component 모드)는 / ? # & = 같은 예약문자까지 전부 퍼센트 인코딩합니다. 값 하나, 경로 한 조각을 다룰 때 씁니다. encodeURI(full 모드)는 그 구분자들을 보존하며, 아직 퍼센트 인코딩되지 않은 주소 텍스트에 적용합니다. MDN도 둘을 용도로 구분합니다. 이 두 함수를 혼동하면 쿼리 값의 경계가 달라질 수 있습니다.

쿼리 값에 예약문자가 있으면 component

한 가지 재현하기 쉬운 오류는 파라미터 값 안에 &나 =가 들어갈 때입니다.

값으로 a=1&b=2를 넣는다고 해봅시다. PiPi Worlds URL 인코딩 도구에서 component로 인코딩하면 a%3D1%26b%3D2가 나옵니다. =와 &가 각각 %3D, %26으로 바뀌어 하나의 값으로 안전하게 들어갑니다. 반면 full로 인코딩하면 a=1&b=2 그대로입니다. 이 값을 ?x=a=1&b=2처럼 붙이면 서버는 &b=2를 별개 파라미터로 읽어 데이터가 깨집니다. 값에는 component가 답입니다.

인코딩 전 주소 텍스트 전체라면 full

반대 상황도 있습니다. 주소의 구분자는 만들었지만 한글·공백 등은 아직 퍼센트 인코딩하지 않은 경우입니다.

https://pipi-worlds.com/길?q=안녕을 full로 인코딩하면 https://pipi-worlds.com/%EA%B8%B8?q=%EC%95%88%EB%85%95이 됩니다. ://, /, ?, =는 그대로 두고 한글만 퍼센트로 바꿉니다. 여기에 component를 쓰면 https%3A%2F%2Fpipi-worlds.com...처럼 스킴 구분자까지 인코딩됩니다. 이미 인코딩된 URL은 full로 다시 처리하지 마세요. 예를 들어 a%20b는 a%2520b로 바뀌어 원래 의미가 달라집니다. 새 쿼리 값은 URL.searchParams.set으로 추가하세요.

’+‘가 공백이 되는 함정

디코딩에서 사람을 헷갈리게 하는 게 +입니다. 같은 a+b가 출처에 따라 다르게 풀립니다.

+의 의미는 “URL인지 아닌지”만으로 결정되지 않습니다. 특히 URL의 쿼리 문자열을 URLSearchParams로 읽으면 application/x-www-form-urlencoded 규칙에 따라 원문 +가 공백이 됩니다. HTML 폼뿐 아니라 브라우저의 쿼리 API도 이 규칙을 사용합니다.

decodeURIComponent("a+b")                 // "a+b"
new URLSearchParams("q=a+b").get("q")   // "a b"
new URLSearchParams("q=a%2Bb").get("q") // "a+b"
const url = new URL("https://example.com/search");
url.searchParams.set("q", "A+B & C");
url.searchParams.get("q")                // "A+B & C"
// 직렬화된 q 값의 +는 %2B, 공백은 +, &는 %26

따라서 이미 조합된 쿼리 q=A+B를 값 그대로 보존해야 한다면 원문 +를 %2B로 인코딩해야 합니다. 가장 간단한 방법은 문자열을 직접 이어 붙이지 않고 URL.searchParams.set에 인코딩 전 값을 주는 것입니다. 사이트 도구의 “+를 공백으로”는 Component 모드에서 쿼리·폼 값 한 조각을 수동 디코딩할 때만 켜세요. Full URL 모드에서는 경로를 훼손하지 않도록 +를 모두 보존하므로, URL 속 쿼리 해석은 URLSearchParams로 하거나 그 값만 복사해 Component 모드로 확인해야 합니다.

%25가 보이면, 원본 값과 다시 비교하기

검색어 한글+A B를 전달하고 싶다고 가정합니다. 아래는 실제 검색 요청을 보내지 않는 가상의 시험입니다. 사이트 변환 도구에 넣을 원본 값은 다음 한 줄입니다.

한글+A B

URL 인코딩 도구에서 Component · 인코딩을 고르면 아래 문자열이 됩니다. 디코딩으로 되돌리면 원본과 같아야 합니다.

%ED%95%9C%EA%B8%80%2BA%20B

하지만 이 결과를 searchParams.set에 다시 넣으면 안 됩니다. MDN의 퍼센트 인코딩 설명처럼 set에는 인코딩 전 값을 줍니다. 이미 인코딩한 값의 %도 문자로 취급해 다시 인코딩하기 때문입니다. 다음 JavaScript는 메모리 안에서 문자열만 만들고 읽습니다. 주소를 열거나 요청을 보내는 코드는 없습니다.

const original = "한글+A B";
const direct = new URLSearchParams();
direct.set("q", original);
const twice = new URLSearchParams();
twice.set("q", encodeURIComponent(original));

console.log(direct.toString());
// q=%ED%95%9C%EA%B8%80%2BA+B
console.log(twice.toString());
// q=%25ED%2595%259C%25EA%25B8%2580%252BA%2520B

console.log(new URLSearchParams(direct.toString()).get("q"));
// 한글+A B
console.log(new URLSearchParams(twice.toString()).get("q"));
// %ED%95%9C%EA%B8%80%2BA%20B

직접 넣은 쿼리는 읽었을 때 한글+A B로 돌아옵니다. 미리 인코딩한 쿼리는 읽어도 퍼센트 문자열이 남아 원본과 다릅니다. 공백이 도구에서는 %20, 쿼리 직렬화에서는 +로 보이는 차이와 원본 값이 달라지는 문제를 구분하세요.

%25를 무조건 지우면 안 됩니다

원본이 할인 20%라면 쿼리에 %25가 생기는 것이 맞습니다. 더 나아가 원본이 문자 그대로 a%20b일 수도 있습니다. 그 경우 q=a%2520b를 한 번 읽어 a%20b로 돌려주는 것이 올바릅니다. “한글이 나올 때까지 디코딩”하거나 %25를 일괄 치환하면 의도된 문자를 바꿀 수 있습니다.

  1. 원본을 먼저 적습니다. 보내려던 값이 한글인지, 퍼센트 문자를 포함한 문자열인지 구분합니다. 비밀값 대신 위 시험값을 쓰세요.
  2. 인코딩을 맡는 단계를 확인합니다. 직접 값 한 조각을 변환할지, URLSearchParams에 원문을 맡길지 정합니다. 두 경로를 무심코 겹치지 않습니다.
  3. 읽은 값을 원본과 대조합니다. 위 예제의 get("q") 결과를 확인합니다. 기대한 원본과 다르면 값을 만드는 쪽을 고치고, 이유 없이 수신 쪽에 추가 디코딩을 붙이지 않습니다.

이 왕복 비교는 이 예제의 문자열 보존 검사입니다. 실제 서버의 파서·서명 규칙·업무 처리까지 검증하거나 링크의 안전성을 보증하지 않습니다.

도구로 확인할 때와 실제 링크를 만들 때

PiPi Worlds URL 인코딩 도구는 component/full 범위를 고르고 + 해석을 비교하는 수동 변환기입니다. 전체 URL 모드의 encodeURI는 &, #, ? 같은 구분자를 남기므로, 아직 만들지 않은 쿼리 값까지 안전하게 조합해 주지는 않습니다. 새 링크를 만들 때는 위처럼 URL과 searchParams.set을 쓰세요. 변환 입력은 브라우저 안에서 처리되지만 페이지 분석 비컨은 별도로 동작할 수 있고, 값을 실제 URL에 넣어 방문하면 그 주소가 요청·기록에 남을 수 있습니다. 잘린 % 시퀀스는 오류로 표시합니다.

자주 묻는 질문

encodeURIComponent와 encodeURI는 뭐가 다른가요?
encodeURIComponent는 `/ ? # & =` 같은 예약문자까지 퍼센트 인코딩합니다. 쿼리 값 하나나 경로 한 조각에 씁니다. encodeURI는 구분자를 보존하므로 아직 퍼센트 인코딩되지 않은 URL 텍스트에만 쓰세요. 이미 `%20` 등이 있다면 다시 인코딩하지 마세요.
쿼리 파라미터 값에는 어느 것을 써야 하나요?
encodeURIComponent입니다. 값 안에 `&`나 `=`가 들어 있으면 encodeURI는 그대로 두기 때문에 파라미터 경계가 깨집니다. 예를 들어 `a=1&b=2`를 값으로 넣을 때 encodeURIComponent는 `a%3D1%26b%3D2`로 인코딩해 하나의 값으로 안전하게 전달합니다.
디코딩했더니 '+'가 공백이 됐어요. 왜죠?
쿼리 문자열을 URLSearchParams 같은 form-urlencoded 파서로 읽으면 +가 공백이 됩니다. 예를 들어 q=a+b는 a b, q=a%2Bb는 a+b로 읽힙니다. 반면 decodeURIComponent("a+b")는 +를 그대로 둡니다. 이 도구에서 쿼리/폼 값을 직접 풀 때만 “+를 공백으로”를 켜세요.
한글이나 이모지를 URL에 넣으면 어떻게 되나요?
UTF-8 퍼센트 시퀀스로 인코딩됩니다. 예를 들어 '안녕'은 `%EC%95%88%EB%85%95`이 됩니다. 디코딩하면 원래 글자로 정확히 돌아오고, 잘린 `%` 같은 깨진 시퀀스는 깨진 글자 대신 오류로 표시됩니다.
입력한 값이 서버로 전송되나요?
변환할 때 붙여 넣은 입력 자체는 이 도구가 변환 요청으로 서버에 보내지 않습니다. 하지만 페이지 방문·성능 정보는 Cloudflare Web Analytics 비컨이 처리할 수 있으며, 값을 URL에 담아 방문하거나 공유하면 쿼리 값이 요청·로그·브라우저 기록에 남을 수 있습니다. 비밀값은 넣지 마세요.

출처

검토 기록: Codex AI가 WHATWG URL 표준·MDN 문서와 사이트 변환 함수를 대조한 기술 검토. 외부 전문가 검토나 브라우저별 전수 시험은 아닙니다.

이번 검토 범위 — 이번 후속 작업은 첫 FAQ 답변의 원문 형식과 실제 표시 복구에 한정합니다. 본문 예제·기술 설명·출처 확인일은 앞선 검토 기록을 유지하며 전면적인 재검토나 도구 기능·접근성 전체 검수를 뜻하지 않습니다.

확인 내역과 한계 펼쳐 보기

2026-10-15에는 MDN URLSearchParams와 WHATWG 직렬화 규칙을 다시 확인하고, 한글 시험값을 직접 넣은 경우와 미리 인코딩해 넣은 경우의 왕복 결과를 비교했습니다. %25 자체를 오류로 단정하지 않는 예외도 검산했습니다. 앞선 2026-09-28 검토는 다음과 같습니다. encodeURIComponent, encodeURI, URLSearchParams의 + 동작을 Node.js 및 도구 단위 테스트로 재현하고, KO/EN/JA 글·도구 설명을 함께 교정했습니다. URL을 실제 서버에 보내는 동작은 이 예시에서 수행하지 않았습니다. 2026-10-15 FAQ 표시 복구: 첫 답변의 # 뒤가 YAML 주석으로 누락되던 원문 형식을 수정해 기존 의도한 문장 전체를 되살렸습니다. 본문·예제·출처 확인일은 바꾸지 않았으며 새 사실 검증이나 도구 기능 변경을 뜻하지 않습니다.

이 글에서 더한 것 — 한글·더하기·공백을 포함한 원본 값의 직렬화·재읽기를 대조해 이중 인코딩을 찾는 복사 가능한 실험과 확인 순서를 더했습니다. 브라우저 URLSearchParams와 사이트 도구를 같은 입력으로 비교해, 쿼리의 + 처리 및 안전한 값 추가 방식을 재현합니다. 전체 URL 모드의 한계와 입력 데이터의 처리 범위도 밝힙니다.

검토 기록은 기재된 확인 내역이며, 독립적인 정확성 보증은 아닙니다.

도구로 돌아가기 →