SwaggerModule
는 라우트 핸들러에서 @Body()
, @Query()
, @Param()
데코레이터를 검색하여 API 문서를 생성한다. 또한 리플렉션을 활용하여 해당 모델 정의를 생성한다.
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
명시적으로 body 정의를 설정하려면
@ApiBody()
데코레이터를 사용
CreateCatDto
를 기반으로 Swagger UI에서 다음 모델 정의가 생성된다.
모델 정의가 빈 것처럼 보이지만 클래스에는 몇 가지 속성이 선언되어 있다.
SwaggerModule에 클래스 속성을 표시하려면 속성에 @ApiProperty()
데코레이터를 지정하거나 이를 자동으로 수행하는 CLI 플러그인을 사용해야한다.
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Hint!
각 속성을 수동으로 주석 처리하는 대신, 이 작업을 자동으로 수행하는 Swagger 플러그인을 사용하는 것을 고려하자.
브라우저를 열고 생성된 CreateCatDto 모델을 확인하면 변화가 있다.
또한 @ApiProperty() 데코레이터를 사용하면 다양한 Schema Object 속성을 설정할 수 있다.
@ApiProperty({
description: 'The age of a cat',
minimum: 1,
default: 1,
})
age: number;
Hint!
명시적으로@ApiProperty({ required: false })
를 입력하는 대신@ApiPropertyOptional()
단축 데코레이터를 사용할 수 있다.
속성의 유형을 명시적으로 설정하려면 type 키를 사용하자.
@ApiProperty({
type: Number,
})
age: number;
속성이 배열인 경우 아래와 같이 배열 유형을 수동으로 표시해야한다.
@ApiProperty({ type: [String] })
names: string[];
Hint!
배열을 자동으로 감지할 수 있는 Swagger 플러그인을 고려하자!
배열인 경우 배열 유형을 배열의 첫 번째 요소로 포함하거나 isArray
속성을 true
로 설정하자.
클래스 간에 환형 종속성이 있는 경우, SwaggerModule
에 유형 정보를 제공하기 위해 지연 함수(lazy function)를 사용하자.
@ApiProperty({ type: () => Node })
node: Node;
위의 코드 예시에서 사용한 @ApiProperty({ type: () => Node })
는 이러한 환형 종속성 상황에서 유용하게 사용되는 패턴이다. Node 클래스를 참조할 때 직접 유형을 명시하지 않고, 대신 함수를 사용하여 유형 정보를 제공한다. 함수가 호출될 때 Node 클래스를 반환하므로 SwaggerModule는 이 유형 정보를 사용하여 node 프로퍼티의 유형을 알 수 있다.
TypeScript는 제네릭 또는 인터페이스에 대한 메타데이터를 저장하지 않으므로, DTO에서 제네릭 또는 인터페이스를 사용할 때 SwaggerModule
은 런타임에서 모델 정의를 올바르게 생성하지 못할 수 있다.
createBulk(@Body() usersDto: CreateUserDto[])
이 제한을 극복하려면 유형을 명시적으로 설정해야한다.
@ApiBody({ type: [CreateUserDto] })
createBulk(@Body() usersDto: CreateUserDto[])
열거형(enum)을 식별하려면 @ApiProperty
에 enum
속성을 수동으로 설정해야 한다. 값 배열과 함께 enum
속성을 사용한다.
@ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
role: UserRole;
대신 다음과 같이 실제 TypeScript enum을 정의해서 사용할 수 있다.
export enum UserRole {
Admin = 'Admin',
Moderator = 'Moderator',
User = 'User',
}
그런 다음 @Query() 매개 변수 데코레이터와 함께 @ApiQuery() 데코레이터를 사용하여 enum을 직접 사용할 수 있다.
@ApiQuery({ name: 'role', enum: UserRole })
async filterByRole(@Query('role') role: UserRole = UserRole.User) {}
isArray
를 true
로 설정하면 enum
을 다중 선택으로 선택할 수 있다.
enum
속성은 매개 변수에 Enum의 parameter
를 추가한다.
- breed:
type: 'string'
enum:
- Persian
- Tabby
- Siamese
위 명세는 대부분의 경우에 잘 작동한다. 그러나 명세서를 입력으로 사용하고 클라이언트 측 코드를 생성하는 도구를 활용하는 경우, 생성된 코드에 중복된 열거형이 포함되는 문제가 발생할 수 있다.
// generated client-side code
export class CatDetail {
breed: CatDetailEnum;
}
export class CatInformation {
breed: CatInformationEnum;
}
export enum CatDetailEnum {
Persian = 'Persian',
Tabby = 'Tabby',
Siamese = 'Siamese',
}
export enum CatInformationEnum {
Persian = 'Persian',
Tabby = 'Tabby',
Siamese = 'Siamese',
}
Hint!
위의 코드 스니펫은 NSwag라는 도구를 사용하여 생성된다.
여기에서 동일한 열거형이 두 번 생성되었음을 확인할 수 있다.
이 문제를 해결하려면 데코레이터에서 enum
속성과 함께 enumName
을 전달할 수 있다.
export class CatDetail {
@ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
breed: CatBreed;
}
enumName
속성을 사용하면 @nestjs/swagger
가 CatBreed
를 자체 스키마로 변환하여 CatBreed
열거형을 재사용할 수 있게 된다.
CatDetail:
type: 'object'
properties:
...
- breed:
schema:
$ref: '#/components/schemas/CatBreed'
CatBreed:
type: string
enum:
- Persian
- Tabby
- Siamese
Hint!
enum
을 속성으로 사용하는 데코레이터는 모두enumName
을 사용할 수 있다.
특정 시나리오(예: deeply nested arrays, matrices)에서 유형을 수동으로 설명하고 싶을 수 있다.
@ApiProperty({
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
})
coords: number[][];
비슷하게, 컨트롤러 클래스에서 입력 및 출력 콘텐츠를 수동으로 정의하려면 schema 속성을 사용하자.
@ApiBody({
schema: {
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
},
})
async create(@Body() coords: number[][]) {}
컨트롤러에서 직접 참조되지 않지만 Swagger
모듈에서 검사해야 하는 추가 모델을 정의하려면 @ApiExtraModels()
데코레이터를 사용하면된다.
@ApiExtraModels(ExtraModel)
export class CreateCatDto {}
Hint!
특정 모델 클래스에 대해 @ApiExtraModels()를 한 번만 사용하면 된다.
또는 SwaggerModule#createDocument()
메서드에 extraModels
속성이 지정된 옵션 객체를 전달할 수 있다.
const document = SwaggerModule.createDocument(app, options, {
extraModels: [ExtraModel],
});
모델을 참조하기 위해 getSchemaPath(ExtraModel) 함수를 사용할 수 있다.
'application/vnd.api+json': {
schema: { $ref: getSchemaPath(ExtraModel) },
},
스키마를 결합하기 위해 oneOf, anyOf 또는 allOf 키워드를 사용할 수 있다.
이러한 키워드는 스키마를 복합하거나 복수의 스키마를 결합할 때 사용된다.
// oneOf
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
})
pet: Cat | Dog;
위의 예시에서 pet은 Cat 스키마 또는 Dog 스키마 중 하나일 수 있음을 나타낸다.
type Pet = Cat | Dog;
@ApiProperty({
type: 'array',
items: {
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
},
})
pets: Pet[];
이 예시에서 pets는 Cat 또는 Dog와 같은 스키마를 가진 다중 스키마의 배열로 정의된다.
oneOf 키워드를 사용하여 배열의 각 원소는 여러 스키마 중 하나일 수 있음을 나타낸다.
getSchemaPath()
함수는 @nestjs/swagger
에서 가져올 수 있는 함수로, 해당 스키마에 대한 $ref
를 생성하는 데 사용된다.이를 통해 스키마를 참조하고 재사용할 수 있다.