쿼리스트링 배열 파라미터, 언어마다 다르게 읽히는 이유

URL에 ?tag=a&tag=b처럼 같은 키가 여러 번 나오면, 이걸 배열로 묶어서 읽을지 마지막 값만 남길지는 언어나 프레임워크마다 다르게 정해져 있습니다. 그래서 프론트엔드가 보낸 쿼리스트링을 백엔드가 다르게 해석해서 값이 하나만 도착하는 문제가 자주 생깁니다. 이 글은 그 규칙이 왜 갈리는지와, 실제로 어떻게 해석되는지 직접 확인하는 방법을 정리합니다.

요약 ① URL 쿼리스트링 자체에는 배열이라는 자료형이 없고, 배열로 볼지는 각 언어·프레임워크가 정한 관례일 뿐입니다. ② 크게 같은 키를 반복하는 방식(tag=a&tag=b)과, 대괄호를 붙여야 배열로 인식하는 방식(tag[]=a&tag[]=b)으로 갈립니다. ③ 어느 쪽 규칙인지는 코드를 보지 않아도, 쿼리스트링을 실제로 JSON으로 바꿔보면 바로 확인됩니다.

문제

체크박스로 태그를 여러 개 고르는 필터 화면을 생각해보면, 브라우저는 보통 이름이 같은 값들을 tag=a&tag=b처럼 반복해서 전송합니다.

이 요청을 받는 서버가 대괄호 표기만 배열로 인식하는 언어라면 얘기가 달라집니다. 대괄호 없이 반복된 키는 배열로 묶이지 않고 마지막 값 하나만 남습니다.

반대로 API 문서를 보고 tag[]=a&tag[]=b 형태로 직접 만들어 보냈는데, 받는 쪽이 반복 키만 배열로 처리하는 쪽이라면 이번엔 "tag[]"라는 이름의 키 하나로 잘못 읽힙니다.

이런 문제는 값 하나로 테스트할 땐 안 보이고, 다중 선택·다중 태그처럼 같은 키가 여러 번 나오는 상황에서만 터져서 원인을 찾기가 유독 까다롭습니다.

해결

먼저 짚어야 할 건, URL 쿼리스트링 자체에는 "배열"이라는 자료형이 없다는 점입니다. 물음표 뒤는 그냥 key=value 쌍이 &로 나열된 문자열일 뿐이고, 이걸 배열로 묶을지는 이를 읽는 쪽이 정한 관례입니다.

이 관례는 크게 두 갈래로 나뉩니다. 하나는 같은 키를 그대로 반복하는 방식이고, 다른 하나는 대괄호를 붙여야 배열로 인식하는 방식입니다.

반복 키 방식과 대괄호 방식

브라우저 표준 API인 URLSearchParams는 반복된 키를 알아서 배열로 묶어주지 않습니다. get("tag")는 첫 번째 값만 돌려주고, 전체 값을 받으려면 getAll("tag")을 직접 호출해야 합니다.

대괄호도 특별 취급하지 않습니다. tag[]=a&tag[]=b를 넣으면 "tag"가 아니라 "tag[]"라는 이름의 키 하나로 인식됩니다.

반면 대괄호 방식을 쓰는 언어·프레임워크는 정반대로 동작합니다. 대괄호 없이 tag=a&tag=b만 반복하면 배열이 아니라 마지막 값 하나만 남고, 배열로 받으려면 tag[]=a&tag[]=b 형태를 써야 합니다.

손으로 확인하는 순서

값이 씹히는 문제는 이 순서로 짚으면 됩니다. 먼저 내 쪽 코드가 반복 키로 보내는지 대괄호로 보내는지 확인합니다.

그다음 서버 쪽 문서에서 쿼리 파싱 규칙이 어느 쪽인지 맞춰봅니다. 둘이 다르면 클라이언트에서 형식을 맞추거나, 서버에서 반복 키를 직접 배열로 묶는 코드를 추가해야 합니다.

가장 빠른 확인법은 실제로 쿼리스트링을 넣어서 결과가 배열로 묶이는지 직접 눈으로 보는 것입니다. 저희 도구로 확인해 보세요.

URL 파서·쿼리스트링 변환

URL이나 쿼리스트링을 넣으면 배열로 묶인 파라미터를 JSON으로 바로 확인해 드립니다.

자주 막히는 지점

상황원인
체크박스 여러 개를 선택했는데 서버에는 값이 하나만 도착한다서버가 대괄호 표기(tag[]=)만 배열로 인식하는데, 클라이언트는 반복 키(tag=a&tag=b)로 보냈기 때문입니다
URLSearchParams.get("tag")로 꺼냈는데 값이 하나뿐이다get()은 항상 첫 번째 값만 반환합니다. 전체를 받으려면 getAll("tag")을 써야 합니다
JSON의 배열 값을 쿼리스트링으로 바꿨는데 서버가 못 읽는다반복 키 방식으로 직렬화됐는데, 서버는 대괄호 표기만 배열로 인식하는 경우입니다
같은 URL인데 언어·프레임워크를 바꾸니 결과가 달라진다쿼리스트링 배열 표기에 정해진 표준이 없어서, 파싱 규칙이 저마다 다르기 때문입니다

정리

쿼리스트링에서 같은 키가 여러 번 나올 때 배열로 묶을지, 어떤 표기를 배열로 인정할지는 표준이 아니라 언어·프레임워크별 관례입니다.

값이 사라지거나 하나만 남는 문제는 대부분 클라이언트가 보내는 표기와 서버가 기대하는 표기가 서로 다를 때 생깁니다. 어느 쪽 관례인지 헷갈리면 실제 쿼리스트링을 JSON으로 바꿔서 눈으로 확인하는 게 제일 확실합니다.

URL 파서·쿼리스트링 변환

URL이나 쿼리스트링을 넣으면 배열로 묶인 파라미터를 JSON으로 바로 확인해 드립니다.

자주 묻는 질문

tag[]=a&tag[]=b처럼 대괄호를 넣으면 배열로 인식되나요?

이 도구는 브라우저 표준 URLSearchParams를 그대로 사용하기 때문에 대괄호를 특별히 해석하지 않습니다. tag[]=a&tag[]=b를 넣으면 "tag"가 아니라 "tag[]"라는 이름의 키에 값 두 개가 들어있는 것으로 표시됩니다. 대괄호를 배열 표기로 쓰는 백엔드와 연동할 때는 이 차이를 감안해야 합니다.

JSON의 배열 값을 쿼리스트링으로 바꾸면 어떤 형식이 되나요?

"JSON → 쿼리" 탭은 배열 값을 대괄호가 아니라 같은 키를 반복하는 방식으로 직렬화합니다. 예를 들어 {"tag": ["a", "b"]}tag=a&tag=b가 됩니다. 대괄호 표기가 필요한 서버라면 결과에 직접 []를 덧붙여야 합니다.

"URL 분해"와 "쿼리 → JSON" 탭은 어떻게 다른가요?

"URL 분해"는 protocol·host·port·pathname·hash까지 URL 전체를 쪼개서 보여주고, 그 안의 쿼리 부분도 함께 JSON으로 보여줍니다. 물음표 뒤 쿼리스트링만 따로 갖고 있다면 "쿼리 → JSON" 탭에 바로 붙여넣으면 됩니다.

가격 보기카톡 무료 상담