기주

[nest.js] swagger 적용하기 본문

TIL

[nest.js] swagger 적용하기

기주그지마 2024. 5. 29. 16:04

 

 

왜 swagger를 쓰는가?)

 

swagger 를 사용하면 api명세서를 코드내에서 자동으로 쓸 수 있게해준다

 

api가 많아질경우 api명세서를 쓰는 시간을 훨씬 더 단축시킬 수 있고,

 

백엔드 작업을 하다보면 놓친부분이나, api명세서 변경이 꽤 많이 일어난다.

 

 

그럴때마다 코드와 api명세서를 둘다 수정하는 것은 시간도 오래걸리고, 만약 코드만 수정하고, api명세서 수정하는 것을 깜빡하면

 

프론트엔드쪽에서는 잘못된 명세서를 많은 시간을 낭비하게된다.  

 

 

 

 

 

 

swagger 설치하기

 

npm install --save @nestjs/swagger

 

 

 

 

main.ts에 swagger 적용하기)

 

main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('nest-practice-youtube')             // 명세서 제목
    .setDescription('nest practice description')   // 명세서 설명
    .setVersion('1.0')                             // 명세서 버전
    .addTag('cats')                                // 명세서 태그 
    .build();                                      // 명세서 생성(필수)
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();

https://docs.nestjs.com/openapi/introduction

 

 

ㄴ 별도의 파일에 swagger config를 작성해준뒤 main.ts에서 불러올 수도있다.

 

 

 

 

 

 

 

 

api에 swagger 적용하기)

 

@ApiTags('channel')
@Controller('channel')
export class ChannelController {
  constructor(private channelService: ChannelService) {}

  //회원가입
  @Post()
  @ApiOperation({ summary: '회원가입' })
  @ApiCreatedResponse({ description: 'created id' })
  @ApiConflictResponse({ description: 'id duplicate' })
  async signUp(
    @Body(ValidationPipe) signUpDto: SignUpDto,
  ): Promise<ChannelEntity> {
    return await this.channelService.signUp(signUpDto);
  }
  
   //채널상세보기
  @Get(':channelIdx')
  @ApiOperation({ summary: 'get channel in detail' })
  @ApiParam({ name: 'channelIdx' })
  @ApiBadRequestResponse({ description: 'no channelIdx' })
  @ApiNotFoundResponse({ description: 'not found channel' })
  @ApiOkResponse({ description: 'ok' })
  @UseGuards(AuthGuard)
  async getChannelInfo(
    @GetUser() loginUser: LoginUser,
    @Param('channelIdx', ParseIntPipe) channelIdx: number,
  ): Promise<{
    channel: ChannelEntity;
    subscribe: boolean;
  }> {
    const subscribeState = await this.channelService.getSubscrbeState(
      loginUser.idx,
      channelIdx,
    );
    const channel = await this.channelService.getChannelByIdx(channelIdx);

    return {
      channel,
      subscribe: subscribeState,
    };
  }

 

 

 

 

@ApiTags 

ㄴ api들을 태그별로 분류한다

ㄴ컨트롤러에 쓸 수도 있고, 개별api에 쓸 수도 있다

 

 

 

 

@ApiOperation

ㄴapi 설명

 

 

 

 

@ApiResponse

ㄴ 각각의 상태코드 기본값 예외처리로도 쓸 수 있음 (ex) @ApiCreatedResponse,  @ApiConflictResponse ... )

ㄴ 상태코드, 설명등을 추가할수 있음

 

 

 

 

@ApiBody

@ApiParam

@ApiQuery

 

ㄴ 필요한 body, param, query 지정

ㄴ변수명, 타입지정(dto, string)

 

 

 // SignupDto
 
 export class SignUpDto {
  @IsString()
  @Length(1, 20)
  @ApiProperty({
    example: 'testid',
    description: '1-20자',
  })
  id: string;

  @IsString()
  @Length(8, 20)
  @IsStrongPassword({
    minLength: 8,
  })
  @ApiProperty({
    example: 'testpw',
    description: '8-20자',
  })
  pw: string;

  @IsString()
  @Length(1, 10)
  @ApiProperty({
    example: 'testname',
    description: '1-10자',
  })
  name: string;
}
  
  
  
  
  //컨트롤러
  
  //회원가입api
  @Post()
  @ApiOperation({ summary: '회원가입' })
  @ApiBody({ type: SignUpDto })
  @ApiCreatedResponse({ description: 'created id' })
  @ApiConflictResponse({ description: 'id duplicate' })
  async signUp(
    @Body(ValidationPipe) signUpDto: SignUpDto,
  ): Promise<ChannelEntity> {
    return await this.channelService.signUp(signUpDto);
  }