JavaScript7분 분량

JavaScript에서 URL을 디코딩하는 방법 (완벽 가이드)

JavaScript는 개별 URI 구성 요소를 디코딩하는 decodeURIComponent()와 완전한 URI를 디코딩하는 decodeURI()를 제공합니다. 잘못된 형식의 URI는 try-catch로 처리하세요. URL과 URLSearchParams API는 URL 파싱을 위한 더 안전한 대안을 제공합니다.

decodeURIComponent() — URI 구성 요소 디코딩

decodeURIComponent()는 JavaScript에서 URL 인코딩된 문자열을 디코딩하는 기본 함수입니다. 모든 percent-encoding 시퀀스를 디코딩하여 원래 문자로 되돌립니다. 쿼리 파라미터 값, 경로 세그먼트, 또는 URI의 개별 구성 요소를 디코딩할 때 이 함수를 사용하세요.

// 기본 디코딩
console.log(decodeURIComponent('hello%20world'));
// "hello world"

console.log(decodeURIComponent('price%3D10%26qty%3D2'));
// "price=10&qty=2"

// 유니코드 문자 디코딩
console.log(decodeURIComponent('caf%C3%A9'));
// "cafe" (악센트 포함)

console.log(decodeURIComponent('%E4%B8%AD%E6%96%87'));
// 한자

// URL에서 쿼리 파라미터 값 디코딩
const url = 'https://example.com/search?q=C%2B%2B%20%26%20Java';
const params = url.split('?')[1];
const value = params.split('=')[1];
console.log(decodeURIComponent(value));
// "C++ & Java"

decodeURIComponent()%2F (/)와 %3F (?) 같은 예약 문자를 나타내는 시퀀스를 포함하여 모든 percent-encoding 시퀀스를 디코딩합니다. 이는 개별 URI 구성 요소를 다룰 때는 올바른 동작이지만, 전체 URL에 적용하면 문제가 발생할 수 있습니다.

decodeURI() — 완전한 URI 디코딩

decodeURI()는 URI의 구조를 유지하면서 완전한 URI를 디코딩합니다. decodeURIComponent()와 달리, %2F (/), %3F (?), %23 (#), %26 (&)와 같이 예약된 URI 문자를 나타내는 시퀀스는 디코딩하지 않습니다.

// decodeURI는 URI 구조를 유지합니다
console.log(decodeURI('https://example.com/my%20page?q=hello%20world'));
// "https://example.com/my page?q=hello world"
// 공백은 디코딩되지만 /, ?, =는 유지됩니다

// 전체 URL에 decodeURIComponent를 적용한 경우와 비교
console.log(decodeURIComponent('https%3A%2F%2Fexample.com%2Fpath'));
// "https://example.com/path" - URL 전체가 인코딩된 경우 올바르게 디코딩됩니다

// decodeURI는 예약 문자 시퀀스를 디코딩하지 않습니다
console.log(decodeURI('path%2Fto%2Ffile'));
// "path%2Fto%2Ffile" - /가 예약 문자이므로 %2F는 디코딩되지 않습니다
console.log(decodeURIComponent('path%2Fto%2Ffile'));
// "path/to/file" - %2F가 디코딩됩니다

URL을 (예를 들어 표시 목적으로) 구조를 변경하지 않고 더 읽기 쉽게 만들고 싶을 때 decodeURI()를 사용하세요. 대부분의 프로그래밍 사용 사례에서는 개별 구성 요소에 적용하는 decodeURIComponent()가 필요할 것입니다.

잘못된 형식의 URI 처리

decodeURI()decodeURIComponent()는 모두 유효하지 않은 percent-encoding 시퀀스를 만나면 URIError를 던집니다. 이는 단독 퍼센트 기호, 불완전한 시퀀스, 또는 유효하지 않은 UTF-8 바이트 시퀀스가 있을 때 발생합니다. 사용자가 입력했거나 외부에서 가져온 URL을 다룰 때는 항상 디코딩 작업을 try-catch 블록으로 감싸세요.

// 다음은 URIError: URI malformed를 던집니다
try {
  decodeURIComponent('%');         // 단독 퍼센트
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

try {
  decodeURIComponent('%2');        // 불완전한 시퀀스
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

// 안전한 디코딩 함수
function safeDecode(str) {
  try {
    return decodeURIComponent(str);
  } catch (e) {
    console.warn('Failed to decode:', str);
    return str; // 실패 시 원본 문자열 반환
  }
}

// 디코딩 전에 잘못된 형식의 퍼센트 시퀀스를 수정
function fixAndDecode(str) {
  // 단독 %를 %25(인코딩된 퍼센트 기호)로 치환
  const fixed = str.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
  return decodeURIComponent(fixed);
}

console.log(fixAndDecode('100% complete'));
// "100% complete"

URL API 사용하기 (권장)

최신 URLURLSearchParams API는 URL을 파싱하고 디코딩하는 더 안전하고 구조화된 방법을 제공합니다. 인코딩과 디코딩을 자동으로 처리하여 오류 발생 위험을 줄여줍니다.

// URL을 파싱하고 구성 요소에 접근 (자동으로 디코딩됨)
const url = new URL('https://example.com/path%20here?q=hello%20world&lang=en');

console.log(url.pathname);  // "/path here" (디코딩됨)
console.log(url.search);    // "?q=hello%20world&lang=en" (원본)

// URLSearchParams는 파라미터 값을 자동으로 디코딩합니다
console.log(url.searchParams.get('q'));     // "hello world"
console.log(url.searchParams.get('lang')); // "en"

// 모든 파라미터를 순회
for (const [key, value] of url.searchParams) {
  console.log(key, '=', value);
}
// q = hello world
// lang = en

// URLSearchParams는 +를 공백으로 처리합니다 (폼 인코딩)
const formParams = new URLSearchParams('q=hello+world&lang=en');
console.log(formParams.get('q'));  // "hello world"

// 자동 인코딩으로 URL 구성하기
const newUrl = new URL('https://example.com/search');
newUrl.searchParams.set('q', 'C++ & Java');
newUrl.searchParams.set('page', '1');
console.log(newUrl.toString());
// "https://example.com/search?q=C%2B%2B+%26+Java&page=1"

흔한 디코딩 실수

실수 1: decodeURIComponent()로 전체 URL 디코딩하기. URL에 인코딩된 예약 문자가 포함되어 있으면 URL 구조가 깨질 수 있습니다. 쿼리 값에 있는 %2F/로 바뀌면서 URL의 의미가 달라질 수 있습니다.

실수 2: 이중 디코딩. 문자열이 이미 한 번 디코딩되었다면, 다시 디코딩하면 예상치 못한 결과나 오류가 발생할 수 있습니다. 예를 들어 문자열 %2520은 먼저 %20으로, 그다음 공백으로 디코딩됩니다. 인코딩이 한 단계만 되어 있다고 예상한다면, 이중 디코딩은 데이터를 손상시킵니다.

// 이중 디코딩 문제
const encoded = '%2520'; // 이것은 인코딩된 %20입니다
console.log(decodeURIComponent(encoded));  // "%20" (올바름 - 한 단계)
console.log(decodeURIComponent(decodeURIComponent(encoded)));  // " " (이중 디코딩됨!)

// 디코딩하기 전에 문자열에 디코딩이 필요한지 확인
function needsDecoding(str) {
  return str !== decodeURIComponent(str);
}

실수 3: + 기호를 처리하지 않기. decodeURIComponent()+를 공백으로 변환하지 않습니다. form-urlencoded 데이터를 디코딩한다면, 먼저 +를 공백으로 치환하거나 이를 자동으로 처리하는 URLSearchParams를 사용해야 합니다.

// decodeURIComponent는 +를 공백으로 디코딩하지 않습니다
console.log(decodeURIComponent('hello+world'));
// "hello+world" ("hello world"가 아님!)

// 해결책: 디코딩 전에 +를 치환
function decodeFormValue(str) {
  return decodeURIComponent(str.replace(/\+/g, ' '));
}
console.log(decodeFormValue('hello+world'));
// "hello world"

// 또는 URLSearchParams를 사용 (+를 자동으로 처리)
const params = new URLSearchParams('q=hello+world');
console.log(params.get('q'));
// "hello world"

관련 글

무료 도구 사용해 보기