환경설정 - NestJS는 npm으로 써야함
Nest.js 게시판 프로젝트 생성
nest new nestjs_board #npm 선택택
Nest.js 게시판 프로젝트 디렉토리로 이동
cd nestjs_board
@nestjs/mapped-types, class-validator 패키지를 설치
npm i @nestjs/mapped-types class-validator
lodash 패키지를 설치
npm i lodash @types/lodash
Visual Studio Code로 게시판 프로젝트 열기
code .
tsconfig.json에 추가
"esModuleInterop": true // 추가. ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져오기
prettier 설치
npm install --save-dev prettier
.prettierrc파일 추가
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false,
"printWidth": 80,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always"
}
package.json에 추가(자동으로 추가되지만 확인해보자)
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
nestjs_board프로젝트의 ./src 폴더로 이동
cd src
게시판 뼈대 한 번에 완성하기 nest g resource 폴더이름
nest g resource post # REST API, Y
결과
PS V:\Sparta\memoNote\github\D1\dailyCoding\NESTJS\3W> cd src
PS V:\Sparta\memoNote\github\D1\dailyCoding\NESTJS\3W\src> nest g resource post
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? Yes
CREATE post/post.controller.ts (917 bytes)
CREATE post/post.controller.spec.ts (576 bytes)
CREATE post/post.module.ts (250 bytes)
CREATE post/post.service.ts (633 bytes)
CREATE post/post.service.spec.ts (464 bytes)
CREATE post/dto/create-post.dto.ts (31 bytes)
CREATE post/dto/update-post.dto.ts (173 bytes)
CREATE post/entities/post.entity.ts (22 bytes)
빌드
npm run build
실행
npm run start
이렇게 뜨면 정상작동
PS V:\Sparta\memoNote\github\D1\dailyCoding\NESTJS\3W\nestjs_board\src> npm run start
> nestjs_board@0.0.1 start
> nest start
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [NestFactory] Starting Nest application...
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [InstanceLoader] AppModule dependencies initialized +6ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [InstanceLoader] PostModule dependencies initialized +0ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RoutesResolver] AppController {/}: +3ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RoutesResolver] PostController {/post}: +0ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RouterExplorer] Mapped {/post, POST} route +1ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RouterExplorer] Mapped {/post, GET} route +0ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RouterExplorer] Mapped {/post/:id, GET} route +1ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RouterExplorer] Mapped {/post/:id, PATCH} route +0ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [RouterExplorer] Mapped {/post/:id, DELETE} route +1ms
[Nest] 23560 - 2025. 01. 21. 오후 3:31:03 LOG [NestApplication] Nest application successfully started +1ms
클라이언트와 서버의 데이터 송수신은 DTO
서비스에서
BadRequestException 던지기
throw new BadRequestException
// post.service.ts
import _ from 'lodash'; // 추가
import { BadRequestException, 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 }[] = [];
// 각 배열은 { id: 11; title: '열한번째 포스트'; content: '이제 열한번째 포스트. 한참남았다' } 이런 형태
private articlePasswords = new Map<number, number>();
create(createPostDto: CreatePostDto) {
const { title, content, password } = createPostDto;
const id = this.articles.length + 1;// 프리즈마가 아니라 그런지 id를 지정하네
this.articles.push({ id, title, content }); // 배열에 추가
this.articlePasswords.set(id, password); // articlePasswords배열은 id값이 키고, password가 값으로 추가
return { id };
}
// 게시물 ID와 게시물 제목만 리턴
findAll() {
return this.articles.map(({id,title}) => ({id,title}))
// this.articles 배열의 각 요소를 특정 형식으로 변환
}
findOne(id: number) {
return this.articles.find((article)=>article.id===id);
}
update(id: number, updatePostDto: UpdatePostDto) {
const { content, password } = updatePostDto;
const article = this.articles.find((article) => article.id === id);
// 받은 id와 일치하는 값이 있는지 확인
if (_.isNil(article)) { // _.isNil : 널이 아니면 false 반환
throw new NotFoundException('게시물을 찾을 수 없습니다.');// NotFound는 404. ctrl + '.' -> "@nestjs/common"에서 가져오기 업데이트
}
// id로 비번찾고 받은 비번과 일치하는지 확인
const articlePassword = this.articlePasswords.get(id);
if (!_.isNil(articlePassword) && articlePassword !== password) {
throw new NotFoundException('비밀번호가 일치하지 않습니다.'); // 404
}
// id와 일치하는 기사와 비번이 있으면 내용 수정
article.content = content;
}
remove(id: number, deletePostDto: RemovePostDto) {
const articleIndex = this.articles.findIndex(
(article) => article.id === id,
);
if (articleIndex === -1) {
throw new NotFoundException('게시물을 찾을 수 없습니다.');
}
const articlePassword = this.articlePasswords.get(id);
if (
!_.isNil(articlePassword) &&
articlePassword !== deletePostDto.password
) {
throw new NotFoundException('비밀번호가 일치하지 않습니다.');
}
this.articles.splice(articleIndex, 1);
}
}
// nestjs_board\src\post\dto\create-post.dto.ts
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;
}
// nestjs_board\src\post\dto\remove-post.dto.ts
import { PickType } from '@nestjs/mapped-types';
import { CreatePostDto } from './create-post.dto';
export class RemovePostDto extends PickType(CreatePostDto, ['password']) {} // 비번만 받음
// nestjs_board\src\post\dto\update-post.dto.ts
import { OmitType } from '@nestjs/mapped-types'; // 여기 타입을 수정
import { CreatePostDto } from './create-post.dto';
// OmitType : 무언가 생략한타입
// type를 생략해서 OmitType사용함
export class UpdatePostDto extends OmitType(CreatePostDto, ['title']) {}
// post.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { RemovePostDto } from './dto/remove-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 http://localhost:3000/post/1
@Get(':id') // 인자가 ':'로 시작하는건 파라미터로 쓰인다는 뜻
findOne(@Param('id') id: string) {
return this.postService.findOne(+id);
}
// PATCH http://localhost:3000/post/1
@Patch(':id')
// @Param(파람) : 'id'를 id의 값(문자열 타입)으로 받는다는 뜻
update(@Param('id') id: string, @Body() updatePostDto: UpdatePostDto) {
return this.postService.update(+id, updatePostDto); // service에서는 id가 숫자타입
}
// DELETE http://localhost:3000/post/1
@Delete(':id')
remove(@Param('id') id: string, @Body() deletePostDto: RemovePostDto) {
return this.postService.remove(+id, deletePostDto);
}
}
서버 실행
npm run start # package.json 의 scripts{"start": "nest start",},
이렇게 뜨면 서버 실행중
> nestjs_board@0.0.1 start
> nest start
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [NestFactory] Starting Nest application...
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [InstanceLoader] AppModule dependencies initialized +6ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [InstanceLoader] PostModule dependencies initialized +0ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RoutesResolver] AppController {/}: +3ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RoutesResolver] PostController {/post}: +0ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RouterExplorer] Mapped {/post, POST} route +1ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RouterExplorer] Mapped {/post, GET} route +0ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RouterExplorer] Mapped {/post/:id, GET} route +0ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RouterExplorer] Mapped {/post/:id, PATCH} route +1ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [RouterExplorer] Mapped {/post/:id, DELETE} route +0ms
[Nest] 18860 - 2025. 01. 21. 오후 7:29:41 LOG [NestApplication] Nest application successfully started +1ms
_______________________________________
개시글 생성
POST http://localhost:3000/
헤더에 추가 - 컨텐트를 json타입으로 넘길거다
Content-Type : application/json
body에 내용 추가
{
"title": "열한번째 포스트", // string
"content": "이제 열한번째 포스트. 한참남았다", // string
"password": 15555 // number
}
_______________________________________
전체조회
GET http://localhost:3000/post
상세조회
GET http://localhost:3000/post/:id
_______________________________________
수정
PATCH http://localhost:3000/post
헤더에 추가 - 컨텐트를 json타입으로 넘길거다
Content-Type : application/json
body에
{
"content": "이제 열한번째 포스트. 수정완료",
"password": 15555
}
_______________________________________
삭제
DELETE http://localhost:3000/post:id
body에
{"password": 15555 }