개발이 취미인 사람

[Nest.js] Nest.js API 만들기 (12) - API 문서화(Swagger) 본문

백앤드(Back-End)/Nest.js

[Nest.js] Nest.js API 만들기 (12) - API 문서화(Swagger)

RyanSin 2021. 12. 26. 18:29
반응형

- 개요

안녕하세요. 이번 시간에는 지난 시간까지 만들었던 API 서버에 문서화를 진행해보겠습니다.

 

Nest.js에는 OpenAPI Swagger를 지원합니다. Swagger를 활용해서 API 문서화를 진행하겠습니다.

 

혹시 Swagger에 대해 잘 모르시는 분들은 아래 링크를 통해 학습하고 오시는 걸 추천드리겠습니다.

[Swagger] Swagger UI를 활용한 내부 API Client 구축

 

[Swagger] Swagger UI를 활용한 내부 API Client 구축

- 개요 안녕하세요. 이번 글에서는 Swagger UI를 활용한 API 문서화 및 Client 서버 구축에 대해서 알아보겠습니다. 일단 Swagger라는 기술은 API를 설계하고 문서화를 해주는데 도움을 주는 기술입니다.

any-ting.tistory.com

 

- 설정

Swagger를 사용하기 위해서는 필요한 모듈을 설치해야 합니다.

#npm
npm install --save @nestjs/swagger swagger-ui-express

#yarn
yarn add @nestjs/swagger swagger-ui-express --dev

 

express를 사용해서 API 서버를 구축했기 때문에 express와 관련된 모듈을 설치합니다.

 

설치를 완료했다면 Swagger 설정 파일 코드를 작성하겠습니다.

 

파일 경로는 src/utils/swagger.ts 에 생성하겠습니다.

 

- sagger.ts

import { INestApplication } from '@nestjs/common';
import {
  SwaggerModule,
  DocumentBuilder,
  SwaggerCustomOptions,
} from '@nestjs/swagger';

//웹 페이지를 새로고침을 해도 Token 값 유지
const swaggerCustomOptions: SwaggerCustomOptions = {
  swaggerOptions: {
    persistAuthorization: true,
  },
};

/**
 * @author Ryan
 * @description Swagger 세팅
 */
export function setupSwagger(app: INestApplication): void {
  const options = new DocumentBuilder()
    .setTitle('개발이 취미인 사람')
    .setDescription('개발이 취미인 사람 Swagger API 서버')
    .setVersion('1.0.0')
    //JWT 토큰 설정
    .addBearerAuth(
      {
        type: 'http',
        scheme: 'bearer',
        name: 'JWT',
        in: 'header',
      },
      'access-token',
    )
    .build();

  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup('api-docs', app, document, swaggerCustomOptions);
}

 Swagger를 통해 API 문서를 만들어 보신 분들은 위 소스코드가 이해가 잘 되실 겁니다.

 

추가적으로 JWT 토큰 인증 관련 정보도 설정했습니다.

 

- main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { HttpExceptionFilter } from './utils/http-exception.filter';
import { existsSync, mkdirSync } from 'fs';
import { setupSwagger } from './utils/swagger';

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

  const uploadPath = 'uploads';

  if (!existsSync(uploadPath)) {
    // uploads 폴더가 존재하지 않을시, 생성합니다.
    mkdirSync(uploadPath);
  }

  //예외 필터 연결
  app.useGlobalFilters(new HttpExceptionFilter());

  //Global Middleware 설정 -> Cors 속성 활성화
  app.enableCors({
    origin: '*',
    methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
    optionsSuccessStatus: 200,
  });

  app.useGlobalPipes(
    new ValidationPipe({
      /**
       * whitelist: DTO에 없은 속성은 무조건 거른다.
       * forbidNonWhitelisted: 전달하는 요청 값 중에 정의 되지 않은 값이 있으면 Error를 발생합니다.
       * transform: 네트워크를 통해 들어오는 데이터는 일반 JavaScript 객체입니다.
       *            객체를 자동으로 DTO로 변환을 원하면 transform 값을 true로 설정한다.
       * disableErrorMessages: Error가 발생 했을 때 Error Message를 표시 여부 설정(true: 표시하지 않음, false: 표시함)
       *                       배포 환경에서는 true로 설정하는 걸 추천합니다.
       */
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
      disableErrorMessages: true,
    }),
  );

  //Swagger 환경설정 연결
  setupSwagger(app);

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

위에서 설정한 Swagger 설정 파일을 가져와 설정했습니다. 서버를 실행하면 아래와 같은 화면이 보입니다.

 

http://localhost/api-doc

default 탭을 클릭하면 우리가 생성했던 API를 확인할 수 있습니다.

 

기본 설정을 완료했습니다.

 

자세한 설정 정보를 통해 API 문서를 업그레이드해보겠습니다.

(위와 같이 설정하고 다했다고 하면... 클라이언트 개발자가 죽일 수 있습니다.)

 

- user.controller.ts

Controller 파일에 Swageer 데코레이터를 설정해서 문서화를 진행하겠습니다.

 

첫 번째로 설정하는 데코레이터는 @ApiTages()입니다.

@Controller('user')
@ApiTags('User') // Swagger Tage 설정
export class UserController {

@ApiTags 데코레이터를 사용하면 API에 태그가 설정됩니다. 아래 화면을 보면 default -> User로 변경된 걸 확인하실 수 있습니다.

 

Tag 설정

 

두 번째로 설정하는 데코레이터는 @ApiOperation(), @ApiCreateResponse()입니다.

 

우리가 만들었던 Controller 함수 위에 설정하면 됩니다.

  /**
   * @author Ryan
   * @description @Body 방식 - @Body 어노테이션 여러개를 통해 요청 객체를 접근할 수 있습니다.
   *
   * CreateUserDto를 사용해서 @Body 전달 방식을 변경합니다.
   *
   * @param id 유저 고유 아이디
   * @param name 유저 이름
   */
  @Post('/create_user')
  @UsePipes(ValidationPipe)
  @ApiOperation({
    summary: '유저 생성',
    description: '유저 생성 API',
  })
  @ApiCreatedResponse({
    description: '성공여부',
    schema: {
      example: { success: true },
    },
  })
  onCreateUser(@Res() res: Response, @Body() createUserDto: CreateUserDto) {
    return this.userService.onCreateUser(createUserDto).then((result) => {
      res.status(HttpStatus.OK).json({ success: result });
    });
  }

summary와 description은 해당 API에 대한 설명을 설정할 때 사용됩니다. schema는 클라이언트에게 반환 정보를 나타냅니다.

 

여기서 중요한 부분이 한 가지 있습니다. 바로 DTO입니다.

 

클라이언트에서 서버로 어떤 데이터를 전송해야 하는지 정의가 필요합니다. DTO에 Swagger 데코레이터를 정의해 사용하면 됩니다.

 

#user.dto.ts
export class CreateUserDto {
  @ApiProperty({
    example: 'Ryan',
    description: '유저 아이디',
    required: true,
  })
  @IsString()
  @IsNotEmpty()
  user_id: string; // 유저 아이디

CreateUserDto 클래스에 @ApiProperty() 데코레이터를 통해 전송 데이터에 대한 정보를 정의합니다.

 

  • example : 예시 데이터
  • description : 설명
  • required : 필수 전송 데이터 설정 true / false

위와 같이 설정했다면 아래와 같은 모습이 보일 겁니다.

요청 정보 그리고 반환 정보를 알 수 있습니다.

 

- 로그인

JWT 토큰 인증 방식을 사용하게 되면 로그인 성공 후 토큰 정보를 클라이언트에게 반환합니다. 그리고 API 접근 시 JWT 토큰을 발급받은 유저만 접근이 가능합니다. Swagger를 통해 JWT 토큰 인증방식에 대해 문서화를 진행하겠습니다.

 

Swagger에서 클라이언트가 전송할 데이터를 @ApiProperty() 데코레이터를 통해 정의했습니다.

 

sign_in.dto.ts 파일을 생성해 아래와 같은 코드를 작성하겠습니다.

//sing_in.dto.ts

import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString, Matches } from 'class-validator';

export class SignInDto {
  @ApiProperty({
    example: 'Ryan',
    description: '유저 아이디',
    required: true,
  })
  @IsNotEmpty()
  @IsString()
  user_id: string;

  @ApiProperty({
    example: '1234qweR!!',
    description: '유저 비밀번호',
    required: true,
  })
  @IsNotEmpty()
  @IsString()
  // 최소 8자 및 최대 20자 하나의 소문자, 하나의 숫자 및 하나의 특수 문자
  @Matches(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d~!@#$%^&*()+|=]{8,16}$/, {
    message: '비밀번호 양식에 맞게 작성하세요.',
  })
  password: string;
}

@ApiProperty() 데코레이터를 통해 유저 아이디와, 유저 비밀번호를 생성합니다.

 

생성을 완료했다면 로그인 Controller에 해당 DTO를 설정합니다.

 

// user.controller.ts

  /**
   * @author Ryan
   * @description 로그인
   *
   * @param req Request 데코레이터
   * @returns User
   */
  @Post('/auth/login')
  @UseGuards(LocalAuthGuard)
  @ApiOperation({
    summary: '로그인 API',
    description: '아이디와 비밀번호를 통해 로그인을 진행',
  })
  @ApiCreatedResponse({
    description: '로그인 정보',
    schema: {
      example: {
        id: 'cea1d926-6f1b-4a37-a46c-8ddf0b17a0bc',
        user_id: 'Ryan',
        salt: '임시',
        name: 'Ryan',
        age: 25,
        createdAt: '2021-12-25T23:30:51.371Z',
        updatedAt: '2021-12-25T23:30:51.371Z',
        deletedAt: null,
        token:
          'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImNlYTFkOTI2LTZmMWItNGEzNy1hNDZjLThkZGYwYjE3YTBiYyIsInVzZXJfaWQiOiJSeWFuIiwic2FsdCI6IuyehOyLnCIsIm5hbWUiOiJSeWFuIiwiYWdlIjoyNSwiY3JlYXRlZEF0IjoiMjAyMS0xMi0yNVQyMzozMDo1MS4zNzFaIiwidXBkYXRlZEF0IjoiMjAyMS0xMi0yNVQyMzozMDo1MS4zNzFaIiwiZGVsZXRlZEF0IjpudWxsLCJpYXQiOjE2NDA1MDc0NzMsImV4cCI6MTY0MDUwNzUzM30.gm-Yf_C8szEOvcy-bK-r-CP4Nz6aCr1AgqvH8KonxvU',
      },
    },
  })
  // Swageer API를 사용하기 위해 DTO적용
  async login(@Request() req, @Body() sign_in_dto: SignInDto) {
    console.log('Login Route');

    return req.user;
  }

@Body 데코레이터에 우리가 생성한 DTO를 연결하고 그리고 API 정보를 설정하면 끝입니다.

 

요청 정보와 반환 정보가 기재된 걸 확인할 수 있습니다.

 

마지막으로 토큰 인증을 진행할 수 있게 @ApiBearerAuth() 데코레이터를 사용하겠습니다.

 /**
   * @author Ryan
   * @description 전체 유저 조회
   */
  @Get('/user_all')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth('access-token') //JWT 토큰 키 설정
  @ApiOperation({
    summary: '전체 유저 조회',
    description: '전체 유저 조회 API',
  })
  @ApiCreatedResponse({
    description: '성공여부',
    schema: {
      example: {
        success: true,
        data: [
          {
            id: 'cea1d926-6f1b-4a37-a46c-8ddf0b17a0bc',
            user_id: 'Ryan',
            password: '1234qweR!!',
            salt: '임시',
            name: 'Ryan',
            age: 25,
            createdAt: '2021-12-25T23:30:51.371Z',
            updatedAt: '2021-12-25T23:30:51.371Z',
            deletedAt: null,
          },
        ],
      },
    },
  })
  getUserAll(@Res() res: Response) {
    return this.userService.getUserAll().then((result) => {
      res.status(HttpStatus.OK).json({ success: true, data: result });
    });
  }

@ApiBearerAuth('access-token') 데코레이터를 설정하면 해당 API는 JWT 토큰 정보를 같이 전송해야 합니다.

(access-token 값은 addBearerAuth() 두 번째 설정 값과 동일해야 합니다.)

 

BearerAuth 설정

전체 유저 조회 API 오른쪽에 자물쇠 아이콘이 생성된 걸 알 수 있습니다. 인증이 필요한 API라는 걸 알 수 있습니다.

 

 

 

발급받은 토큰 정보를 설정하면 해당 API에 토큰 정보가 삽입됩니다.

 

또는 웹 페이지 상단에 Authorize 버튼을 클릭해 해당 토큰 정보를 삽입할 수 있습니다.

 

로그인 후 유저 정보를 조회를 해보겠습니다.

 

1. 로그인



아이디와 비밀번호를 설정하고 Execute 버튼을 클릭해 로그인을 진행합니다.

 

로그인 성공 시 token 정보를 받을 수 있습니다. 이제 토큰 정보를 복사해 등록해줍니다.

 

토큰 정보 입력

 

토큰 등록 완료

 

2. 전체 유저 조회

 

만약 토큰 정보를 등록하지 않으면 어떻게 될까요?... 당연히 에러가 발생하겠죠...  당연하지만 실습을 해보겠습니다.

 

토큰 해제

등록했던 토큰을 해제합니다. Logout 버튼을 눌러 토큰 등록을 해제합니다. 그리고 다시 한번 요청하겠습니다.

 

허가 받지 않은 요청

Unauthorized : 승인되지 않음

 

토큰 정보가 없어서 승인되지 않았다는 걸 알 수 있습니다.

 

이번 시간에는 Nest.js에서 Swageer(Open API)를 사용해서 API 문서화를 진행했습니다. 

 

전체 소스 코드는 아래 공유하겠습니다. 꼭 실습을 통해 학습하시는 걸 추천드리겠습니다.

 

 

소스 저장소 

GitHub : https://github.com/Ryan-Sin/Node_Nest/tree/v11

 

GitHub - Ryan-Sin/Node_Nest

Contribute to Ryan-Sin/Node_Nest development by creating an account on GitHub.

github.com