JavaScript / encodeURI와 encodeURIComponent 차이, URL 인코딩·디코딩
encodeURI()는 URL 구조에 쓰이는 구분 문자를 남겨 두고 인코딩하며, encodeURIComponent()는 검색어 같은 URL 구성요소의 값을 인코딩할 때 사용합니다. 쿼리 매개변수를 만들 목적이라면 원본 값을 URLSearchParams에 넣는 방식도 적합합니다. 디코딩은 대응하는 범위를 정해 한 번 수행하고, 잘못된 퍼센트 표현 때문에 생길 수 있는 URIError를 처리해야 합니다.
네 함수가 처리하는 범위
| 함수 | 주요 용도 | 구분 문자를 다루는 방식 |
|---|---|---|
encodeURI() |
전체 URI 표현을 다룰 때 | :, /, ?, &, =, # 등을 남김 |
encodeURIComponent() |
개별 경로 조각이나 쿼리 값 | 위 구분 문자도 값의 일부로 인코딩 |
decodeURI() |
전체 URI에 맞는 디코딩 | 인코딩된 일부 예약 구분 문자는 유지 |
decodeURIComponent() |
개별 값 디코딩 | 구분 문자를 포함한 퍼센트 표현을 해석 |
URI(Uniform Resource Identifier)는 자원을 식별하는 문자열이며 URL은 위치를 나타내는 형태의 URI입니다. 퍼센트 인코딩은 특정 문자를 바이트 값의 %HH 형태로 표현하는 방식입니다. 한글 등은 UTF-8 바이트에 대응하는 여러 퍼센트 표현으로 바뀝니다. 인코딩은 암호화가 아니므로 주소에 넣은 비밀번호나 비밀값을 감춰 주는 기능이 아닙니다.
encodeURI("a&b=c"); // "a&b=c"
encodeURIComponent("a&b=c"); // "a%26b%3Dc"
decodeURI("%26"); // "%26"
decodeURIComponent("%26"); // "&"
encodeURIComponent()가 모든 문자를 바꾼다는 설명은 정확하지 않습니다. 영문자·숫자와 - _ . ! ~ * ' ( ) 등은 그대로 남습니다. URL 구조용 구분 문자를 더 많이 인코딩한다는 점이 핵심입니다. 함수별 적용 범위는 MDN의 encodeURI() 문서와 encodeURIComponent() 문서에서 비교할 수 있습니다.
검색어를 URL에 넣는 전체 예제
아래 코드를 url-encoding.html에 UTF-8로 저장하고 브라우저에서 여세요. 실제 요청을 전송하지 않고 만들어진 URL과 원래 값으로 복원되는지를 출력합니다. 예제의 s는 검색어를 담는 매개변수 이름입니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>URL 값 인코딩과 디코딩</title>
</head>
<body>
<h1>검색어를 URL에 넣기</h1>
<label for="keyword">검색어</label>
<input id="keyword" value="C++ & 한글">
<button id="encode" type="button">변환</button>
<button id="restore" type="button">처음 값으로 복원</button>
<pre id="output"></pre>
<label for="encoded">디코딩할 값</label>
<input id="encoded" value="%ED%95%9C%EA%B8%80">
<button id="decode" type="button">디코딩</button>
<pre id="decoded"></pre>
<script>
const keyword = document.getElementById("keyword");
const output = document.getElementById("output");
function encode() {
try {
const value = keyword.value;
const component = encodeURIComponent(value);
const url = new URL("https://www.codingfactory.net/");
url.searchParams.set("s", value);
output.textContent = [
`encodeURI: ${encodeURI(value)}`,
`encodeURIComponent: ${component}`,
`다시 디코딩: ${decodeURIComponent(component)}`,
`검색 URL: ${url.href}`,
`URL에서 읽은 값: ${url.searchParams.get("s")}`
].join("\n");
} catch (error) {
output.textContent = error.name;
}
}
document.getElementById("encode").addEventListener("click", encode);
document.getElementById("restore").addEventListener("click", () => {
keyword.value = "C++ & 한글";
encode();
});
document.getElementById("decode").addEventListener("click", () => {
try {
document.getElementById("decoded").textContent =
decodeURIComponent(document.getElementById("encoded").value);
} catch (error) {
document.getElementById("decoded").textContent = error.name;
}
});
encode();
</script>
</body>
</html>
기본 검색어의 +와 &는 encodeURI() 결과에 그대로 남지만, encodeURIComponent()에서는 각각 %2B와 %26이 됩니다. 이를 그대로 ?s= 뒤에 붙일 값이라면 &가 다른 매개변수의 시작으로 해석되지 않게 해야 합니다. 전체 주소에 encodeURI()를 적용하는 것만으로는 검색어 내부의 구분 문자를 값으로 보호할 수 없습니다.
URLSearchParams는 값의 공백을 +로 직렬화할 수 있고, 실제 더하기 기호는 %2B로 표현합니다. 따라서 두 방식의 출력 문자열이 글자 단위로 같지 않아도, 파싱한 매개변수 값은 같은 원본 문자열이 될 수 있습니다. 위 예제에서 ‘URL에서 읽은 값’이 입력한 C++ & 한글과 같은지 확인하세요.
검색어만 a&b=1로 바꿔 다시 변환해 보고, ‘처음 값으로 복원’으로 되돌리세요. URL 구성 객체의 searchParams.set()에는 이미 인코딩한 문자열을 넣지 않습니다. 원본 문자열을 전달해야 중복 인코딩을 피할 수 있습니다. MDN의 URLSearchParams 설명은 값을 주고받을 때의 인코딩 처리를 구분합니다.
디코딩 실패와 중복 인코딩 구분하기
예제의 디코딩 입력 기본값은 ‘한글’로 복원됩니다. 이 값을 % 또는 %E0%A4%A로 바꾸면 완전한 인코딩 표현이 아니므로 URIError가 표시됩니다. 외부 URL이나 저장 데이터를 읽는 코드에서는 이런 오류가 다른 처리를 중단하지 않도록 실패 시의 동작을 정하세요.
const once = encodeURIComponent("&"); // "%26"
const twice = encodeURIComponent(once); // "%2526"
decodeURIComponent(twice); // "%26": 한 단계만 해석
decodeURIComponent("a+b"); // "a+b": +를 공백으로 바꾸지 않음
문자열에 %가 있다는 이유로 반복해서 디코딩하면 데이터의 뜻이 달라질 수 있습니다. 어느 단계가 인코딩을 담당했는지 확인하고 그 단계에 맞는 한 번의 변환을 수행하세요. 특히 URL의 쿼리를 이미 URLSearchParams.get()으로 읽었다면 일반적으로 디코딩된 값을 받으므로 곧바로 다시 decodeURIComponent()를 적용하지 않습니다.
단독 UTF-16 대리 코드 단위처럼 UTF-8로 표현할 수 없는 문자열을 인코딩할 때도 오류가 날 수 있습니다. 일반적인 한글 입력이 모두 문제라는 뜻은 아닙니다. 오류 메시지는 브라우저마다 다를 수 있으므로 예제에서는 메시지 전문 대신 error.name을 표시합니다. 오류 조건은 MDN의 URI 인코딩·디코딩 오류 설명에 정리돼 있습니다.
인코딩과 URL의 허용 여부 검사는 별개입니다
인코딩했다고 해서 주소의 연결 대상이나 프로토콜이 안전해지는 것은 아닙니다. 외부 입력으로 이동 주소를 정하는 기능이라면 허용할 프로토콜과 호스트를 별도로 판단해야 합니다. 화면에 문자열을 보여 주는 예제에서는 textContent를 사용해 인코딩 전후 문자열을 HTML로 해석하지 않도록 했습니다. 주소 생성, 값의 직렬화, 연결 대상 검사를 한 기능으로 혼동하지 마세요.




