저번 포스팅에 nest를 사용하기위한 기초 세팅, DB 연결을 모두 끝냈다.
이번 포스팅에서는 CRUD 구현을 해보려고 한다.
지난 포스팅
board.service.ts
constructor(
@Inject('BOARD_REPOSITORY')
private boardRepository: Repository<Board>,
) {}
service단에서 위의 생성자를 만들어주어야 이전 포스팅에서 만들어 둔 DB 레포지토리를 사용할 수 있다!
ex) this.boardRepository.create(생성할 객체)
우선 create하기위한 dto를 생성해야한다.
dto는 board를 만들기위해서 필요한 entity의 필드들을 정리해둔 것인데, create할때에는 id값은 넣지 않기 때문에 id값을 제외하여 사용자가 입력해야할 필드들만 넣어준다.
create-board.dto.ts
export class CreateBoardDto {
readonly title: string;
readonly description: string;
}
사실 이외에도 데코레이터를 더 추가할 수 있는데 그것은 이후에 따로 포스팅하려고 한다.
이렇게 만든 create_dto를 아래의 controller,service 파일에서 사용한다.
board.controller.ts
@Post()
createBoard(@Body() boardData: CreateBoardDto): Promise<Board> {
return this.boardService.create(boardData);
}
board.service.ts
async create(createBoardDto: CreateBoardDto): Promise<Board> {
const { title, description } = createBoardDto;
const board = await this.boardRepository.create({
title,
description,
});
await this.boardRepository.save(board);
return board;
}
위와 같은 코드를 짜면 CRUD에서 C
는 완성했다. 현재는 swagger를 사용하지 않기 때문에 포스트맨으로 확인해보면 다음과 같다.
포스트맨을 이용하여 위와 같이 title, description 값을 넣어주고 성공했다는 응답을 받고 나면
해당하는 postgres DB안에 값이 들어가는 것을 확인할 수 있다.
Create 완성
board 정보를 조회하는 것은 따로 dto가 필요없기 때문에 사용하지 않고 바로 레포지토리를 이용하여 정보를 조회한다.
board.controller.ts
@Get()
getAllBoards(): Promise<Board[]> {
return this.boardService.AllBoards();
}
board.service.ts
async AllBoards(): Promise<Board[]> {
return this.boardRepository.find({});
}
controller, service 모두 반환값의 타입을 보면 알 수 있듯이 Board
형식을 따르되 하나의 정보만 받는 것이 아니기 때문에 []
을 이용하여 배열 형식으로 여러개의 Board 정보를 받도록 한다.
이렇게 간단한게 레포지토리를 통해 find
해주면 모든 Board 정보를 조회할 수 있다.
update 또한 create와 같이 필요한 필드들만 값을 수정하도록 dto를 생성해주어야 한다.
npm install --save @nestjs/mapped-types
위의 모듈을 설치해준후 CreateBoardDto의 정보를 일부 가져온다.
update-board.dto.ts
import { CreateBoardDto } from './create-board.dto';
import { PartialType } from '@nestjs/mapped-types';
export class UpdateBoardDto extends PartialType(CreateBoardDto) {}
현재는 createBoardDto와 같은 필드를 이용하기 때문에 위와 같은 코드를 사용하면 된다.
board.controller.ts
@Patch(':id')
updateBoard(@Param('id') id: number, @Body() boardData: UpdateBoardDto) {
return this.boardService.update(id, boardData);
}
board.service.ts
async update(id: number, updateBoardDto: UpdateBoardDto): Promise<Board> {
const board = await this.boardRepository.findOne({
where: {
id,
},
});
if (!board) {
throw new NotFoundException('해당 id의 게시글 정보는 존재하지 않습니다.');
}
await this.boardRepository.update(id, updateBoardDto);
const updatedBoard = await this.boardRepository.findOne({
where: {
id,
},
});
return updatedBoard;
}
board 정보를 수정하려면 해당하는 board의 id값을 받아서 해당하는 board의 정보만 수정해야한다.
@Param()
을 이용해서 id값을 받아오고 @Body()
를 이용하여 수정할 정보를 받아온다.
이때, update를 할 때 데코레이터를 두 개를 선택할 수 있는데 @Put()
과 @Patch()
가 있다.
@Put()
은 정보를 전체적으로 모두 수정할때 사용하고
@Patch()
은 일부의 정보만 수정할때 사용한다.
보통 @Put()
보다는 @Patch()
을 많이 사용하니 참고하자.
포스트맨으로 실행하면 값이 수정된것을 확인할 수 있다.
정보를 삭제하는 것도 해당하는 board의 id값을 받아 삭제한다.
board.controller.ts
@Delete(':id')
deleteBoard(@Param('id') id: number) {
return this.boardService.delete(id);
}
board.service.ts
async delete(id: number) {
const result = await this.boardRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException('해당하는 id의 게시글 정보가 없습니다.');
}
}
위의 코드에서 result를 console로 출력해보면 다음과 같이 나온다.
즉, affected:1
이라는 뜻은 성공적으로 삭제를 했다는 뜻이고 affected:0
이라는 뜻은 없는 id값이거나 삭제에 실패했다는 뜻이기 때문에 if문으로 조건을 걸어주어 오류를 보내는 것이다.
위의 코드를 모두 작성하고 실행해보면 아래와 같이 정보가 사라진 것을 확인할 수 있다.
이렇게 Nestjs를 통한 CRUD 구현을 완성했다.
아래는 전체 코드이다.
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BoardModule } from './board/board.module';
@Module({
imports: [BoardModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
board.entity.ts
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Board extends BaseEntity {
@PrimaryGeneratedColumn()
id!: number;
@Column()
title!: string;
@Column()
description!: string;
}
board.repository.ts
import { Board } from 'src/entities/board.entity';
import { DataSource } from 'typeorm';
export const boardRepository = [
{
provide: 'BOARD_REPOSITORY',
useFactory: (dataSource: DataSource) => dataSource.getRepository(Board),
inject: ['DATA_SOURCE'],
},
];
board.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from 'src/database/database.module';
import { boardRepository } from 'src/repository/board.repository';
import { BoardController } from './board.controller';
import { BoardService } from './board.service';
@Module({
imports: [DatabaseModule],
controllers: [BoardController],
providers: [...boardRepository, BoardService],
})
export class BoardModule {}
board.controller.ts
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
} from '@nestjs/common';
import { Board } from 'src/entities/board.entity';
import { BoardService } from './board.service';
import { CreateBoardDto } from './dto/create-board.dto';
import { UpdateBoardDto } from './dto/update-board.dto';
@Controller('board')
export class BoardController {
constructor(private readonly boardService: BoardService) {}
@Get()
getAllBoards(): Promise<Board[]> {
return this.boardService.AllBoards();
}
@Post()
createBoard(@Body() boardData: CreateBoardDto): Promise<Board> {
return this.boardService.create(boardData);
}
@Patch(':id')
updateBoard(@Param('id') id: number, @Body() boardData: UpdateBoardDto) {
return this.boardService.update(id, boardData);
}
@Delete(':id')
deleteBoard(@Param('id') id: number) {
return this.boardService.delete(id);
}
}
board.service.ts
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
import { Board } from 'src/entities/board.entity';
import { Repository } from 'typeorm';
import { CreateBoardDto } from './dto/create-board.dto';
import { UpdateBoardDto } from './dto/update-board.dto';
@Injectable()
export class BoardService {
constructor(
@Inject('BOARD_REPOSITORY')
private boardRepository: Repository<Board>,
) {}
async AllBoards(): Promise<Board[]> {
return this.boardRepository.find({});
}
async create(createBoardDto: CreateBoardDto): Promise<Board> {
const { title, description } = createBoardDto;
const board = await this.boardRepository.create({
title,
description,
});
await this.boardRepository.save(board);
return board;
}
async update(id: number, updateBoardDto: UpdateBoardDto): Promise<Board> {
const board = await this.boardRepository.findOne({
where: {
id,
},
});
if (!board) {
throw new NotFoundException('해당 id의 게시글 정보는 존재하지 않습니다.');
}
await this.boardRepository.update(id, updateBoardDto);
const updatedBoard = await this.boardRepository.findOne({
where: {
id,
},
});
return updatedBoard;
}
async delete(id: number) {
const result = await this.boardRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException('해당하는 id의 게시글 정보가 없습니다.');
}
}
}