URLSearchParams는 쿼리를 단일 값 객체가 아니라 순서가 있는 이름-값 쌍 목록으로 다룹니다. 필터처럼 같은 키를 여러 번 보내는 API라면 이 차이가 요청 의미를 바꿀 수 있습니다. WHATWG URL Standard는 getAll()이 일치하는 값을 목록 순서대로 반환한다고 규정하고, MDN API 안내는 각 메서드의 사용 예를 보여 줍니다.

여러 색의 열쇠와 도형 카드가 중앙의 회색 기계로 들어가고 기호가 붙은 카드로 나오는 그림

열쇠와 도형을 기계에 넣어 기호가 바뀌는 비유입니다. 순서가 있는 name=value 쌍이나 URLSearchParams의 실제 직렬화 결과를 보여 주는 그림은 아닙니다.

같은 키를 읽고 추가하고 바꾸기

다음 요청은 tag를 두 번 포함합니다. get()은 첫 값만, getAll()은 두 값을 순서대로 돌려줍니다. append()는 기존 쌍을 보존하면서 새 값을 끝에 붙이고, set()은 그 이름의 기존 중복 쌍을 하나의 새 값으로 바꿉니다.

const params = new URLSearchParams("tag=blue&tag=green");
params.get("tag");       // "blue"
params.getAll("tag");    // ["blue", "green"]
params.append("tag", "red");
params.set("tag", "gold"); // tag는 gold 하나만 남음

페이지 필터를 서버로 보낼 때 여러 선택값을 보존하려면 append()를 사용하고, 단일 설정값을 갱신하려면 set()을 사용하세요. 다만 서버 프레임워크가 중복 필드를 배열, 첫 값 또는 마지막 값 중 무엇으로 해석하는지는 별도 계약입니다. 클라이언트 메서드가 서버의 해석을 정해 주지는 않습니다.

공백과 더하기 기호는 입력 경로에 따라 다르다

문자열 생성자에 q=a+b를 전달하면 폼 형식 파서가 +를 공백으로 읽어 a b가 됩니다. 반면 append("q", "a+b")의 더하기 기호는 데이터이므로 직렬화할 때 %2B로 표현됩니다. 폼 직렬화에서 공백은 +로 나타날 수 있습니다. 따라서 원본 표기와 파싱된 값, 다시 직렬화한 문자열은 구분해야 합니다.

new URLSearchParams("q=a+b").get("q"); // "a b"
const p = new URLSearchParams();
p.append("q", "a+b");
p.toString(); // "q=a%2Bb"

전체 주소를 생성자에 넣어도 URL 파싱이 되지 않습니다. new URL(location.href).searchParams 또는 new URLSearchParams(location.search)처럼 쿼리 부분을 전달하세요.

서버 경계에서 확인할 세 가지

예를 들어 사용자가 tag=blue, tag=green을 선택했다면 요청 문자열이 두 쌍을 유지하는지, 서버가 같은 순서의 배열로 읽는지, 빈 값과 키 누락을 구별하는지 통합 테스트로 확인합니다. 예상 결과가 다르면 API 계약이나 서버 파서를 조정해야 합니다.

또한 논리적으로 같은 쿼리도 공백 인코딩이나 직렬화 과정에 따라 원시 문자열이 달라질 수 있습니다. 서명·캐시 키·정확한 로그 비교에 원시 query를 쓰는 경우에는 중복 키, 순서, 빈 값, 정규화 규칙을 먼저 정하고 테스트하세요. URLSearchParams는 URL 인코딩 도구이지 입력 정화, 인증 또는 권한 검사를 대신하지 않습니다.

참고 자료