Skip to content

Swagger의 사용 ​

DTO ​

Github Link

단일 Object에 사용 ​

typescript
export class CreateCatDto {
  @IsNotEmpty()
  @IsString()
  name: string;

  @IsNotEmpty()
  @IsNumber()
  age: number;

  @IsNotEmpty()
  @IsString()
  breed: string;
}

이전에 만들어 놓았던 dto에서 swagger를 사용해보도록 하겠습니다.

typescript
export class CreateCatDto {
  @ApiProperty({
    description: "The name of a cat",
    example: "Kitty",
    required: true,
    type: String,
  })
  @IsNotEmpty()
  @IsString()
  name: string;

  @ApiProperty({
    description: "The age of a cat",
    example: 3,
    required: true,
    type: Number,
  })
  @IsNotEmpty()
  @IsNumber()
  age: number;

  @ApiProperty({
    description: "The breed of a cat",
    example: "Scottish Fold",
    required: true,
    type: String,
  })
  @IsNotEmpty()
  @IsString()
  breed: string;
}

이렇게 만들어놓으면 아래와 같이문서가 바뀌었을 겁니다. alt

create를 만들었으니 update도 똑같이 하면 되겠죠?
그런데 코드가 중복되는 것을 볼 수 있습니다.
코드의 중복을 줄이기 위해 코드를 좀 변경해보도록 하겠습니다.

typescript
import { ApiProperty, PartialType } from "@nestjs/swagger";
import { IsNotEmpty, IsNumber, IsString } from "class-validator";

export class CreateCatDto {
  @ApiProperty({
    description: "The name of a cat",
    example: "Kitty",
    required: true,
    type: String,
  })
  @IsString()
  @IsNotEmpty()
  name: string;

  @ApiProperty({
    description: "The age of a cat",
    example: 3,
    required: true,
    type: Number,
  })
  @IsNumber()
  @IsNotEmpty()
  age: number;

  @ApiProperty({
    description: "The breed of a cat",
    example: "Scottish Fold",
    required: true,
    type: String,
  })
  @IsString()
  @IsNotEmpty()
  breed: string;
}

export class UpdateCatDto extends PartialType(CreateCatDto) {}

NestJs OpenApi Mapped Types
parialtype은 validate를 모두 optional로 변경해 줍니다.
이외에도 골라쓰는 PickType, 원하는건 뺴는 OmiType, 두개를 합치는 IntersectionType 있습니다.

Object가 List로 요청이 온다면? ​

프로젝트를 하다보면 데이터를 받는 여러가지 경우가 있습니다.
많은 데이터를 받는다면 단순 배열의 형태나 배열안에 객체가 담겨서 올 수도 있겠죠.
그럼 이때 dto를 어떻게 만들고 validation은 어떻게 해야할까요?
컨트롤러에 아래와 같이 라우트를 만들면 마음과 같이 나오지 않을겁니다.

typescript
@Post('many')
createMany(@Body() createCatDto: CreateCatDto[]): CreateCatDto[] {
  return createCatDto;
}

example도 그렇고 뭔가 뭉게져 있는 모습을 볼 수 있죠.
nestjs의 공식문서는 다음과 같은 방법을 제시하고 있습니다.

typescript
@ApiBody({ type: [CreateCatDto] })
@Post('many')
createMany(@Body() createCatDto: CreateCatDto[]): CreateCatDto[] {
  return createCatDto;
}

그럼 잘 나오게 될겁니다.
하지만.. validation이 안되는 걸 확인할 수 있습니다.
몇가지를 뺴서 전송해보세요. 그럼 에러없이 값을 반환하는 모습을 볼 수 있습니다.
나는 문서도 잘나오고 validation도 하고 싶은데 그럼 어떻게해야할까요?
DTO를 추가해봅시다.

typescript
export class ArrayCreateCatDto {
  @ApiProperty({
    description: 'The datas of a cat',
    required: true,
    type: [CreateCatDto],
  })
  @ValidateNested({ each: true })
  @Type(() => CreateCatDto)
  @IsArray()
  data: CreateCatDto[];
}

@Post('many')
createMany(@Body() createCatDto: ArrayCreateCatDto): ArrayCreateCatDto {
  return createCatDto;
}

이렇게 dto를 만들고 컨트롤러에 타입을 지정해주면 data key값 안에 배열 형태로 고양이정보의 객체가 있는 모습을 볼 수 있습니다.
또한 validation도 잘 되는 것을 볼 수 있습니다.
단순이 string[]의 형태로 하고 싶다면 아래와 같이하면 됩니다.

typescript
export class OnlyArrayDto {
  @ApiProperty({
    isArray: true,
    example: ["a", "b"],
  })
  @IsArray()
  @IsNotEmpty({ each: true })
  @IsString({ each: true })
  prescription_ids: string[];
}

Controller ​

NestJs Operations
Github Link

typescript
@ApiTags("cats")
@Controller("cats")
export class CatsController {
  private cats: CatsDto = {
    id: 1,
    name: "Kitty",
    age: 3,
    breed: "Scottish Fold",
  };

  @ApiCreatedResponse({
    description: "생성된 고양이를 반환합니다.",
    type: CatsDto,
  })
  @ApiOperation({
    summary: "고양이 생성",
    description:
      "이 API는 새로운 고양이를 생성합니다.<br/>고양이의 이름, 나이, 품종을 입력하세요.",
  })
  @Post()
  create(@Body() createCatDto: CreateCatDto): CatsDto {
    return this.cats;
  }
}

ApiTags ​

api 태그는 api의 문서를 볼 때 그룹을 만들어줘서 구분하기 편하게 해줍니다.

ApiOperation ​

api가 어떤 용도로 쓰이는지 좀더 상세하게 기재할 수 있습니다.
주로 summary, description, deprecated를 사용합니다.

응답 데코레이터 ​

예상되는 응답코드를 지정해줘서 예상되는 성공이나 실패에 관한 내용을 작성할 수 있습니다.
예시에서는 성공에 대한 데코레이터만 사용했습니다.

성공 응답 데코레이터 ​

응답코드
@ApiOkResponse()200 OK - 요청이 성공적으로 처리됨.
@ApiCreatedResponse()201 Created - 요청이 성공적으로 처리되어 새로운 리소스가 생성됨.
@ApiAcceptedResponse()202 Accepted - 요청이 접수되었으나, 처리가 완료되지 않음.
@ApiNoContentResponse()204 No Content - 요청이 성공적으로 처리되었으나, 컨텐츠를 반환하지 않음.
@ApiMovedPermanentlyResponse()301 Moved Permanently - 요청한 리소스의 URI가 변경되었음.
@ApiFoundResponse()302 Found - 요청한 리소스가 일시적으로 다른 URI로 변경되었음.

클라이언트 에러 응답 데코레이터 ​

응답코드
@ApiBadRequestResponse()400 Bad Request - 서버가 요청을 이해할 수 없음.
@ApiUnauthorizedResponse()401 Unauthorized - 요청이 인증을 필요로 함.
@ApiNotFoundResponse()404 Not Found - 요청한 리소스를 찾을 수 없음.
@ApiForbiddenResponse()403 Forbidden - 서버가 요청을 거부함.
@ApiMethodNotAllowedResponse()405 Method Not Allowed - 요청한 메소드는 허용되지 않음.
@ApiNotAcceptableResponse()406 Not Acceptable - 요청한 리소스의 미디어 타입이 클라이언트에 의해 허용되지 않음.
@ApiRequestTimeoutResponse()408 Request Timeout - 서버가 요청을 기다리다가 타임아웃 됨.
@ApiConflictResponse()409 Conflict - 요청이 서버의 현재 상태와 충돌.
@ApiPreconditionFailedResponse()412 Precondition Failed - 서버가 요청의 전제 조건을 만족하지 못함.
@ApiTooManyRequestsResponse()429 Too Many Requests - 클라이언트가 지정된 시간 동안 너무 많은 요청을 보냄.
@ApiGoneResponse()410 Gone - 요청한 리소스가 영구적으로 이동되었으며, 더 이상 사용할 수 없음.
@ApiPayloadTooLargeResponse()413 Payload Too Large - 요청 페이로드가 너무 큼.
@ApiUnsupportedMediaTypeResponse()415 Unsupported Media Type - 요청이 지원되지 않는 미디어 타입임.
@ApiUnprocessableEntityResponse()422 Unprocessable Entity - 요청 구조는 올바르나, 의미상 오류가 있어 처리할 수 없음.

서버 에러 응답 데코레이터 ​

응답코드
@ApiInternalServerErrorResponse()500 Internal Server Error - 서버가 요청을 처리할 수 없음.
@ApiNotImplementedResponse()501 Not Implemented - 서버가 요청된 기능을 수행할 수 없음.
@ApiBadGatewayResponse()502 Bad Gateway - 서버가 게이트웨이나 프록시 역할을 하며, 상위 서버로부터 잘못된 응답을 받음.
@ApiServiceUnavailableResponse()503 Service Unavailable - 서버가 일시적으로 요청을 처리할 수 없음, 일반적으로 유지보수 또는 과부하로 인해 발생.
@ApiGatewayTimeoutResponse()504 Gateway Timeout - 서버가 게이트웨이나 프록시 역할을 하며, 상위 서버로부터 응답을 적시에 받지 못함.

기타 응답 데코레이터 ​

응답코드
@ApiDefaultResponse()일반적으로 예상치 못한 다른 모든 종류의 에러 상황을 문서화할 때 사용됩니다. 이는 특정 HTTP 상태 코드에 국한되지 않고, 다양한 종류의 에러 응답을 나타낼 수 있는 기본 응답을 의미합니다.