Blog

[NestJS] 6. DB+TypeORM → CRUD Operation 연동 리팩토링 | 🎯 중요

Category
Author
PinOnMain
1 more property
NestJS, TypeORM 이해하기
Table of Content

1. Service 계층 리팩토링

📌
임시DB에서 MySQL, TypeORM을 적용했으므로 Service 계층 코드를 전체적으로 수정한다.
임시 비활성화 주석 부분을 모두 삭제해도 무방하다 전반적인 코드가 async/await 문법으로 수정되었다.
CREATE, UPDATE 기능은 save() 메서드를 사용했다.
create() 메서드는 저장 전 DTO객체를 Entity 클래스를 참조한 인스턴스를 생성한다.
PATCH 메서드의 일부 수정 기능은 update() 메서드를 사용했다.
READ 중 전체 조회 기능은 find() 메서드를 사용했다.
READ 중 특정 조회 기능은 findOneBy(), findBy() 메서드 사용했다.
findOneOrFail() 처럼 예외 처리를 함께 할 수 있는 메서드도 존재
DELETE 기능은 remove(), delete() 메서드를 사용할 수 있다.
Cascade 에 대해 인지하고 사용해야 한다.
boards.service.ts
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { Board } from './boards.entity'; import { BoardStatus } from './boards-status.enum'; import { CreateBoardDto } from './dto/create-board.dto'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { UpdateBoardDto } from './dto/update-board.dto'; @Injectable() export class BoardsService { // Repository 계층 DI constructor( @InjectRepository(Board) private boardRepository : Repository<Board> ){} // 게시글 조회 기능 async getAllBoards(): Promise<Board[]> { const foundBoards = await this.boardRepository.find(); return foundBoards; } // 특정 게시글 조회 기능 async getBoardDetailById(id: number): Promise<Board> { const foundBoard = await this.boardRepository.findOneBy({ id: id }); if(!foundBoard) { throw new NotFoundException(`Board with ID ${id} not found`); } return foundBoard; } // 키워드(작성자)로 검색한 게시글 조회 기능 async getBoardsByKeyword(author: string): Promise<Board[]> { if (!author) { throw new BadRequestException('Author keyword must be provided'); } const foundBoards = await this.boardRepository.findBy({ author: author }) if (foundBoards.length === 0) { throw new NotFoundException(`No boards found for author: ${author}`); } return foundBoards; } // 게시글 작성 기능 async createBoard(createBoardDto: CreateBoardDto): Promise<Board> { const { author, title, contents } = createBoardDto; if (!author || !title || !contents) { throw new BadRequestException('Author, title, and contents must be provided'); } const newBoard: Board = { id: 0, // 임시 초기화 author, // author: createBoardDto.author title, contents, status: BoardStatus.PUBLIC }; const createdBoard = await this.boardRepository.save(newBoard); return createdBoard; } // 특정 번호의 게시글 수정 async updateBoardById(id: number, updateBoardDto: UpdateBoardDto): Promise<Board> { const foundBoard = await this.getBoardDetailById(id); const { title, contents } = updateBoardDto; if (!title || !contents) { throw new BadRequestException('Title and contents must be provided'); } foundBoard.title = title; foundBoard.contents = contents; const updatedBoard = await this.boardRepository.save(foundBoard) return updatedBoard; } // 특정 번호의 게시글 일부 수정 async updateBoardStatusById(id: number, status: BoardStatus): Promise<void> { const result = await this.boardRepository.update(id, { status }); if (result.affected === 0) { throw new NotFoundException(`Board with ID ${id} not found`); } } // 게시글 삭제 기능 async deleteBoardById(id: number): Promise<void> { const foundBoard = await this.getBoardDetailById(id); await this.boardRepository.delete(foundBoard); } }
TypeScript
복사

2. Controller 계층 리팩토링

📌
Service 계층 수정사항에 따라 일부 반환 타입등에 Promise가 추가되었으므로 아래처럼 Controller도 수정한다.
임시 주석을 해제하고 일부 변경 사항을 확인하여 수정한다.
단일 객체 반환은 Promise<Board> 리스트 배열 반환은 Promise<Board[]> 와 같다.
boards.controller.ts
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, UsePipes, ValidationPipe } from '@nestjs/common'; import { BoardsService } from './boards.service'; import { Board } from './boards.entity'; import { CreateBoardDto } from './dto/create-board.dto'; import { BoardResponseDto } from './dto/board-response.dto'; import { BoardSearchResponseDto } from './dto/board-search-response.dto'; import { UpdateBoardDto } from './dto/update-board.dto'; import { BoardStatusValidationPipe } from './pipes/board-status-validation.pipe'; import { BoardStatus } from './boards-status.enum'; @Controller('api/boards') @UsePipes(ValidationPipe) export class BoardsController { // 생성자 주입 constructor(private boardsService: BoardsService){} // 게시글 조회 기능 @Get('/') async getAllBoards(): Promise<BoardResponseDto[]> { const boards: Board[] = await this.boardsService.getAllBoards(); const boardsResponseDto = boards.map(board => new BoardResponseDto(board)); return boardsResponseDto; } // 특정 게시글 조회 기능 @Get('/:id') async getBoardDetailById(@Param('id') id: number): Promise<BoardResponseDto> { const boardResponseDto = new BoardResponseDto(await this.boardsService.getBoardDetailById(id)); return boardResponseDto; } // 키워드(작성자)로 검색한 게시글 조회 기능 @Get('/search/:keyword') async getBoardsByKeyword(@Query('author') author: string): Promise<BoardSearchResponseDto[]> { const boards: Board[] = await this.boardsService.getBoardsByKeyword(author); const boardsResponseDto = boards.map(board => new BoardSearchResponseDto(board)); return boardsResponseDto; } // 게시글 작성 기능 @Post('/') async createBoard(@Body() createBoardDto: CreateBoardDto): Promise<BoardResponseDto> { const boardResponseDto = new BoardResponseDto(await this.boardsService.createBoard(createBoardDto)) return boardResponseDto; } // 특정 번호의 게시글 수정 @Put('/:id') async updateBoardById( @Param('id') id: number, @Body() updateBoardDto: UpdateBoardDto): Promise<BoardResponseDto> { const boardResponseDto = new BoardResponseDto(await this.boardsService.updateBoardById(id, updateBoardDto)) return boardResponseDto; } // 특정 번호의 게시글 일부 수정 @Patch('/:id') async updateBoardStatusById( @Param('id') id: number, @Body('status', BoardStatusValidationPipe) status: BoardStatus): Promise<void> { await this.boardsService.updateBoardStatusById(id, status); } // 게시글 삭제 기능 @Delete('/:id') async deleteBoardById(@Param('id') id: number): Promise<void> { await this.boardsService.deleteBoardById(id); } }
TypeScript
복사
Search
⚙
 | Main Page | Category |  Tags | About Me | Contact | Portfolio