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://pub.dev/packages/dio

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/

+ Recent posts