NESTJS로 Swagger 문서화 하기

fana·2021년 1월 25일
7

nestjs

목록 보기
2/2
post-thumbnail

오늘은 nestjs로 Swagger API 문서 만들었던 작업을 정리하려고 한다.
지금껏 nodejs express를 이용해 여러 API들을 만들면서 항상 골칫거리였던 것이 API 문서화였다. nestjs가 나오기 전 Express API를 문서화 하는 방법에는 여러가지가 있었는데 내가 선택했던 방법은 주석을 파싱해서 Swagger 문서를 만드는 방법이었다.

기존 방법

이 방법은 Swagger 문서를 만드는 swagger-jsdocswagger-ui-express가 코드 내 모든 @swagger 데코레이터가 달린 주석들을 파싱해 API문서들을 만들어 준다.

Definition

일단 굉장히 번거롭고 좋지 않다고 생각하기 때문에 자세히 언급하지는 않겠지만 대충 다음과 같이 사용했다.

벌써 가슴이 답답해진다.. 위 예에는 User 모델의 필드를 id, name만 쓴 것인데 실제 모델에는 훨씬 많은 필드들이 있다.
그걸 하나하나 작성해야하고 심지어 이런 작업을 모든 모델에 대해서 반복해야 한다.

여기서 끝도 아니다.

API Endpoint

각각의 API 엔드포인트들에도 위와 비슷한 명세를 하나하나 해줘야한다.
예시로 한가지만 살펴보면

위 주석은 로그인하는 엔드포인트의 예시이다. yaml 형식으로 명세하는 위 방식은 indent가 하나라도 어긋나면 바로 에러를 뱉어낸다. 이러한 작업들을 각 API 엔드포인트마다 다 해줘야 한다. 정말 미친 방법이 아닐 수 없다. 아마 편하게 하는 방법을 찾지 못한 내가 문제였을 거라 생각함

nestjs로 명세

nestjs가 좋다고 좋다고 하는 말을 여러번 듣다가 개인적으로 연습용 작은 프로젝트를 만들면서 문서화를 해봤는데, 정말 믿기 어려울 정도로 편했다.
사실 내가 재직중인 회사의 서비스는 Ruby on Rails로 만들어져 있다. 레일즈는 Convention Over Configuration이라는 말 아래 갱장히 빡빡하지만 하라는대로만 하면 어느정도의 어플리케이션을 금방 만들어낼 수 있는 프레임워크다. 반면 Express로 API를 만들라치면 망망대해나 다름없어서, 도대체 어디서부터 어떻게 시작해야할지 막막하다.
와중에 nestjs라는 프레임워크가 드디어 나와서 망망대해에서 헤맬 일은 많이 줄거라 생각한다.

Definition

각설하고 nestjs로 명세는 다음과 같이 하였다.

User모델이다. 데코레이터가 굉장히 많이 보이는데 @ApiProperty() 데코레이터는 nestjs/swagger에 자체 내장되어있는 데코레이터이다.
나머지 데코레이터들은 typeorm에서 사용하는 데코레이터이다.
기존 방법으로는 ORM이든 ODM이든 모델의 명세 따로, API 문서화를 위한 Definition 명세를 따로했다.
@nestjs/swagger의 이 데코레이터를 사용하면, ORM 모델과 API definition을 위한 명세를 한번만 하면 된다.

API Endpoint

로그인 엔드포인트는 위와 같이 작성했다.

  • @ApiTag()로 같은 태그가 달린 엔드포인트들을 Swagger에서 알아서 묶음해준다.
  • @ApiResponse() 클래스 자체에 달린 데코레이터는 해당 클래스의 매서드들에 공통된 응답 코드와 description을 명세해준다.
  • @ApiBody() 데코레이터로 요청 바디 명세를 한다.
  • @ApiCreatedResponse() 데코레이터로 해당 엔드포인트의 성공적인 응답 명세를 한다.

위에 언급하지 않은 Dto에 대한 명세, 기타 SwaggerOptions, SwaggerDocument에 대한 설정들은 아래 Full Implementations에서 다루기로 하고,
여기까지 성공적으로 작성했다면 아름다운 다음과 같은 Swagger명세를 확인할 수 있게 된다.

이렇게 하고보니 POST요청에 200 응답 성공 코드는 잘못 들어갔다.

Full Implementations

자! 그럼 기존 방법이고 뭐고 nestjs로 Swagger 명세를 처음부터 끝까지 이번에 내가 어떻게 했는지 기록으로 남기자

nestjs new project!

nestjs를 글로벌로 설치한 이후,

$ nest new project-name

으로 새로운 프로젝트를 만들었다.

install dependencies

기본적으로 swagger명세에 필요한 패키지들을 설치해준다. @nestjs/swagger가 기본 내장이었던가? 가물가물하다.

$ npm install @nestjs/swagger swagger-ui-express

프로젝트 구조

처음 nestjs 프로젝트를 만들고 나면, 다음과 같은 디렉토리 구조를 가지고 있다.

이를 다음과 같은 프로젝트 디렉토리 구조로 추가하였다.

나머지 부분은 다 같고 변경된 부분만 설명해보자면,

  • src/api/base.document.ts Swagger의 기본 빌더가 들어간다.
  • api/entities에 이 API의 entity들의 명세들을 담았다. 그 아래에 response_entities가 있고 그 하위에 auth 도메인을 위한 명세가 있다.
  • api/v1은 v1 API들이 담긴 곳이다, 그 하위에 auth 도메인을 위한 컨트롤러, 모듈, 서비스, dto, interfaces가 있다.
  • src/entity가 추가되었고, 그 하위에 typeorm의 Entity들이 들어갈 것이다.

Implementation

로그인 API를 예시로 Swagger 문서화를 해볼것이다.

Swagger Document Build

가장 기본인 Swagger Document 빌드를 해보자. 위 디렉토리 구조에서 src/api/base.document.ts 파일이다.

이제 이 클래스를 nestjs의 default 메인파일인 src/main.ts에 마운트 해준다.

  • app.setGlobalPrefix('/api/v1');를 통해 default 라면 localhost:3000/의 API 주소가 전역적으로 localhost:3000/api/v1로 변경된다.
  • 중간 부분에 빌드된 BaseAPIDocumentationapp에 마운트 한다.
  • SwaggerModule.setup('api/v1/docs', app, document);로 Swagger 명세 주소를 localhost:3000/api/v1/docs가 되도록 해준다.

이 상태에서 해당 주소로 들어가보면 다음과 같은 화면이 뜬다.

로그인 API 추가

이제 로그인하는 API 엔드포인트를 추가해보자. 뭐 어떤식으로 로그인하는지 중간단계는 모두 생략하고, 이메일과 비밀번호를 입력받으면 무조건 로그인이 됐다는 성공 응답만 보내기로 한다.
nestjs의 자세한 사용법은 언급하지 않고, Swagger 문서화에 필요한 것들만 언급하자.

  • @ApiTags('Auth')로 Swagger에 'Auth'태그를 만들어 준다. 현재 저 데코레이터가 AuthController 클래스 위에 있으므로, AuthController내에 있는 모든 매서드들이 'Auth' 태그 하위로 포함될 것이다.

위와 같이만 작성한다면 localhost:3000/api/v1/docs에 접속했을때 보이는 화면은 다음과 같다.

  • 현재 Responses의 Code 201은 nestjs@Post를 사용했을 때 기본값으로 설정되어 있는 것이다.
  • 위 API에 요청하기 위해서는 어떤 파라미터 혹은 바디를 보내야하는지 명세되지 않아 요청 자체를 보낼 수가 없다.
  • 또 요청을 보낼 수 있다고 하더라도, 응답 명세가 포함되지 않아 실제 해당 API를 사용하는 사람들은 직접 요청을 보내 출력을해서 어떤 응답명세가 오는지 눈으로 확인할 수 밖에 없다.

이제 이 명세에 어떻게 요청 바디를 명세하는지 추가해보자.

LoginDto 추가


위 경로에 login.dto.ts파일에 다음과 같은 Dto클래스를 추가한다. 이후에 이를 auth.controller.ts에 다음과 같이 추가해준다.

  • @ApiBody() 데코레이터에 type: LoginDto가 추가되었다. 이제 localhost:3000/api/v1/docs에 접속해보면, 어떤 요청 바디를 받는지 명세가 추가되어있다.

LoginResponse 추가

자! 이제 저 API에 요청 바디 명세대로 요청을 보낸다면 어떤 응답이 올지 명세하는 단계이다.
아까 디렉토리 구조에서 src/api/entities/response_entities에 다음과 같은 파일들을 작성해준다.
먼저,

  • 가장 기본이 될 API 응답 명세 클래스를 만들어준다. 가장 기본적으로 API 응답 명세에 코드, 메시지를 보내준다는 뜻이다.
  • src/api/entities/response_entities/base.response.ts에 작성하자.

이제 이 기본 응답 명세를 커스터마이징 해줄 필요가 있다. 로그인 API에서 이메일, 비밀번호가 오면 { code, message, data: User } 와 같은 형태로 응답을 보내기 위해서다. src/api/entities/response_entities/auth/login.response.ts에 작성해보자.

다음과 같이 응답 명세를 추상클래스로 만들어보았다. LoginResponse클래스가 BaseResponse를 상속했으니 code, message 프로퍼티는 가지고있다. 여기에 data를 추가해, 또 다른 추상 클래스인 LoginResponseData를 명세해주었다. 이제 이 LoginResponse를 다시, auth.controller.ts로 가지고 간다.

  • @ApiCreatedResponse() 데코레이터에 descriptiontype을 작성해준다. 타입은 방금전 작성했던 LoginResponse이다.
    이제 다시 localhost:3000/api/v1/docs에 접속해보면,

응답 명세가 추가되었다! 여기에는 token이 추가되어있는데 실수임

마지막

마지막으로 @nestjs/swagger@ApiProperty() 데코레이터를 Dto, Response, 뭐 여기저기 사용하다보면 localhost:3000/api/v1/docs 접속시 하단에 다음과 같이 Definitions들이 엄청 쌓이게 된다.

이건 뭐 사용하는 사람 입장에서 있는게 더 좋을 수 있지만, 개인적으로는 없애고 싶어서 disable하는 방법을 찾아보았다.

src/main.ts의 Swagger 마운트를 했던 코드에서

요렇게 swaggerOptions: { defaultModelsExpandDepth: -1 }을 추가해주면 사라진다.

1개의 댓글