Skip to content

Querystring

jin-frame에서 클래스 필드에 @Query() 데코레이터를 선언하면 해당 필드 값이 URL 쿼리스트링으로 직렬화되어 요청에 포함됩니다.

간단한 예제

ts
@Get({ host: 'https://api.example.com', path: '/search' })
export class SearchFrame extends JinFrame {
  @Query() declare readonly q: string;
  @Query() declare readonly page?: number;
  @Query() declare readonly tags?: string[]; // → ?tags=red&tags=blue
  @Query() declare readonly debug?: boolean; // → ?debug=true
}

// 실행
const frame = SearchFrame.of({ q: 'pikachu', page: 2, tags: ['red', 'blue'], debug: true });
const reply = await frame.execute();

console.log(reply.config.url);
// https://api.example.com/search?q=pikachu&page=2&tags=red&tags=blue&debug=true

지원 타입과 직렬화 방식

@Query()가 지원하는 타입과 URL 직렬화 결과는 다음과 같습니다.

타입옵션예제 값직렬화 결과
string'pikachu'?q=pikachu
number2?page=2
booleantrue?debug=true
string[]comma ✗['a','b']?tags=a&tags=b
string[]comma ✓['a','b']?tags=a,b
number[]bit ✗[1,2,4]?ids=1&ids=2&ids=4
number[]bit ✓[1,2,4]?ids=7
(비트 OR: 1|2|4 = 7)
undefined / nullundefined/null생략됨
(쿼리 키 생성 안 됨)
  • 배열 기본 직렬화는 반복 키(repeated key) 방식입니다.
  • 숫자/불리언 값은 문자열로 변환되어 전송됩니다.
  • undefined / null은 자동으로 생략됩니다.

배열 옵션

기본 배열 직렬화 (반복 키)

ts
@Get({ host: 'https://api.example.com', path: '/filter' })
export class ArrayQueryFrame extends JinFrame {
  @Query() declare readonly tags?: string[];
}

const reply = await ArrayQueryFrame.of({ tags: ['red', 'blue'] }).execute();
// → ?tags=red&tags=blue

쉼표 구분 배열

@Query({ comma: true }) 옵션을 사용하면 배열을 쉼표 구분 문자열로 직렬화할 수 있습니다.

ts
@Get({ host: 'https://api.example.com', path: '/filter' })
export class CommaQueryFrame extends JinFrame {
  @Query({ comma: true })
  declare readonly tags?: string[];
}

const reply = await CommaQueryFrame.of({ tags: ['red', 'blue', 'green'] }).execute();
// → ?tags=red,blue,green

비트 OR 배열 (숫자 전용)

@Query({ bit: { enable: true } })를 사용하면 숫자 배열을 비트 OR 합산 값으로 직렬화할 수 있습니다.

ts
@Get({ host: 'https://api.example.com', path: '/flags' })
export class BitwiseQueryFrame extends JinFrame {
  @Query({ bit: { enable: true } })
  declare readonly flags?: number[];
}

const reply = await BitwiseQueryFrame.of({ flags: [1, 2, 4] }).execute();
// → ?flags=7   (1 | 2 | 4 = 7)

API 서버가 비트 마스크 값을 기대할 때 유용합니다.

배열 키 포맷 지정

@Query({ keyFormat: '...' }) 옵션으로 배열 쿼리의 키 형식을 지정할 수 있습니다.

ts
@Get({ host: 'https://api.example.com', path: '/items' })
export class KeyFormatFrame extends JinFrame {
  @Query({ keyFormat: 'brackets' })
  declare readonly tags?: string[];
  
  @Query({ keyFormat: 'indices' })
  declare readonly ids?: number[];
}

const reply = await KeyFormatFrame.of({ 
  tags: ['red', 'blue'], 
  ids: [10, 20] 
}).execute();
// → ?tags[]=red&tags[]=blue&ids[0]=10&ids[1]=20

지원하는 키 포맷:

  • 기본값: ?tags=red&tags=blue (반복 키)
  • brackets: ?tags[]=red&tags[]=blue
  • indices: ?tags[0]=red&tags[1]=blue
  • one-indices: ?tags[1]=red&tags[2]=blue (1부터 시작)

URL 인코딩

모든 키와 값은 URL에 추가되기 전에 안전하게 URL 인코딩됩니다.

ts
@Get({ host: 'https://api.example.com', path: '/search' })
export class EncodedFrame extends JinFrame {
  @Query() declare readonly q: string;
}

const reply = await EncodedFrame.of({ q: 'hello world & tea' }).execute();
// → ?q=hello%20world%20%26%20tea

선택적 쿼리

값이 없으면 해당 쿼리 키는 생략됩니다.

ts
@Get({ host: 'https://api.example.com', path: '/items' })
export class OptionalQueryFrame extends JinFrame {
  @Query() declare readonly q?: string;
  @Query() declare readonly page?: number;
}

const reply = await OptionalQueryFrame.of({}).execute();
// → https://api.example.com/items   (쿼리 없음)

Param, Header와 함께 사용하기

ts
@Get({ host: 'https://api.example.com', path: '/orgs/:orgId/users' })
export class ListUsersFrame extends JinFrame {
  @Param() declare readonly orgId: string;
  @Query() declare readonly page?: number;
  @Header() declare readonly Authorization: string;
}

const reply = await ListUsersFrame.of({
  orgId: 'acme',
  page: 1,
  Authorization: 'Bearer token',
}).execute();
// → GET /orgs/acme/users?page=1

디버깅 팁

요청 직전에 frame.getData('query')를 사용해 최종 쿼리 맵을 확인할 수 있습니다.

ts
const frame = SearchFrame.of({ q: 'pikachu', tags: ['red', 'blue'] });
const req = frame.request();

console.log(frame.getData('query')); // { q: 'pikachu', tags: ['red','blue'] }
console.log(req.url); // 최종 URL