본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.
nest new nestjs_board
npm i @nestjs/mapped-types class-validator
npm i lodash @types/lodash
"esModuleInterop": true
항목 추가{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
...
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"esModuleInterop": true
}
}
DTO (Data Trasfer Object)는 간단하게 말하면 데이터를 전송하기 위해 작성된 객체를 의미
전송되는 데이터에 대한 유효성 검사를 진행할 수 있음
계층(컨트롤러 - 서비스)간 데이터를 전송하는 데 사용됨 (계층 간 결합도를 낮출 수 있음)
Nest.js에서는 모든 데이터가 DTO를 통해 운반됨
nest g resource post
npm run build
npm run start:dev
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;
}
게시물을 수정할 때는 내용(content)만 수정한다고 가정
그럼 CreatePostDto
를 상속받아서 제목(title)만 제외한 나머지만 입력받으면 됨
제목(title)를 제외하기 위해서 OmitType
타입을 이용해서 특정 데이터를 제외하고 상속
import { OmitType } from '@nestjs/mapped-types';
import { CreatePostDto } from './create-post.dto';
export class UpdatePostDto extends OmitType(CreatePostDto, ['title']) {}
게시물 삭제는 생성과 수정과는 다르게 비밀번호(password)만 있으면 그 비밀번호를 통해서 인증된 사용자인지 검증함
그래서 수정과는 반대로 비밀번호(password)만 있으면 되기 때문에 PickType
타입을 이용해서 특정 데이터만 상속
import { PickType } from '@nestjs/mapped-types';
import { CreatePostDto } from './create-post.dto';
export class RemovePostDto extends PickType(CreatePostDto, ['password']) {}
DTO 객체를 어떻게 사용하는지 확인하는 것이 중요함
아직까지는 로컬 저장소를 사용하기 때문에 서버 재실행 시 저장했던 데이터는 날라감
import _ from 'lodash';
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { RemovePostDto } from './dto/remove-post.dto';
@Injectable()
export class PostService {
private articles: { id: number; title: string; content: string }[] = [];
private articlePasswords = new Map<number, number>();
// 게시물 생성
create(createPostDto: CreatePostDto) {
const { title, content, password } = createPostDto;
const length = this.articles.length;
const id = length === 0 ? 1 : this.articles[length - 1].id + 1;
this.articles.push({ id, title, content });
this.articlePasswords.set(id, password);
return { id };
}
// 게시물 목록 조회
findAll() {
return this.articles.map(({ id, title }) => ({ id, title }));
}
// 게시물 상세 조회
findOne(id: number) {
return this.articles.find((article) => id === article.id);
}
// 게시물 수정
update(id: number, updatePostDto: UpdatePostDto) {
const { content, password } = updatePostDto;
const existArticle = this.articles.find((article) => article.id === id);
// isNil() 메서드는 값이 null이거나 undefined면 true를 반환
if (_.isNil(existArticle)) {
throw new NotFoundException('게시물이 존재하지 않습니다.');
}
const articlePassword = this.articlePasswords.get(id);
if (!_.isNil(articlePassword) && articlePassword !== password) {
throw new NotFoundException('비밀번호가 일치하지 않습니다..');
}
existArticle.content = content;
}
// 게시물 삭제
remove(id: number, removePostDto: RemovePostDto) {
const { password } = removePostDto;
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('비밀번호가 일치하지 않습니다.');
}
const articleIndex = this.articles.indexOf(article);
this.articles.splice(articleIndex, 1);
this.articlePasswords.delete(id);
}
}
기존 Express 프로젝트에서의 라우터와 컨트롤러의 역할을 같이 하고 있음
DTO 객체를 통해서 사용자에게 받은 입력 값을 postService에게 넘김
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') // 주소상에서 /post를 사용하면 이 컨트롤러를 사용
export class PostController {
constructor(private readonly postService: PostService) {}
@Post()
// @Body 데코레이터는 사용자에게 CreatePostDto를 통해서 입력을 받기 위해 사용함
create(@Body() createPostDto: CreatePostDto) {
return this.postService.create(createPostDto);
}
@Get()
findAll() {
return this.postService.findAll();
}
// 주소상에서 /post/1 처럼 path parameter를 사용한다는 의미
// GET http://localhost:3000/post/1
@Get(':id')
// 실제로 path parameter를 사용하기 위해서는 @Param 데코레이터가 필요함
findOne(@Param('id') id: string) {
return this.postService.findOne(+id);
}
// PATCH http://localhost:3000/post/1
@Patch(':id')
update(@Param('id') id: string, @Body() updatePostDto: UpdatePostDto) {
return this.postService.update(+id, updatePostDto);
}
// DELETE http://localhost:3000/post/1
@Delete(':id')
remove(@Param('id') id: string, @Body() deletePostDto: RemovePostDto) {
return this.postService.remove(+id, deletePostDto);
}
}
Nest.js 프로젝트 생성, 깃허브 연결 등과 같은 기본적인 프로젝트 세팅을 진행할 예정
그리고 프로젝트에 대한 ERD와 API 명세서도 구상해서 작성할 예정
기본적인 설계 뿐만 아니라 세팅한 것들이 잘 돌아가는지 웹 서버를 구동할 예정
가능하다면 추가적인 몇가지 API도 구현할 예정
일단 주말 안으로 진행할 예정
3주차 내용은 간단한 게시판 프로젝트 실습을 진행하는 것임
아직 구조적으로 모든 걸 파악하기는 어렵지만 코드에 주석을 달면서 조금씩 이해하고 있음
특히 DTO라는 구조는 처음 알았기 때문에 신선하고 생각보다 편리했음