본문 바로가기
Back-end/기초부터 따라하는 nest.js

시즌 2 #13. 기초부터 따라하는 Nest.js 2 : Class Validator와 Swagger

by hsloth 2024. 8. 11.

지난 포스팅에서는 Entity와 Dto에 대해 간단하게 배워보았습니다.

 

 

이번 포스팅에서는 지난 포스팅에서 언급했던 DTO를 사용하는 이유 중 하나인 Class Validator에 대해서 다뤄보도록 하겠습니다.

 


과제 정답


지난번 과제입니다.

  1. UserEntity를 만든 것처럼, PostEntity, CommentEntity 만들어오기
  2. 게시글 생성, 조회(게시글 하나 상세조회) API를 DTO를 활용해서 만들어오기

1번에 대한 정답을 보시죠!

// post.entity.ts
export class PostEntity {
  id: number;

  title: string;

  content: string;

  createdAt: Date;

  updatedAt: Date | null;

  deletedAt: Date | null;

  userId: number;
}


// comment.entity.ts
export class CommentEntity {
  id: number;

  email: string;

  password: string;

  nickname: string;

  createdAt: Date;

  updatedAt: Date | null;

  deletedAt: Date | null;
}

 

그러면 이제 Post 생성 조회 API를 함께 만들어보죠!

먼저, Controller의 Body로 들어올 데이터를 위한 DTO를 만들어봅시다.

dtos폴더에 post 폴더를 생성하고 post-body.dto.ts 파일을 만들어줍시다.

 

그리고 post-body.dto.ts에 아래와 같이 코드를 작성해줍시다.

// post-body.dto.ts
// Post Controller에서 Body에 사용되는 DTO를 모아놓은 파일입니다.
import { PickType } from '@nestjs/swagger';
import { PostEntity } from '../entities/post.entity';

export class CreatePostBodyDto extends PickType(PostEntity, [
  'title',
  'content',
  'userId',
] as const) {}

이 DTO는 Controller의 @Body 데코레이터에서 사용할 예정입니다.

Post를 만드는데 title, content, userId가 필수이니 해당 속성들을 PickType을 통해 pick해주었습니다.

사실, userId는 로그인 정보를 통해서 (jwt나 세션을 통해) 받아와야하는데, 현재는 구현이 되어있지 않으므로 그냥 body로 받아줍시다.

원래라면... userId를 body로 받는 짓은 하면 안됩니다.

본인의 글은 본인만 쓸 수 있으니, 로그인 정보에서 userId를 빼와야합니다. (나중에 알려드리겠습니다...!)

 

그리고 다음과 같이 함수들을 작성해주세요.

// post.repository.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/databases/prisma/prisma.service';

@Injectable()
export class PostRepository {
  constructor(private readonly prisma: PrismaService) {}

  async createPost(
    title: string,
    content: string,
    userId: number,
  ): Promise<true> {
    await this.prisma.post.create({
      data: {
        title,
        content,
        userId,
      },
    });

    return true;
  }
}

// post.service.ts
import { Injectable } from '@nestjs/common';
import { PostRepository } from './post.repository';

@Injectable()
export class PostService {
  constructor(private readonly postRepository: PostRepository) {}

  async createPost(body: CreatePostBodyDto): Promise<true> {
    const isCreated = await this.postRepository.createPost(
      body.title,
      body.content,
      body.userId,
    );

    return isCreated;
  }
}

// post.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { PostService } from './post.service';
import { CommentService } from '../comment/comment.service';
import { CreatePostBodyDto } from 'src/dtos/post/post-body.dto';

@Controller('post')
export class PostController {
  constructor(
    private readonly postService: PostService,
    private readonly commentService: CommentService,
  ) {}

  @Post()
  async createPost(@Body() body: CreatePostBodyDto): Promise<true> {
    const isCreated = await this.postService.createPost(body);

    return isCreated;
  }
}

 

자세한 설명은 생략하겠습니다...!! 궁금한게 있으시면 댓글 달아주세요!

 

 

이제 Post 생성 API를 만들었으니, Post를 조회하는 API도 만들어보죠!

먼저, Repository에서 사용될 DTO를 만들어봅시다.

dto의 post폴더 안에 find-post.dto.ts 파일을 만들어줍시다.

 

그리고 코드를 작성해주도록 하죠!

// find-post.dto.ts
// Post Repository에서 Create Post와 관련된 DTO들을 모아놓은 파일입니다.
import { OmitType } from '@nestjs/swagger';
import { PostEntity } from '../entities/post.entity';

export class FindOnePostOutputDto extends OmitType(PostEntity, [
  'deletedAt',
] as const) {}

post 정보에는 삭제날짜는 필요없으니, OmitType을 사용하여 deletedAt을 제외한 모든 데이터를 가져오도록 합니다.

 

 

그리고 다음과 같이 코드를 작성해줍시다.

// post.repository.ts
  ...
  async findOnePost(postId: number): Promise<FindOnePostOutputDto | null> {
    // 원래라면 comment도 가져와주어야합니다.
    const post = await this.prisma.post.findFirst({
      select: {
        id: true,
        title: true,
        content: true,
        createdAt: true,
        updatedAt: true,
        userId: true,
      },
      where: {
        id: postId,
      },
    });

    return post;
  }
  
// post.service.ts
  ...
  async findPostDetail(postId: number): Promise<FindOnePostOutputDto> {
    const post = await this.postRepository.findOnePost(postId);

    if (!post) {
      throw '찾으시는 post가 없습니다.';
    }

    return post;
  }

// post.controller.ts
  ...
  @Get('/:postId')
  async getOnePost(
    @Param('postId', ParseIntPipe) postId: number,
  ): Promise<FindOnePostOutputDto> {
    const post = await this.postService.findPostDetail(postId);

    return post;
  }
  
// post.module.ts
import { Module } from '@nestjs/common';
import { PostService } from './post.service';
import { PostController } from './post.controller';
import { CommentModule } from '../comment/comment.module';
import { PostRepository } from './post.repository';

@Module({
  imports: [CommentModule],
  controllers: [PostController],
  providers: [PostService, PostRepository],
})
export class PostModule {}

 

그러면, 이제 게시글 생성 API와 게시글 조회 API가 잘 동작하는지 확인해볼까요?

 

서버를 시작해봅시다.

npm run start

 

 

게시글 조회를 하기 전에 먼저, 게시글을 생성 해야겠죠?

그런데, 게시글을 생성하려면 User가 필요합니다. 그쵸?! 그러니 mysql terminal이나 MySQL workbench에 들어가서 내가 생성한 유저 목록을 봐봅시다.

제 경우에는 id가 1인 User가 있네요! 이 유저가 게시글을 작성한다고 가정하고 진행해보겠습니다!

 

mysql cli(Mysql terminal)로 접속하는 분들은 아래 명령어를 입력해주세요.

# mysql terminal 접속
# 제 경우에는 mysql -u root -p
mysql -u <유저이름> -p

# 비밀번호 입력
# 저의 경우에는 비밀번호가 없어 그냥 엔터를 눌러주었습니다.

# study DB 사용
use study;

# 유저 목록 확인
select * from User;

위 명령어를 사용해서 나온 유저들 중 한 유저를 골라서 id를 사용하면 됩니다.

 

게시글 생성 API 동작 확인

그리고 이제 서버가 열려있다면, Postman으로 가서 다음과 같이 POST 요청을 날려줍니다.

 

true가 반환되었다면, 생성이 잘 된겁니다!

 

게시글 조회 API 동작 확인

방금 생성된 게시글의 id를 토대로 게시글 내용을 조회할 수 있습니다.

MySQL Workbench를 사용해서 알아내거나, MySQL Terminal에서 확인하시기 바랍니다.

이번에는 직접 생각해보세요 ㅎㅎ 어떻게 하면 방금 생성한 Post의 id값을 알아낼 수 있을지...!!

 

라고 하기에는 모르시는 분들도 있을 것 같아서...

MySQL Workbench

 

mysql cli(terminal)

mysql -u <user이름> -p

비밀번호 입력

use study;

select * from Post;

 

저의 경우 Post의 id가 1이니, url의 param부분에 1을 입력하여 확인해보겠습니다.

 

그러면 짜잔! 이렇게 위와같이 게시글 내용을 볼 수 있습니다!

백엔드는 이런식으로 데이터를 프론트엔드(정확히는 브라우저) 프론트엔드는 이 JSON 형식의 데이터를 이용해서 웹 페이지를 이쁘게! 꾸며주는거죠!

 


Class Validator


그런데 여기서 의문이 들 수 있습니다.

HTTP 요청으로부터 input을 받을 때, 다른 정적 타입을 가진 언어들(C, C++, Java 등)의 경우 데이터의 타입이 맞지 않으면 에러를 발생시키겠지만, 타입스크립트의 경우 그렇지 않습니다.

타입스크립트는 결국 자바스크립트로 컴파일(트랜스파일이라고도 합니다)되므로 런타임에 돌아가는 코드는 자바스크립트이기 때문입니다.

자바스크립트는 타입이 없습니다. (정확히 말하면 모든 것이 객체입니다) 그래서 HTTP 요청으로부터 예상과는 다른 타입의 Input(body)가 들어와도 에러를 발생시키지 않습니다. 

 

 

따라서 위와같은 회원가입 로직이 있다고 했을 때, email, password, nickname이 string 타입이 아니어도 정상적으로 동작한다는 거죠.

 

이런식의 동작은 DB에 예상치 못한 데이터를 삽입하거나, 계산의 결과가 예상과 다른 등의 개발자가 예상하지 못한 결과를 불러일으킬 수 있습니다.

예를들면 자바스크립트에서 "1" + 1은 "11" 인데, 1+1 = 2가 되는 것처럼, 개발자는 11이 되기를 바라고 로직을 짰지만, 실제 들어온 값이 문자열 "1"이 아닌 숫자 1이 들어와서 2라는 계산 결과를 도출하게 되는 것입니다. 모든 로직이 이런식으로 동작한다면 문제가 많이 발생하겠죠?

 

그러면 직접 한 번 예시를 봐봅시다. (코드는 따라치지 말아주세요)

만약에 내가 내 통장에 돈을 송금한다고 생각해보자구요. 현재 내 통장에 있는 돈은 10,000원입니다. 그 상황에서 내가 내 통장에 10,000원을 더 송금을 하려고 하는 상황입니다.

 

송금 API에 짜여져 있는 코드는 다음과 같습니다.

  @Post('/money')
  async sendMoneyToMyAccount(@Body() body: any): Promise<number> {
    const originalMoney = 10000;

    const money: number = body.money;

    return money + originalMoney;
  }

 

그리고 API 요청을 보내봅시다.

 

다음과 같이 숫자 10,000을 적어서 요청을 보내면 무리 없이 내 통장의 잔고가 10,000 + 10,000 = 20,000원이 되었다고 응답이 옵니다.

 

하지만 여기서 문자열 10,000을 보낸다면... 어떻게 될까요?

 

짜잔! 와! 돈이 10000배로 복사가 되었어요! 1,000,010,000원(약 10억원)이 되었네요!

 

이런 상황이 생기면 안되겠죠? 그래서 money를 number 형식만 받을 수 있도록 유효성 검사를 해주어야합니다 ㅎㅎ.

 

그리고 Nest.js에서는 이렇게 요청의 속성들의 타입과 값을 검사해주는 라이브러리로 Class validator를 사용합니다.

 

 

회원가입 로직에 Class Validator를 달아보자!

우리는 그러면 간단하게 연습겸 회원가입 로직에 Class Validator를 달아봅시다.

 

먼저, class-validator, class-transformer, @nestjs/swagger, swagger-ui-express를 설치해줍시다.

npm i class-validator class-transformer @nestjs/swagger swagger-ui-express

 

 

그리고 UserEntity에 Validator를 적용해봅시다!

// user.entity.ts
import {
  IsDateString,
  IsEmail,
  IsInt,
  IsNotEmpty,
  IsString,
} from 'class-validator';

export class UserEntity {
  @IsInt()
  id: number;

  @IsEmail()
  email: string;

  @IsString()
  @IsNotEmpty()
  password: string;

  @IsString()
  @IsNotEmpty()
  nickname: string;

  @IsDateString()
  createdAt: Date;

  @IsDateString()
  updatedAt: Date | null;

  @IsDateString()
  deletedAt: Date | null;
}

 

@IsInt 데코레이터를 통해 id 속성이 Integer인지 검증할 수 있습니다. UserEntity에서 id값에 1.1 같은 소수나 "1"같은 string 문자열이 들어오면 에러를 뱉게 됩니다.

 

@IsEmail 데코레이터를 통해 email 속성이 이메일 형식인지 검증합니다. 보통 이메일 형식은 <아이디>@<이메일주소>로 이루어져 있습니다. 이런 값 외에는 모두 에러를 뱉게 됩니다.

 

@IsString 데코레이터를 통해 해당 속성의 값이 string인지 검증합니다. 여기서, 빈 문자열이 들어오는 것을 방지하기 위해 @IsNotEmpty 데코레이터를 적용하여 빈 문자열이 들어오면 에러를 뱉도록 합니다.

여기서, 데코레이터는 위에서 아래로 적용된다고 생각하면 됩니다. @IsString() 아래에 @IsNotEmpty()가 적혀있을 경우, IsString을 거치고, IsNotEmpty를 거칩니다. 따라서 해당 속성의 값이 문자열인지 확인 후, 빈 문자열인지 확인하게 됩니다.

 

@IsDateString 데코레이터를 통해서 해당 속성의 값이 Date형식의 문자열인지 검증합니다. @IsDate가 아닌 @IsDateString인 이유는, HTTP 통신으로 Date객체를 주고받게 되면, 자동으로 문자열로 변환되기 때문입니다. 예를 들어서, new Date("2020-01-01") 데이터를 HTTP 통신으로 주고받게 되면, HTTP 메시지에는 "2020-01-01" 이런식으로 문자열 형태로 담깁니다. 그래서 Date가 아닌 Date형식의 문자열인 DateString으로 검증하는 것이죠!

 

 

자, 그러면 이제 UserEntity에 Class Validator를 달아주었으니, UserEntity를 이용해서 DTO를 만들어봅시다!

 

회원가입할 때 필요한 속성들을 body로 받을 것이기 때문에, body에 적용해줄 DTO를 만들어봅시다.

먼저, dtos폴더의 user폴더 안에 user-body.dto.ts 파일을 만들어주고, 아래와 같이 코드를 작성해줍시다.

// src/dtos/user/user-body.dto.ts
import { PickType } from '@nestjs/swagger';
import { UserEntity } from '../entities/user.entity';

export class SignUpBodyDto extends PickType(UserEntity, [
  'email',
  'password',
  'nickname',
] as const) {}

 

PickType을 상속받아서 SignUpBodyDto를 만들어줍시다.

PickType의 형식은 다음과 같습니다.

PickType(class, [class에서 사용할 속성들] as const)

 

우리의 경우에는 class에 UserEntity를 넣어주었고, UserEntity에서 회원가입에 필요한 email, password, nickname 속성을 가져왔습니다. as const는... 그냥 적어줍시다.

 

이런식으로 SignUpBodyDto를 만들어줄 수 있습니다.

 

이렇게 PickType을 이용해서 DTO를 작성하면, UserEntity에 적용한 class-validator가 그대로 적용된다는 점입니다.

따라서 변수에 SignUpBodyDto를 타입으로 지정하면, email은 이메일 형식이 들어오도록 유효성 검사를 진행하고, passwordnickname은 빈 문자열이 아닌 문자열이 들어왔는지 유효성 검사를 진행합니다. (정확히 말하면, 변수에 타입을 지정하면 유효성 검사를 진행하는 것이 아닌, @Body, @Param, @Query등 몇몇 변수에 대해서만 검사를 진행합니다. 일단, 변수에 class-validator가 달린 타입을 지정하면 유효성 검사를 한다! 라고 기억해둡시다)

 

여기서 주의할 점PickType@nestjs/mapped-types에서 import하는게 아닌, @nestjs/swagger에서 import를 해야한다는 점입니다! @nestjs/mapped-types에서 불러와도 상관없지만(아마도...?), @nestjs/swagger에서 불러와야 나중에 swagger를 적용해줄 때도 적용할 수 있습니다.

 

그러면 이제 회원가입 로직으로 가서 BodyValidation(유효성 검사)할 수 있도록 만들어줍시다!

// user.controller.ts
import { SignUpBodyDto } from 'src/dtos/user/user-body.dto';
  ...
  @Post('/sign-up')
  async signUp(@Body() body: SignUpBodyDto): Promise<true> {
    const isCreated = await this.userService.signUp(
      body.email,
      body.password,
      body.nickname,
    );

    return isCreated;
  }
  ...
}

 

UserControllersignUp함수의 body 파라미터의 타입을 SignUpBodyDto로만 바꿔줍시다.

 

그런데... 여기서 이대로 서버를 켜고 API 요청을 보내면, body의 데이터가 validation되지 않습니다. (=유효성 검증이 이뤄지지 않습니다)

그래서 ValidationPipe를 사용하겠다고 등록을 해주어야합니다. 이러한 등록 방법에는 두 가지가 있지만, 일단 여기서는 직접 인스턴스를 주입하는 방법을 사용하겠습니다.

 

main.ts에서 ValidationPipe를 붙여줍시다.

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './databases/prisma/prisma.service';
import { ValidationPipe } from '@nestjs/common';

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

  const prismaService = app.get(PrismaService);
  await prismaService.enableShutdownHooks();

  app.useGlobalPipes(new ValidationPipe()); // 이 부분만 추가되었습니다.

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

 

 

이제 서버를 실행하고, postman을 켜봅시다.

npm run start

 

Postman에서 http://127.0.0.1:3000/user/sign-up 으로 Post 요청을 날려봅시다.

 

이런식으로 body에 email과 password를 형식에 맞지 않게 입력하게 되면 response로 message가 날라옵니다.

"email must be an email" : body의 email 필드가 email 형식이 아닙니다.

"password should not be empty" : body의 password 필드가 비어있으면(빈 문자열이면) 안됩니다.

"nickname should not be empty" : body의 nickname 필드가 비어있으면(빈 문자열이면) 안됩니다.

"nickname must be a string" : body의 nickname 필드가 문자열이 아닙니다.

 

그리고 이번에는 형식에 맞게 Request를 날리게 되면...!

 

이런식으로 true를 받을 수 있습니다.

 


Swagger


자 그럼 이제 Swagger에 대해서 알아봅시다.

먼저 Swagger 관련 패키지를 설치해야하지만, Class Validator를 설치할 때 함께 설치했으니 넘어가겠습니다.

회원 가입 로직에 Swagger를 달아보겠습니다.

 

먼저, Swagger를 사용하겠다는 설정을 main.ts에 해주어야합니다. main.ts의 bootstrap 함수에 다음과 같은 코드를 추가해줍시다.

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './databases/prisma/prisma.service';
import { ValidationPipe } from '@nestjs/common';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

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

  const prismaService = app.get(PrismaService);
  await prismaService.enableShutdownHooks();

  app.useGlobalPipes(new ValidationPipe());

  // Swagger 관련 세팅
  const options = new DocumentBuilder()
    .setTitle('Swagger 문서 제목입니다.')
    .setDescription('Swagger 문서 설명입니다.')
    .setVersion('Swagger 문서 버전입니다.')
    .build();

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

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

 

그 후, 서버를 키고, http://127.0.0.1:3000/api-docs 로 접속을 해보면 다음과 같은 화면이 뜨게 됩니다.

 

/user/sign-up API의 설명을 봤을 때, 아무것도 안뜨고 있죠? 우리는 이제 이러한 것들을 데코레이터들을 통해 채워줄 겁니다!

 

먼저, UserEntity에 스웨거 관련 데코레이터를 달아줍시다!

// dtos/entities/user.entity.ts
import { ApiProperty } from '@nestjs/swagger';
import {
  IsDateString,
  IsEmail,
  IsInt,
  IsNotEmpty,
  IsString,
} from 'class-validator';

export class UserEntity {
  @ApiProperty({
    type: Number,
    required: true,
  })
  @IsInt()
  id: number;

  @ApiProperty({
    type: String,
    description: '이메일',
    example: 'example@gmail.com',
    required: true,
  })
  @IsEmail()
  email: string;

  @ApiProperty({
    type: String,
    description: '비밀번호',
    required: true,
  })
  @IsString()
  @IsNotEmpty()
  password: string;

  @ApiProperty({
    type: String,
    description: '닉네임',
    example: 'nickname',
    required: true,
  })
  @IsString()
  @IsNotEmpty()
  nickname: string;

  @ApiProperty({
    type: Date,
  })
  @IsDateString()
  createdAt: Date;

  @ApiProperty({
    type: Date,
  })
  @IsDateString()
  updatedAt: Date | null;

  @ApiProperty({
    type: Date,
  })
  @IsDateString()
  deletedAt: Date | null;
}

 

이렇게 UserEntity에 @ApiProperty라는 데코레이터를 적용해주면, PickType과 같은 속성들을 이용해서 DTO를 만들 경우, 해당 DTO의 속성(프로퍼티)들에 대해서 타입과 설명, 예제가 들어가게 됩니다.

 

그러면 UserEntity를 이용해서 SignUpBodyDto를 만들었으니, sign-up 로직의 body부분이 Swagger에 적용이 되어야하겠죠?

 

다시 서버를 키고, http://127.0.0.1:3000/api-docs 로 접속을 해보면, 이제 sign-up api 부분의 Body에 Example Value가 들어갔습니다...!!!

 

Example Value 옆에 Schema를 클릭하면 다음과 같이 Dto에 대한 정보도 함께 제공합니다.

 

그러면 sign-up의 Request body 부분은 작성을 해주었으니, 이제 Swagger에서 sign-upResponses 부분을 작성을 해주어야겠죠!

 

Swagger에서 API의 Response 부분을 작성해주려면, Controller에서 처리를 해주어야 합니다.

// user.controller.ts
...
  @ApiOperation({
    summary: '회원가입 API',
    description: '회원가입 API입니다.',
  })
  @ApiResponse({
    type: Boolean,
  })
  @Post('/sign-up')
  async signUp(@Body() body: SignUpBodyDto): Promise<true> {
    const isCreated = await this.userService.signUp(
      body.email,
      body.password,
      body.nickname,
    );

    return isCreated;
  }
...

 

@ApiOperation 데코레이터를 이용하여 해당 Api어떠한 동작을 하는지에 대해서 설명을 적어줍니다.

그리고 @ApiResponse 데코레이터로 Swagger에 Response 타입에 대한 정보를 적게끔 할 수 있습니다.

 

서버를 시작해서 http://127.0.0.1:3000/api-docs 경로로 들어가서 스웨거를 보면,

API에 대한 설명이 들어가 있는 것을 볼 수 있고,

Responses를 보면 true라는 값이 들어가는 걸 볼 수 있습니다.

 

물론, Class-Validator를 사용한 DTO를 적용할 수도 있습니다.

우리가 Class-Validator를 사용한 이유도 여기에 있죠!

UserController에서 이전에 만들어 둔 getUserInfo 함수를 예로 들겠습니다.

// user.controller.ts
...
  @ApiOperation({
    summary: '유저 정보 확인 API',
    description: '유저 정보를 확인할 수 있습니다.',
  })
  @ApiOkResponse({
    type: FindOneUserOutputDto,
  })
  @Get('/:userId/info')
  async getUserInfo(
    @Param('userId', ParseIntPipe) userId: number,
  ): Promise<FindOneUserOutputDto> {
    const user = await this.userService.getUserInfo(userId);

    return user;
  }
...

 

전의 예제와는 다르게 여기서는 @ApiOkResponse 데코레이터를 사용했습니다. @ApiResponse 데코레이터와 같은 기능인데, 자동으로 status 코드에 200을 넣어준다는 점만 기억합시다.

이런식으로 Responses에 예제가 들어갑니다. 또한, Body와 마찬가지로 Example Value 옆에 Schema를 누르면 스키마에 대한 정보도 볼 수 있습니다.

 

 


과제


과제가 있습니다! 조....끔? 어려울 수도 있는데요....!

우리가 위에서 UserEntity에 Class Validator와 Swagger 기능을 데코레이터를 이용해 추가해주었습니다.

그러면 이번에는 PostEntity와 CommentEntity에 Class ValidatorSwagger를 추가해봅시다.

 

게시글 생성, 댓글 생성 API를 직접 만들어서 Dto를 달고 해당 API에 Class Validator와 Swagger를 적용해주세요!

Class Validator의 경우, body와 response에 적용해주면 되고

Swagger의 경우, Body와 Response에 대해서 그리고 API에 대한 설명을 추가해주시면 됩니다.

 

UserEntity에서 했던대로! 똑같이 하시면 됩니다!

 

 

 

이것으로 Class Validator와 Swagger 부분을 마치겠습니다.

오늘도 긴 글 읽어주셔서 감사합니다.