URL을 문자열로 쪼개지 마세요
URL API가 생기기 전에는 다들 split('?')에 정규식 몇 줄, 그리고 기도로 URL을 파싱했습니다. 값에 &, =, 공백, 비 ASCII 문자가 들어오기 전까지는 그럭저럭 돌아갔죠. 하지만 그런 값이 섞이는 순간 바로 깨집니다. 브라우저와 Node 모두 제대로 된 파서를 기본으로 제공하니, 이걸 쓰세요.
한 번의 호출만으로 URL의 모든 구성 요소가 이미 분리되어 있고 디코딩까지 깔끔하게 끝나 있습니다. 잘못된 입력이 들어오면 생성자가 TypeError를 던지는데, 보통은 이 동작이 오히려 반갑습니다 — 쓰레기 같은 URL은 조용히 이상한 값을 뒤로 흘려보내기보다 그 자리에서 요란하게 터지는 편이 낫죠.
쿼리스트링 파싱하기 (URLSearchParams 사용법)
모든 URL 객체에는 .searchParams 프로퍼티가 있습니다. 이건 쿼리 문자열을 읽고 쓸 줄 아는 URLSearchParams 객체예요:
짚고 넘어갈 만한 포인트 몇 가지:
- 값은 이미 디코딩된 상태로 반환됩니다.
?name=Ada%20Lovelace를 넘기면"Ada Lovelace"가 나와요. - 모든 값은 문자열입니다.
"2"는2가 아니에요. 숫자로 써야 한다면Number()로 변환하세요. - 키가 중복되어도 괜찮습니다.
get은 첫 번째 값만 돌려주고, 전부 필요하면getAll을 쓰면 됩니다. - 없는 키는
undefined가 아니라null을 돌려줍니다. 그래서?? "default"패턴과 궁합이 잘 맞아요.
쿼리스트링 만들기
URLSearchParams를 쓰면 쿼리스트링을 직접 조립할 수 있습니다. 이스케이프를 손으로 할 필요도, &로 일일이 이어 붙일 필요도 없어요:
객체로부터 바로 만들 수도 있는데, [key, value] 쌍으로 된 이터러블이든 평범한 객체든 모두 받아줍니다:
set vs append의 차이: set은 해당 키에 이미 값이 있으면 덮어쓰고, append는 같은 키에 값을 하나 더 붙입니다. 태그나 필터처럼 키가 여러 번 반복될 수 있는 경우엔 append를, 값이 하나뿐인 파라미터엔 set을 쓰면 됩니다.
URL 수정하기
URL은 살아있는 객체이기 때문에, searchParams를 바꾸면 .search와 .href가 자동으로 같이 업데이트됩니다:
기존 URL에 쿼리 파라미터를 덧붙일 때 가장 깔끔한 방법이 바로 이 방식입니다. URL에 이미 ?가 붙어 있는지 따로 확인할 필요도 없고, &를 붙여야 할지 ?를 붙여야 할지 고민할 필요도 없습니다.
URL의 다른 부분도 같은 방식으로 수정할 수 있습니다:
파라미터 순회하기
URLSearchParams는 이터러블(iterable)이라서 for...of로 돌리면 [key, value] 형태의 쌍을 하나씩 꺼낼 수 있습니다. 그리고 익숙한 keys(), values(), entries() 메서드도 그대로 사용할 수 있어요:
중복된 키는 여러 번 등장한다는 점에 주의하세요. 예를 들어 tag = web이 먼저 나오고 tag = beginner가 별도 항목으로 또 나옵니다. 실제 쿼리스트링 구조를 있는 그대로 반영한 결과죠.
디버깅용으로 간단하게 객체 형태로 찍어보고 싶다면 Object.fromEntries를 쓰면 됩니다. 단, 중복 키는 합쳐져서 마지막 값만 남는다는 점을 알아두세요:
디버깅 용도로는 괜찮지만, 같은 키가 중복될 수 있는 상황이라면 잘못된 방식입니다.
상대 경로 URL에는 base가 필요하다
new URL("/search?q=js")만 쓰면 에러가 납니다. 상대 경로는 그 자체로 유효한 URL이 아니기 때문이죠. 이럴 땐 두 번째 인자로 base URL을 넘겨주면 됩니다:
이 해석 규칙은 브라우저가 <a href>를 처리할 때 쓰는 것과 똑같다. 앞에 /가 붙으면 호스트 기준 절대 경로, 슬래시가 없으면 현재 경로 기준 상대 경로, ..는 한 단계 위로 올라간다. 설정된 base URL에서 API URL을 조립할 때 정말 유용하다.
브라우저 환경에서는 window.location.href를 base로 넘기면 현재 페이지 URL을 바로 파싱할 수 있다:
const u = new URL(window.location.href);
const page = u.searchParams.get("page") ?? "1";
잘못된 URL 처리하기
URL 생성자는 형식이 잘못된 값이 들어오면 예외를 던집니다. 이게 오히려 장점이 되긴 하지만, 사용자가 직접 입력한 값이나 외부 시스템에서 넘어온 URL을 파싱할 때는 반드시 try/catch로 감싸야 한다는 뜻이기도 합니다.
최신 환경에서는 URL.canParse(input)도 제공됩니다. 단순히 유효성만 확인하고 싶을 때 try/catch로 감쌀 필요 없이 불리언 값으로 바로 판별할 수 있죠:
작은 실전 예제
지금까지 배운 걸 한데 모아볼까요. URL에서 현재 필터 값을 읽어와 살짝 바꾼 뒤, 이동할 새 URL을 만들어내는 흐름입니다.
null을 넘기면 해당 파라미터가 삭제되고, 그 외의 값을 넘기면 새로 설정되거나 덮어쓰기 됩니다. 필터 UI나 페이지네이션, 딥 링크를 만들다 보면 어떤 형태로든 반드시 쓰게 되는 패턴이에요.
핵심 정리
new URL(string)은 URL을 의미 있는 조각들로 파싱해 줍니다. 엉뚱한 값이 들어오면 예외를 던져요.url.searchParams는URLSearchParams객체이고 —get,getAll,set,append,delete,has메서드로 다룰 수 있습니다.- 인코딩은 알아서 처리됩니다. 문자열을 직접 이어 붙이는 경우가 아니라면
encodeURIComponent를 굳이 꺼낼 필요 없어요. - 상대 경로를 해석하고 싶다면 두 번째 인자로 기준 URL을 넘기면 됩니다.
- 신뢰할 수 없는 입력을 검증할 때는
URL.canParse나try/catch를 쓰세요.
URL을 .split('?')로 잘라 보고 싶거나, 정규식으로 쿼리 파라미터를 뽑아내고 싶어질 때마다 이 API들을 먼저 떠올려 보세요. 코드도 짧고, 정확하고, 이미 런타임에 내장되어 있으니까요.
자주 묻는 질문
자바스크립트에서 URL은 어떻게 파싱하나요?
문자열을 URL 생성자에 그대로 넘기면 됩니다: const u = new URL('https://example.com/path?x=1'). 이렇게 만들어진 객체는 protocol, host, pathname, search, hash, 그리고 searchParams까지 바로 꺼내 쓸 수 있어요. 단, 잘못된 URL이면 예외를 던지기 때문에 외부에서 받은 값을 파싱할 때는 try/catch로 감싸는 게 안전합니다.
쿼리스트링 파라미터 값은 어떻게 꺼내나요?
url.searchParams.get('name')을 쓰면 됩니다. 디코딩된 값이 반환되고, 해당 파라미터가 없으면 null이 나와요. ?tag=a&tag=b처럼 같은 키가 여러 번 올 수 있는 경우에는 searchParams.getAll('tag')로 전체 값을 배열로 받아올 수 있습니다.
URL과 URLSearchParams는 어떻게 다른가요?
URL은 프로토콜, 호스트, 경로, 쿼리, 해시까지 URL 전체를 표현하는 객체입니다. 반면 URLSearchParams는 쿼리스트링 부분만 담당해서 a=1&b=2 같은 문자열을 만들거나 파싱할 때 단독으로도 쓸 수 있어요. 모든 URL 인스턴스에는 해당 URL에 연결된 .searchParams 프로퍼티가 들어 있습니다.
쿼리 파라미터를 직접 인코딩해야 하나요?
그럴 필요 없습니다. URLSearchParams는 set, append로 값을 넣거나 문자열로 뽑아낼 때 키와 값을 자동으로 인코딩해 줘요. 공백, &, =, 유니코드까지 알아서 처리됩니다. encodeURIComponent는 문자열을 직접 조립할 때만 써야 하는데, 사실 그렇게 수작업할 일 자체가 거의 없어야 합니다.