1. Nest.js 프로젝트에 필요한 패키지
npm i @nestjs/mapped-types class-validator
npm i lodash @types/lodash
mapped-types는 DTO 자체의 변환 및 상속을 도와주는 패키지이며 class-validator는 DTO를 구성하는 데이터의 유효성을 검증하는 패키지이다.
- DTO : Data Transfer Object의 준말로 데이터를 전송하기 위해 작성된 객체. Nest.js에서 모든 데이터는 DTO를 통해서 운반된다. DTO 파일에서는 class-validator 패키지를 참조하는 게 일반적이다.
import { IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePostDto {
@IsString()
@IsNotEmpty({ message: '게시물의 제목을 입력해주세요.' })
readonly title: string;
@IsString()
@IsNotEmpty({ message: '게시물의 내용을 입력해주세요.' })
readonly content: string;
@IsNumber()
@IsNotEmpty({ message: '게시물의 비밀번호를 입력해주세요.' })
readonly password: number;
}
create-post.dto.ts 코드인데, @IsString, @IsNumber라는 데코레이터는 각각의 데이터 필드의 타입을 정의하는 것이다. 이 데코레이터가 있으면 각각 문자열, 숫자가 아닌 다른 값으로 전달하면 유효하지 않게되고 에러 응답을 받는다. 그리고 DTO 파일에서는 데부분의 데이터 필드에는 readOnly 키워드를 붙여 생성 후에 변경될 필요가 없는 객체의 속성값이라 명시해 주는 것이다.
// update-post.dto.ts
import { PickType } from '@nestjs/mapped-types';
import { CreatePostDto } from './create-post.dto';
export class UpdatePostDto extends OmitType(CreatePostDto, ['title']) {}
이미 비슷한 종류의 dto가 사전에 정의되어 있다면 @nestjs/mappedtypes 패키지를 활용하여 DTO 코드를 최대한 간소화하는 게 좋다. Omit 타입을 이용하여 기존 DTO의 특정 속성만을 배체하여 새로운 DTO를 생성하게 했다. 제목이 필요하지 않기 때문에 title만 선택적으로 배제한 모습이다.
// post.service.ts
update(id: number, updatePostDto: UpdatePostDto) {
const { content, password } = updatePostDto;
const article = this.articles.find((article) => article.id === id);
if (_.isNil(article)) {
throw new NotFoundException('게시물을 찾을 수 없습니다.');
}
const articlePassword = this.articlePasswords.get(id);
if (!_.isNil(articlePassword) && articlePassword !== password) {
throw new NotFoundException('비밀번호가 일치하지 않습니다.');
}
article.content = content;
}
import문에서 참조하는 lodash를 사용하여 _isNill이라는 함수를 부르고 해당 함수로 NULL 여부를 체크하고 있다. 물론 !을 이용한 조건문으로 함수를 만들 수 있지만 만약에 !article로 만들면 falsy 값일 때 true를 넘겨주기에 이것이 더 낫다.
마지막으로 인증/인가 기능이 구현된 경우 remove 함수와 같은 DELETE API에서는 별도의 DTO 파일이 필요하지 않다.
2. 컨트롤러 코드 분석
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
} from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { RemovePostDTO } from './dto/remove-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { PostService } from './post.service';
@Controller('post')
export class PostController {
constructor(private readonly postService: PostService) {}
@Post()
create(@Body() createPostDto: CreatePostDto) {
return this.postService.create(createPostDto);
}
@Get()
findAll() {
return this.postService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.postService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updatePostDto: UpdatePostDto) {
return this.postService.update(+id, updatePostDto);
}
@Delete(':id')
remove(@Param('id') id: string, @Body() deletePostDto: RemovePostDTO) {
return this.postService.remove(+id, deletePostDto);
}
}
- @Controller('post') : 서버 주소에 /post를 붙여 API를 호출하면 컨트롤러가 요청을 담당하겠다는 뜻. 즉 express의 route와 비슷한 개념
- @Get, @Patch에 :id라는 인자를 넣었는데, 이것은 URL 파라미터라고 하며 @Param이라는 데코레이터로 값을 받아올 수 있다.
- GET을 제외한 나머지 REST API는 DTO를 통해서 데이터를 전달해야 한다. 이때 @Body라는 데코레이터를 사용해 DTO를 사용자로부터 전달 받는다.
'Sparta > TIL' 카테고리의 다른 글
24.03.15 TIL - ELK stack (0) | 2024.03.18 |
---|---|
24.03.12 TIL - TypeOrm (0) | 2024.03.13 |
24.03.08 TIL - Nest.js 파일 구조 (0) | 2024.03.09 |
24.03.07 TIL - Typescript(3) (0) | 2024.03.08 |
24.03.06 TIL - Typescript(2) (0) | 2024.03.07 |