NestJS에 Swagger 적용하기 (API 인터페이스 명세 자동생성)

박재하·2023년 11월 15일
1

서론

저희 프로젝트의 특성상 프론트의 Three.js관련 Task가 많아 백에서 담당하는 API 구현이 프론트보다 빨리 진행되고 있습니다.

그래서 프론트와 백 담당 인원이 협의하여 API 인터페이스 문서로 명세하기보다 구두로 기본적인 협의만 하고, Swagger로 인터페이스 명세를 프론트 파트를 담당하는 그룹원에게 공유하며, 계속 수정해나가는 전략으로 개발을 진행하기로 결정했습니다.

그럼 NestJS에 Swagger를 적용하는 방법을 알아보죠.

NestJS에 Swagger 적용하기

설치

npm i @nestjs/swagger # npm
# 또는
yarn add @nestjs/swagger # yarn

저희는 yarn berry로 의존성을 관리해서 yarn도 넣어봤습니다. 실제론 모노레포라서 다음처럼 추가해줍니다.

yarn workspace server add @nestjs/swagger

server 프로젝트쪽 package.json에 있어야 정상!

자동완성 안되는 문제 해결

근데 보통 모노레포 + yarn berry + vscode면 이 시점에서 코드는 에러가 없는데 자동완성이 안된다..

yarn workspace server install # 결론: 안해도 됨.

기존엔 VSCode 이런 식으로 설치를 해줘야 자동완성 및 import가 되는 줄 알았는데 (안되면 이것도 해보시길),
자동완성 안되는 이유는 그냥 VSCode가 인식을 못해주는 거여서, 리로드를 하면 되는거였다.

학습메모 5 참고해서, cmd+shift+preload 검색, 개발자: 창 다시 로드 클릭

스크린샷 2023-11-15 오후 10 51 22 스크린샷 2023-11-15 오후 10 53 33

드디어 됨 아이고 베리야..;

적용

적용은 bootstrap에서 해줍니다. src폴더의 main.ts

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

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

원래 이건데, create랑 listen 사이에 swagger setup 추가해주겠습니다.

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

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

	const config = new DocumentBuilder()
		.setTitle('B1G1 API')
		.setDescription('The B1G1 API description')
		.setVersion('1.0')
		.addTag('b1g1')
		.build();

	const document = SwaggerModule.createDocument(app, config);
	SwaggerModule.setup('api', app, document);

	await app.listen(3000);
}
bootstrap();
스크린샷 2023-11-15 오후 10 56 47

이렇게 하면 일단 기본 틀은 생성됨. 하지만 암것도 없쥬?

설명, 예시 추가하기

이제 Swagger에서 제공하는 다양한 Decorator를 이용해서 설명을 달아야 합니다.

크게 API 파트Schema 파트가 있는데요, 각각을 알아보죠. (학습메모 2, 3 참고)

APIs

API 명세를 꾸며주고 싶다면 Controller 코드에 추가시켜주면 됩니다.

// board.controller.ts
...
@Controller('board')
export class BoardController {
	constructor(private readonly boardService: BoardService) {}

	@Post()
	create(@Body() createBoardDto: CreateBoardDto): Promise<Board> {
		return this.boardService.create(createBoardDto);
	}
  
	@Get(':id')
	getBoardById(@Param('id') id: string): Promise<Board> {
		return this.boardService.getBoardById(+id);
	}
}

원래 이런 식이였다면 (귀찮으니까 메소드 2개에 대해서만 해보자)

// board.controller.ts
...
import {
	ApiBadRequestResponse,
	ApiCreatedResponse,
	ApiNotFoundResponse,
	ApiOkResponse,
	ApiOperation,
	ApiTags,
} from '@nestjs/swagger';

@Controller('board')
@ApiTags('게시글 API')
export class BoardController {
	constructor(private readonly boardService: BoardService) {}

	@Post()
	@ApiOperation({ summary: '게시글 생성', description: '게시글을 생성한다.' })
	@ApiCreatedResponse({ status: 201, description: '게시글 생성 성공' })
	@ApiBadRequestResponse({ status: 400, description: '잘못된 요청' })
	create(@Body() createBoardDto: CreateBoardDto): Promise<Board> {
		return this.boardService.create(createBoardDto);
	}

	@Get(':id')
	@ApiOperation({ summary: '게시글 조회', description: '게시글을 조회한다.' })
	@ApiOkResponse({ status: 200, description: '게시글 조회 성공' })
	@ApiNotFoundResponse({ status: 404, description: '게시글 없음' })
	getBoardById(@Param('id') id: string): Promise<Board> {
		return this.boardService.getBoardById(+id);
	}
}

이렇게 설명을 달아주면 된다! Response는 종류별로 다 데코레이터가 따로 있어서 제공해주는거 위주로 골라서 써주시면 되겠다. 200이면 @ApiOkResponse, 404@ApiNotFoundResponse 등등~

스크린샷 2023-11-15 오후 11 28 26 스크린샷 2023-11-15 오후 11 28 33

그럼 이렇게 생성된다. 참하죠?

또 저기 있는 Try It Out 버튼을 누르면 직접 테스트도 진행해볼 수 있다.

스크린샷 2023-11-15 오후 11 13 25 스크린샷 2023-11-15 오후 11 13 37 스크린샷 2023-11-15 오후 11 31 23 스크린샷 2023-11-15 오후 11 31 33

Postman 필요없네! (사실 불편함 그리고 진짜 데이터가 DB에 들어감)

Schemas

// create-board.dto.ts
export class CreateBoardDto {
	title: string;
	content: string;
	author: string;
}

원래 이런 식이였다면

// create-board.dto.ts
import { ApiProperty } from '@nestjs/swagger';

export class CreateBoardDto {
	@ApiProperty({ description: '게시글 제목' })
	title: string;

	@ApiProperty({ description: '게시글 내용' })
	content: string;

	@ApiProperty({ description: '게시글 작성자' })
	author: string;
}

이렇게 설명을 달아준다. copilot과 함께라면 우리는 왠만해선 탭만 치면 된다^^!

스크린샷 2023-11-15 오후 11 20 29

그럼 요렇게 스키마 탭에서 예쁜 DTO 명세를 볼 수 있게 된다!

결론

써보세요 만들면 괜히 기분좋음

학습 메모

  1. NestJS swagger 공식 문서
  2. [Nest.js] swagger 적용하기
  3. NestJS에서 Swagger를 사용하는 방법
  4. 프로젝트 yarn berry 도입
  5. VSCode 개발 창 리로드
profile
해커 출신 개발자

1개의 댓글

comment-user-thumbnail
2023년 11월 16일

지립니다 재하님,, 하 자극 받는다 열심히 해야겠다

답글 달기