티스토리 뷰
query parameter 란
URL 의 경로 뒤에 키, 값(key, value) 의 목록이다. parameter 라고 부르기도 하며 경로 뒤에 ? 로 시작한다. 각 키와 값은 & 로 구분한다.
이 값에 배열을 담는 방법에 대해 정리하려고 한다.
문제 상황
Flutter 의 Dio 플러그인을 이용해서 query parameter 의 값에 배열을 담으려 했다. 배열의 요소가 2개 이상일 경우는 문제가 없었으나 1개일 때는 배열이 사라지고 그 안의 요소만 전송되는 현상이 발생했다.
예를 들어 localhost 의 8080 포트에 foo 경로로 { bar: [123] } 을 보내고 싶을때 Flutter 의 Dio 플러그인을 이용해서 아래와 같이 보낼 수 있다.
try {
List<int> barIdx = [123];
Response<dynamic> result = await dio.get(
"http://localhost:8080/foo",
queryParameters: {
'bar': barIdx,
}
);
return result;
} on DioError catch (err) {
return err;
}
NestJS 서버에서 요청을 받는다.
(query parameter 는 문자열이라 이를 정수로 변환하는건 별도로 처리해야 한다. NestJS 에서는 class-validator, class-transformer 등을 이용할 수 있다.)
@Get('foo')
async getFooData(
@Query() query: any
) {
console.log(query);
// { idx: '123' }
}
Flutter 에서 [123] 인 barIdx 값을 foo 키의 값으로 담아서 보내는데 실제 서버에서 받으면 배열은 사라졌다. 그런데 barIdx 가 [123] 이 아니라 [123, 124, 125] 일 경우는 정상적으로 배열이 유지됐다.
try {
List<int> barIdx = [123, 124, 125];
Response<dynamic> result = await dio.get(
"http://localhost:8080/foo",
queryParameters: {
'bar': barIdx,
}
);
return result;
} on DioError catch (err) {
return err;
}
@Get('foo')
async getFooData(
@Query() query: any
) {
console.log(query);
// { idx: ['123', '124', '125'] }
}
url 로 직접 보낸다면 어떻게 보낼 수 있을까?
터미널에서 cURL 로 url 을 입력해서 보낸다면 barIdx 의 요소를 어떻게 작성해야 할까?
barIdx 의 요소 갯수만큼 parameter 를 반복하면 된다. barIdx 요소가 [123, 124, 125] 이렇게 총 3개라면 아래와 같이 보낼 수 있다.
curl -X GET "http://localhost:8080/foo?bar=123&bar=124&bar=125"
이 시점에 문제에 대한 힌트를 어느정도 얻을 수 있었다. 여러 개의 동일한 키를 보내면 값이 배열로 합쳐진다. 반면에 요소가 한 개일 경우는 한 개의 키를 보내서 값이 배열로 합쳐질 수 없다. 요청한 키의 값을 배열로 보내는지 여부를 알 방법이 없다.
try {
List<int> barIdx = [123];
Response<dynamic> result = await dio.get(
"http://localhost:8080/foo",
queryParameters: {
'bar': barIdx,
}
);
return result;
} on DioError catch (err) {
return err;
}
다시 첫 부분인 '문제 상황'에서 언급한 코드로 돌아와 보면 위의 코드는 키가 bar 이고 값이 123 이라고 서버에서 판단하게 된다. 클라이언트에서 배열로 보냈는지 알 수가 없다.
아래와 같은 url 로 변경되어서 서버로 전송될 것이다. 그렇기 때문에 클라이언트가 배열을 보내도 서버에서는 배열임을 알 수 없다.
"http://localhost:8080/foo?bar=123"
해결방법
배열의 요소가 한 개인지 두 개인지와 관계없이 query parameter 의 값으로 배열을 보내고 싶다면 키에 [] 를 붙여주면 된다.
try {
List<int> barIdx = [123];
Response<dynamic> result = await dio.get(
"http://localhost:8080/foo",
queryParameters: {
'bar[]': barIdx,
}
);
return result;
} on DioError catch (err) {
return err;
}
동일한 내용을 cURL 로는 아래와 같이 보낼 수 있다.
curl -X GET "http://localhost:8080/foo?bar[]=123"
@Get('foo')
async getFooData(
@Query() query: any
) {
console.log(query);
// { idx: ['123'] }
}
위의 경우는 배열의 요소가 하나였는데 만약 { bar: [123, 124, 125] } 이와 같이 여러개 요소를 보낸다고 하더라도 이전과 동일하게 보낼 수 있다.
try {
List<int> barIdx = [123, 124, 125];
Response<dynamic> result = await dio.get(
"http://localhost:8080/foo",
queryParameters: {
'bar[]': barIdx,
}
);
return result;
} on DioError catch (err) {
return err;
}
cURL 로 보낸다면 아래와 같이 키를 barIdx 의 요소 갯수만큼 반복해서 보낼 수 있다.
curl -X GET "http://localhost:8080/foo?bar[]=123&bar[]=124&bar[]=125"
@Get('foo')
async getFooData(
@Query() query: any
) {
console.log(query);
// { idx: ['123', '124', '125'] }
}
<참고>
https://developer.mozilla.org/ko/docs/Learn/Common_questions/Web_mechanics/What_is_a_URL
https://docs.nestjs.com/techniques/validation
https://stackoverflow.com/questions/6243051/how-to-pass-an-array-within-a-query-string
https://ryan-han.com/post/translated/pathvariable_queryparam/