가난한 자의 URL 클래스

2023년에 굳이 이 작업을 합니다

현재에 와서 URL을 우아하게 다루는 방법은 웹 브라우저에서 네이티브로 지원해주는 URL 클래스를 사용하는 것이다. 생성자를 사용해 URL을 만들어주면 프로토콜과 호스트 이름, 파일 경로, 쿼리 문자열까지 아주 잘 뽑아내준다.

const url = new URL('https://taegon.kim/?s=url');
console.log(url.protocol, url.hostname, url.pathname, url.search);

게다가 URL 인스턴스에는 searchParams라는 URLSearchParams 인스턴스도 있는데 기본적으로 맵과 유사한 인터페이스를 가지고 있어서 각 쿼리의 키와 값을 설정하기가 좋다.

const url = new URL('https://taegon.kim/?s=url');
console.log(url.searchParams.get('s')); // 'url'

url.searchParams.set('num', '1234');
console.log(url.searchParams.toString()); // 's=url&num=1234'

URL과 URLSearchParams 모두 Node.js에도 네이티브로 존재하므로(아주 예전 버전엔 없을 수 있다) 클라이언트든 서버든 JS에서 URL을 우아하게 다루기는 쉬워졌...어야 했지만, 아직도 IE 11을 지원해야 하는 입장에서 저 두 개의 아름다운 지원은 그림의 떡일 뿐이다.

문서를 살펴보면 IE 10부터는 URL을 지원한다고 하는데 잘 읽어보면 URL 클래스가 아니라 URL object를 지원한다. IE에서 지원하는 이 URL object는 거의 Object나 마찬가지인데 createObjectURLrevokeObjectURL이라는 두 개의 정적 메소드를 포함하고 있다. 이들을 사용하는 방법은 [JS] Blob와 Blob URL을 참조하자. 어쨌든 중요한 것은 URL의 각 파트를 다루는 데는 도움이 안된다는 점이다.

그럼 URL 클래스가 있기 전에는 어떻게 URL의 각 파트를 구분했을까? 정규표현식을 사용하거나 이 글에서 설명할 방법을 사용했다. 코드부터 보자.

const url = document.createElement('a');
url.href = 'https://taegon.kim/?s=url';
console.log(url.protocol, url.hostname, url.pathname, url.search);

DOM 엘리먼트를 사용하고 있으므로 당연히 웹 브라우저 환경 또는 DOM 환경이 제공되는 Node에서만 사용할 수 있는 방법이다. 또한 아쉽게도 searchParams는 제공해주지 않아서 쿼리 문자열을 간편하게 다루기는 어렵다. 그 외에도 항상 기준이 현재 문서의 URL이 되어서 잘못된 형식의 URL을 전달하는 경우에는 기대한 대로 동작하지 않을 수 있다는 문제가 있다.

본격적으로 구현 된 URL의 폴리필은 이미 core-js에서 잘 다루고 있으니 여기서는 (언제나 그렇듯) 아주 간단한 버전으로 만들어 보려고 한다.

/**
 * URL 문자열을 해석하여 (유사) URL 객체를 반환한다.
 * @param {string} url URL 문자열
 * @return {URL} URL 객체. IE11에서는 유사 URL 객체를 반환한다.
 */
function parseURL(url) {
  if (typeof URL === 'function') {
    return new URL(url);
  }

  var a = document.createElement('a');
  a.href = url;

  return {
    protocol: a.protocol,
    host: a.host,
    hostname: a.hostname,
    pathname: a.pathname,
    hash: a.hash,
    href: a.href,
    search: a.search,
  };
}

아쉬운 부분이 꽤 있다. 이왕이면 searchParams도 지원하면 좋겠고 읽기 전용 객체가 아니라 각 파트를 설정하면 href에 반영되었으면 좋을 것이다. 하지만, 2023년에 굳이 URL 객체가 없는 브라우저를 지원해야 할까. 아마 나도 회사 업무가 아니었다면 아마 작성하지 않았을 것 같다. 그래서 이 작업은 여기서 멈추기로 했다. 혹시 더 많은 기능이 필요한 사람이 있다고 하면 그때 추가하면 되지 않을까.

  1. 내용 잘 봤습니다. polyfill이야 임포트해 사용하면 되지만, 이렇게 설명을 해 주시니 IE11과 최신 브라우저들의 차이를 알 수 있어서 좋네요.

    1. 저희 회사 업무는 용량에 민감한데 폴리필이 의외로 용량이 커서 쓰기가 부담스럽더라고요(그게 다 돈이라...). 그래서 이렇게 기능은 좀 모자라도 가벼운 폴리필을 직접 만들어서 쓰고 있기도 합니다. 하지만 실제 속마음은 얼른 IE를 버리고 싶습니다 ㅠㅠ

댓글을 남겨주세요

This site uses Akismet to reduce spam. Learn how your comment data is processed.