오늘은 nestjs로 Swagger API 문서 만들었던 작업을 정리하려고 한다.
지금껏 nodejs express를 이용해 여러 API들을 만들면서 항상 골칫거리였던 것이 API 문서화였다. nestjs가 나오기 전 Express API를 문서화 하는 방법에는 여러가지가 있었는데 내가 선택했던 방법은 주석을 파싱해서 Swagger 문서를 만드는 방법이었다.
이 방법은 Swagger 문서를 만드는 swagger-jsdoc
및 swagger-ui-express
가 코드 내 모든 @swagger
데코레이터가 달린 주석들을 파싱해 API문서들을 만들어 준다.
일단 굉장히 번거롭고 좋지 않다고 생각하기 때문에 자세히 언급하지는 않겠지만 대충 다음과 같이 사용했다.
벌써 가슴이 답답해진다.. 위 예에는 User 모델의 필드를 id
, name
만 쓴 것인데 실제 모델에는 훨씬 많은 필드들이 있다.
그걸 하나하나 작성해야하고 심지어 이런 작업을 모든 모델에 대해서 반복해야 한다.
여기서 끝도 아니다.
각각의 API 엔드포인트들에도 위와 비슷한 명세를 하나하나 해줘야한다.
예시로 한가지만 살펴보면
위 주석은 로그인하는 엔드포인트의 예시이다. yaml
형식으로 명세하는 위 방식은 indent
가 하나라도 어긋나면 바로 에러를 뱉어낸다. 이러한 작업들을 각 API 엔드포인트마다 다 해줘야 한다. 정말 미친 방법이 아닐 수 없다. 아마 편하게 하는 방법을 찾지 못한 내가 문제였을 거라 생각함
nestjs가 좋다고 좋다고 하는 말을 여러번 듣다가 개인적으로 연습용 작은 프로젝트를 만들면서 문서화를 해봤는데, 정말 믿기 어려울 정도로 편했다.
사실 내가 재직중인 회사의 서비스는 Ruby on Rails
로 만들어져 있다. 레일즈는 Convention Over Configuration
이라는 말 아래 갱장히 빡빡하지만 하라는대로만 하면 어느정도의 어플리케이션을 금방 만들어낼 수 있는 프레임워크다. 반면 Express로 API를 만들라치면 망망대해나 다름없어서, 도대체 어디서부터 어떻게 시작해야할지 막막하다.
와중에 nestjs
라는 프레임워크가 드디어 나와서 망망대해에서 헤맬 일은 많이 줄거라 생각한다.
각설하고 nestjs로 명세는 다음과 같이 하였다.
User모델이다. 데코레이터가 굉장히 많이 보이는데 @ApiProperty()
데코레이터는 nestjs/swagger
에 자체 내장되어있는 데코레이터이다.
나머지 데코레이터들은 typeorm
에서 사용하는 데코레이터이다.
기존 방법으로는 ORM이든 ODM이든 모델의 명세 따로, API 문서화를 위한 Definition 명세를 따로했다.
@nestjs/swagger
의 이 데코레이터를 사용하면, ORM 모델과 API definition을 위한 명세를 한번만 하면 된다.
로그인 엔드포인트는 위와 같이 작성했다.
@ApiTag()
로 같은 태그가 달린 엔드포인트들을 Swagger에서 알아서 묶음해준다.@ApiResponse()
클래스 자체에 달린 데코레이터는 해당 클래스의 매서드들에 공통된 응답 코드와 description을 명세해준다.@ApiBody()
데코레이터로 요청 바디 명세를 한다.@ApiCreatedResponse()
데코레이터로 해당 엔드포인트의 성공적인 응답 명세를 한다.위에 언급하지 않은 Dto에 대한 명세, 기타 SwaggerOptions, SwaggerDocument에 대한 설정들은 아래 Full Implementations
에서 다루기로 하고,
여기까지 성공적으로 작성했다면 아름다운 다음과 같은 Swagger명세를 확인할 수 있게 된다.
이렇게 하고보니 POST요청에 200 응답 성공 코드는 잘못 들어갔다.
자! 그럼 기존 방법이고 뭐고 nestjs
로 Swagger 명세를 처음부터 끝까지 이번에 내가 어떻게 했는지 기록으로 남기자
nestjs를 글로벌로 설치한 이후,
$ nest new project-name
으로 새로운 프로젝트를 만들었다.
기본적으로 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들이 들어갈 것이다.로그인 API를 예시로 Swagger 문서화를 해볼것이다.
가장 기본인 Swagger Document 빌드를 해보자. 위 디렉토리 구조에서 src/api/base.document.ts
파일이다.
이제 이 클래스를 nestjs
의 default 메인파일인 src/main.ts
에 마운트 해준다.
app.setGlobalPrefix('/api/v1');
를 통해 default 라면 localhost:3000/
의 API 주소가 전역적으로 localhost:3000/api/v1
로 변경된다.BaseAPIDocumentation
을 app
에 마운트 한다.SwaggerModule.setup('api/v1/docs', app, document);
로 Swagger 명세 주소를 localhost:3000/api/v1/docs
가 되도록 해준다.이 상태에서 해당 주소로 들어가보면 다음과 같은 화면이 뜬다.
이제 로그인하는 API 엔드포인트를 추가해보자. 뭐 어떤식으로 로그인하는지 중간단계는 모두 생략하고, 이메일과 비밀번호를 입력받으면 무조건 로그인이 됐다는 성공 응답만 보내기로 한다.
nestjs
의 자세한 사용법은 언급하지 않고, Swagger 문서화에 필요한 것들만 언급하자.
@ApiTags('Auth')
로 Swagger에 'Auth'태그를 만들어 준다. 현재 저 데코레이터가 AuthController
클래스 위에 있으므로, AuthController
내에 있는 모든 매서드들이 'Auth' 태그 하위로 포함될 것이다.위와 같이만 작성한다면 localhost:3000/api/v1/docs
에 접속했을때 보이는 화면은 다음과 같다.
nestjs
의 @Post
를 사용했을 때 기본값으로 설정되어 있는 것이다.이제 이 명세에 어떻게 요청 바디를 명세하는지 추가해보자.
위 경로에 login.dto.ts
파일에 다음과 같은 Dto
클래스를 추가한다. 이후에 이를 auth.controller.ts
에 다음과 같이 추가해준다.
@ApiBody()
데코레이터에 type: LoginDto
가 추가되었다. 이제 localhost:3000/api/v1/docs
에 접속해보면, 어떤 요청 바디를 받는지 명세가 추가되어있다.자! 이제 저 API에 요청 바디 명세대로 요청을 보낸다면 어떤 응답이 올지 명세하는 단계이다.
아까 디렉토리 구조에서 src/api/entities/response_entities
에 다음과 같은 파일들을 작성해준다.
먼저,
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()
데코레이터에 description
과 type
을 작성해준다. 타입은 방금전 작성했던 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 }
을 추가해주면 사라진다.