Nest.js로 CRUD 구현하기

노누리·2022년 6월 13일
1

저번 포스팅에 nest를 사용하기위한 기초 세팅, DB 연결을 모두 끝냈다.
이번 포스팅에서는 CRUD 구현을 해보려고 한다.

지난 포스팅


코드에서 데이터베이스 사용하기

board.service.ts

constructor(
    @Inject('BOARD_REPOSITORY')
    private boardRepository: Repository<Board>,
  ) {}

service단에서 위의 생성자를 만들어주어야 이전 포스팅에서 만들어 둔 DB 레포지토리를 사용할 수 있다!

ex) this.boardRepository.create(생성할 객체)


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 완성

Read

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

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()을 많이 사용하니 참고하자.

포스트맨으로 실행하면 값이 수정된것을 확인할 수 있다.

Delete

정보를 삭제하는 것도 해당하는 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의 게시글 정보가 없습니다.');
    }
  }
}
profile
백엔드 개발자입니다.

0개의 댓글