๐ [NestJS, TypeScript] Swagger์ Generic Type ์ ์ํ๋ ๋ฐฉ๋ฒ
๋ฐฑ์๋์์ ์์ฒญ๊ณผ ์๋ต์ ์ฒ๋ฆฌํ ๋ ์ผ์ ํ ํ ํ๋ฆฟ์ด ์ ํด์ ธ ์๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
export class SuccessResponse<T> {
@ApiProperty({ description: '์๋ต ๋ฐ์ดํฐ' })
payload: T;
@ApiProperty({ description: '์ํ์ฝ๋' })
code: string;
@ApiProperty({ description: 'ํธ์ถ๋ URI' })
request: string;
}
์ ํ์
์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌํ๋ฉด ๋ณด๋ด๋ ๋ฐ์ดํฐ ํ์
์
๋๋ค.
์ด๋ฌํ ์ค๋ณต๋๋ ํ์
์ ๊ฐ DTO๋ง๋ค ์ฌ์ฉํ๋ฉด ๋ถํธํจ์ด ํ๋ ๊ฐ์ง๊ฐ ์๋์ด์ ์๋์ ๊ฐ์ด ์ ๋ค๋ฆญ ํ์
์ ์ฌ์ฉํฉ๋๋ค.
@Get()
@ApiExtraModels(GetBrandListReq)
@ApiOkResponse({
description: '๋ธ๋๋ ๋ฆฌ์คํธ',
type: SuccessResponse<Brand[]>,
})
findByFilter(@Query() query: GetBrandListReq) {
return this.brandService.findByFilter(query);
}
๊ทธ๋ฐ๋ฐ ์ฌ๊ฑธ? ์ค์จ์ด๊ฑฐ์ ํ์
์ด ํ์๊ฐ ์๋ฉ๋๋ค.
์ด์ ๋ Swagger๊ฐ NestJS์ ๋ฐํ์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฌธ์๋ฅผ ์์ฑํ๋ ๋ฐ, TypeScript์ ์ ๋ค๋ฆญ์ ์ปดํ์ผ ๋จ๊ณ์์ ์๋ฉธ๋๊ธฐ ๋๋ฌธ์
๋๋ค.
์ด๋ ๊ฒ ๋๋ฉด ํ์ธํ๊ธฐ๋ ๋ถํธํ๊ฑฐ๋์, ํ๋ก ํธ์์ swagger-typescript-api
๋ฅผ ์ฌ์ฉํด ํ์
์ ๊ธ์ด์ฌ ๋๋ ์ ๋๋ก ํ์๊ฐ ์๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
๐ Swagger Generic Type ํ์ํ๊ธฐ
์ด๋ฌํ ํ์์ ํด๊ฒฐํ๊ธฐ ์ํด getSchemaPath
๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๊ฒ ์ง๋ง, ์ค์จ์ด๊ฑฐ ๋ฌธ์ํ ์ฝ๋๊ฐ ๋๋ฌด ๊ธธ์ด์ง๋ ๊ฒ์ด ๋ง์ ์ ๋ค์์ต๋๋ค.
๊ทธ๋์ ์ ๋ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ๋ดค์ต๋๋ค.
import { mixin } from '@nestjs/common';
type Constructor<T = object> = new (...args: any[]) => T;
export class TypeUtil {
static getSuccessResponse<T extends Constructor>(Base: T) {
class Class extends SuccessResponse<T> {
@ApiProperty({ type: Base, description: '์๋ต ๋ฐ์ดํฐ' })
payload: T;
}
return class SuccessResponse extends mixin(Class) {};
}
static getSuccessResponseList<T extends Constructor>(Base: T) {
class Class extends SuccessResponseList<T> {
@ApiProperty({ type: [Base], description: '์๋ต ๋ฐ์ดํฐ' })
payload: T;
}
return class SuccessResponseList extends mixin(Class) {};
}
}
// dto
export class GetBrandListRes extends TypeUtil.getSuccessResponseList(Brand) {}
@Get()
@ApiExtraModels(GetBrandListReq)
@ApiOkResponse({
description: '๋ธ๋๋ ๋ฆฌ์คํธ',
type: GetBrandListRes,
})
findByFilter(@Query() query: GetBrandListReq) {
return this.brandService.findByFilter(query);
}
๐ Mixin์ ์ด์ฉํด ์๋ก์ด ํด๋์ค ๋ฐํ
mixin์ ํด๋์ค์ ์์์ด๋ ํ์ฅ์ ๊ฐ๋ ์ด ์๋ ๋ ๊ฐ ์ด์์ ํด๋์ค๋ฅผ ์์ด ์๋ก์ด ํ๋์ ํด๋์ค๋ฅผ ๋ฐํํ๋ ๊ฐ๋ ์ ๋๋ค.
import { mixin } from '@nestjs/common';
type Constructor<T = object> = new (...args: any[]) => T;
function mixinClass<T extends Constructor>(Base: T) {
class Class extends Response<T> {
property: T;
}
return class NewResponse extends mixin(Class) {};
}
์ ์ฝ๋๋ TypeScript์์ ๊ธฐ์กด ํด๋์ค๋ฅผ ํ์ฅํ๊ฑฐ๋ ๋์ ์ผ๋ก ์๋ก์ด ํด๋์ค๋ฅผ ์์ฑํ๋ ๋ฏน์ค์ธ ํจํด์ ๊ตฌํํ ๊ฒ์
๋๋ค.
๋๋ถ์ ์ปดํ์ผ ์์ ์ ์กฐํฉ๋๋ ์ ๋ค๋ฆญ์ ์ฐํํด ๋ฐํ์์ ๋์ ์ผ๋ก ํด๋์ค๊ฐ ํฉ์ณ์ง๊ฒ ๋์ด ์ค์จ์ด๊ฑฐ์ ํ์
์ ํ์ํ ์ ์๊ฒ ๋ฉ๋๋ค.
'NestJS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[NestJS] PM2 ์๊ฐ๋ถํฐ Docker + Github Action์ ํ์ฉํ ๋ฐฐํฌ๊น์ง (0) | 2024.11.24 |
---|---|
NestJS์ prisma๋ก ํธ๋ฆฌํ๊ฒ CRUD ๊ตฌํํ๊ธฐ (0) | 2024.11.12 |
Docker๋ก PostgreSQL ์ค์นํ๊ณ NestJS + prisma ์ฐ๋ํ๊ธฐ (0) | 2024.10.31 |
NestJS ํ๋ก์ ํธ ์์๊ณผ ๊ตฌ์กฐ์ ์ดํด (0) | 2024.10.28 |
๋๊ธ