NestJS, TypeORM 이해하기
Table of Content
1. 게시글 엔터티 설계
엔터티란?
•
엔터티(Entity)는 업무에 필요하고 유용한 정보를 저장하고 관리하기 위한 "어떤 것(Thing)"
•
"어떤 것"이라고 부르는 것처럼 엔터티는 추상적인 의미를 가지며 학교나 학생처럼 현실 세계에서 눈에 보이는 개념일 수도 있고 주문이나 결제처럼 눈에 보이지 않는 개념일 수도 있다.
•
엔터티는 쉽게 말해 실체, 객체라고 생각할 수 있다.
◦
엔터티는 어떠한 객체이며 속성을 갖는다.
▪
객체가 가지고 있는 정보, 특징은 속성이 된다.
▪
더이상 분리 되지 않는 최소 데이터 단위를 속성이라 한다.(ex:이름, 제목, 나이, 키 등)
◦
두개 또는 그 이상의 엔터티들은 관계를 갖는다.
▪
다른 엔터티와의 존재에 의한 관계(ex:직원엔터티는 부서엔터티에 소속된다. 정적인 관계)
▪
다른 엔터티와의 행위에 의한 관계(ex:고객엔터티는 상품엔터티를 주문한다. 동적인 관계)
게시글이 가지게되는 속성은 어떤것들이 있을까?
•
id : 게시글의 번호(고유번호) - 숫자
•
author : 게시글 작성자 - 문자
•
title : 게시글의 제목 - 문자
•
contents : 게시글의 내용 - 문자
•
status : 공개 상태 - 공개 or 비공개
board.entity.ts
•
nestjs/cli 명령어로는 entity 파일의 자동 generation을 지원하지 않는다.
•
따라서 수동으로 파일을 생성해준다.
•
아래와 같이 위 설계한 속성들을 Board 클래스에 정의해준다.
◦
status는 BoardStatus라는 ENUM 클래스를 사용
import { BoardStatus } from "./board-status.enum";
export class Board {
id: number;
author: string;
title: string;
contents: string;
status: BoardStatus;
}
TypeScript
복사
board-status.enum.ts
•
ENUM 클래스는 열거형(여러개)를 다룰 때 사용되는 클래스이다.
•
카테고리, 장르, 유형, 타입과 같은 종류가 열거되는 것들에서 자주 사용되는 클래스이다.
•
아래와 같이 게시글의 상태를 PUBLIC 또는 PRIVATE로만 선택 될 수 있도록 정의해준다.
◦
해당 ENUM 클래스 자체가 위 board.entity.ts 에서 import 되어야 한다.
export enum BoardStatus {
PUBLIC = 'PUBLIC',
PRIVATE = 'PRIVATE'
}
TypeScript
복사
2. READ 1 - 모든 게시글을 조회하는 기능
boards.service.ts
•
데이터베이스로부터 게시글을 가져오는 비지니스 로직 부분이다.
•
실제 데이터베이스 연결 전 로컬 메모리를 사용하는 배열로 임시 데이터베이스로 시작
•
boards[] 배열은 임시 데이터베이스이며 private 접근제어자를 통해서 BoardsService 클래스를 통해서만 접근 가능 하도록 선언한다.
•
getAllBoards() 메서드를 통해서 데이터베이스의 내용(배열이므로 리스트 형태)을 반환한다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
// 전체 게시글 조회
getAllBoards(): Board[] { // 반환 타입 Board[] 배열
return this.boards;
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
GET 방식 HTTP Method를 받을 수 있는 @Get() 데코레이터를 작성하고 ('/') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
◦
해당 컨트롤러 자체의 공통 엔드포인트는 클래스에 작성된 @Controller(’boards’)로 /boards 프리픽스가 붙으며 내부 메서드에서 추가 엔드포인트를 지정하게 된다.
◦
◦
메서드 명칭은 getAllBoard()로 지정했다.
◦
요청에대한 반환(응답)은 boardsService의 getAllBoards() 메서드를 호출하여 그 반환 데이터를 응답하게 된다.
◦
따라서 생성자 주입을 통해서 BoardsService 를 주입받아야 해당 인스턴스의 메서드를 사용 할 수 있다.
import { Controller, Get } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
@Get('/') // GetMapping 핸들러 데코레이터
getAllBoard(): Board[] { // 반환 타입 Board[] 배열
return this.boardsService.getAllBoards();
}
}
TypeScript
복사
•
아래 테스트는 게시글 작성 기능 구현 이후에 확인 할 수 있다.
this는 현재 클래스(BoardsController)를 지칭한다.
•
위 생성자 주입 코드를 명시적으로 풀어 쓰면 다음과 같다.
import { Controller, Get } from '@nestjs/common';
import { BoardsService } from './boards.service';
@Controller('api/boards')
export class BoardsController {
// 필드 멤버 선언
private boardsService: BoardsService;
// 생성자 주입(DI)
constructor(boardsService: BoardsService){}
@Get('/') // GetMapping 핸들러 데코레이터
getAllBoard(){
return this.boardsService.getAllBoards();
}
}
TypeScript
복사
•
하지만 코드의 중복을 줄이고자 위 처럼 표현하여 필드 생성과 초기화를 동시에 하는 첫번째 방법을 사용하자.
Spring에서는 필드에 private 접근제어자와 함께 final 로 불변성을 유지하던데, Typescript에서는 이것을 고려하지 않는가?
•
Java의 final로 초기화 이후에 변경 할 수 없도록 하는 것은 좋은 접근
•
하지만 Typescript에서는 관례적이지 않은 조치이긴 하다.
•
개발자가 명시적으로 불변성을 원할 경우에만 사용이 필요할 경우 readonly 키워드를 사용하는 방법이 있다.
import { Controller, Get } from '@nestjs/common';
import { BoardsService } from './boards.service';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private readonly boardsService: BoardsService){}
@Get('/') // GetMapping 핸들러 데코레이터
getAllBoard(){
return this.boardsService.getAllBoards();
}
}
TypeScript
복사
•
위 코드에서 boardsService 필드는 readonly로 선언되었기 때문에, 생성자에서 초기화된 이후에는 변경할 수 없어 불변성을 보장 할 수 있다.
3. CREATE - 게시글을 작성하는 기능
boards.service.ts
게시글을 작성하고 데이터베이스에 저장하는 비지니스 로직이 추가되었다.
•
createBoard() 메서드는 파라미터로 author, title, contents 를 받아서 함수에서 사용된다.
•
Board 클래스 타입의 board 변수에는 id, author, title, contents, status가 할당되어 게시글 객체가 생성된다.
◦
id는 게시글의 고유번호로 임시로 1씩 증가하는 연산을 사용했다.
◦
author, title, contents는 파라미터로 넘어온 값들이 할당된다.
◦
status는 게시글의 공개상태로 임시로 공개 상태인 PUBLIC으로 하드코딩 되어 있다.
•
위 필드 값들을 가진 board 객체는 push를 통해 임시 데이터베이스인 board[] 배열에 저장된다.
•
저장 후 board 객체를 반환한다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { BoardStatus } from './board-status.enum';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
// 게시글 작성
createBoard(author: string, title: string, contents: string) {
const board: Board = {
id: this.boards.length + 1, // 임시 Auto Increament
author,
title,
contents,
status: BoardStatus.PUBLIC
}
this.boards.push(board);
return board;
}
// 전체 게시글 조회
getAllBoards(): Board[] { // 반환 타입 Board[] 배열
return this.boards;
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
POST 방식 HTTP Method를 받을 수 있는 @Post() 데코레이터를 작성하고 ('/') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
•
•
메서드 명칭은 createBoard()로 지정했다.
•
@Body() 데코레이터를 통해 HTTP 요청에 담긴 데이터를 가져 올 수 있다.
◦
ex: @Body(’title’) 처럼 key값으로 요청 바디에 담긴 데이터의 값을 가져 올 수 있음
◦
author, title, contents 3개의 값을 service 계층으로 전달한다.
•
요청에대한 반환(응답)은 boardsService의 createBoard() 메서드를 호출하여 그 반환 데이터를 응답하게 된다. (→ 결과, 저장된 board 객체가 반환 되어 클라이언트로 응답하게 된다.)
import { Body, Controller, Get, Post } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
// 게시글 작성 기능
@Post('/') // PostMapping 핸들러 데코레이터
createBoard(
@Body('author') author: string,
@Body('title') title: string,
@Body('contents') contents: string) {
return this.boardsService.createBoard(author, title, contents)
}
// 게시글 조회 기능
@Get('/') // GetMapping 핸들러 데코레이터
getAllBoard(): Board[] { // 반환 타입 Board[] 배열
return this.boardsService.getAllBoards();
}
}
TypeScript
복사
•
POSTMAN을 통한 HTTP 요청 테스트를 진행
DTO란? 복습
•
DTO(Data Transfer Object)는 데이터 전송 및 이동을 위해 생성되는 객체를 의미
•
Client에서 보내오는(요청) 데이터를 객체로 처리할 때 사용
•
서버의 계층간의 이동, 다시 Client로 반환 할 때도 사용
•
추후, DTO에서 유효성 검사를 수행하는 것은 애플리케이션의 데이터 무결성을 보장하고, 중복된 코드 작성을 줄이며, 일관성을 유지하는 데 중요한 역할
◦
클라이언트에서 수신한 데이터가 적절한 형식과 조건을 만족하는지 확인하는 것은 DTO의 책임
•
유지보수적 측면에서도 프론트엔드측 또는 백엔드서버측 속성 수정에 유연하게 대처 가능
DTO를 사용하도록 리팩토링
•
현재 프로젝트는 @Body() 데코레이터를 통해 HTTP 요청에 담긴 데이터를 가져오고 있다.
•
하지만 어떤 객체의 필드가 수십개라면? 실제로 아래처럼 나타날 가능성이 있다.
•
따라서 DTO를 통해서 아래와 같은 문제점을 해결하고자 한다.
createBoard(
@Body('author') author: string,
@Body('title') title: string,
@Body('contents') contents: string
@Body....
@Body....
@Body....
@Body....
@Body....
@Body....
) {
return this.boardsService.create
TypeScript
복사
•
/boards/dto 폴더 생성
•
create-board.dto.ts 생성
◦
명명 규칙은 해당 DTO의 기능, 리소스가 명확하게 드러나도록 하는 것이 일반적인 관례
◦
Spring은 BoardsRequestDto.java 같은 표현이 관례였던것과 조금 차이점이 있음
export class CreateBoardDto {
author: string;
title: string;
contents: string;
}
TypeScript
복사
•
DTO로 계층간 이동에 적용하기 위해서 Controller, Service 계층의 소스코드 수정도 필요하다.
•
boards.controller.ts
◦
@Body로 1개씩 받아오던 것들이 DTO 객체를 활용하는 방식으로 변경되었다.
import { Body, Controller, Get, Post } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
// 게시글 작성 기능
@Post('/') // PostMapping 핸들러 데코레이터
createBoard(@Body() createBoardDto: CreateBoardDto) {
return this.boardsService.createBoard(createBoardDto)
}
// 게시글 조회 기능
@Get('/') // GetMapping 핸들러 데코레이터
getAllBoard(): Board[] { // 반환 타입 Board[] 배열
return this.boardsService.getAllBoards();
}
}
TypeScript
복사
•
boards.service.ts
◦
이제 DTO를 통해서 author, title, contents 필드 값들을 가져 올 수 있다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { BoardStatus } from './board-status.enum';
import { CreateBoardDto } from './dto/create-board.dto';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
// 게시글 작성
createBoard(createBoardDto: CreateBoardDto) {
const {author, title, contents} = createBoardDto;
const board: Board = {
id: this.boards.length + 1, // 임시 Auto Increament
author,
title,
contents,
status: BoardStatus.PUBLIC
}
this.boards.push(board);
return board;
}
// 전체 게시글 조회
getAllBoards(): Board[] { // 반환 타입 Board[] 배열
return this.boards;
}
}
TypeScript
복사
4. READ 2 - 특정 번호 게시글을 조회하는 기능(Path Variable)
boards.service.ts
특정 id의 게시글을 조회하는 비지니스 로직이 추가되었다.
•
getBoardDetailById() 메서드는 파라미터로 id를 받아서 메서드에서 사용된다.
•
find() 메서드를 통해서 데이터베이스에 저장된 게시글의 id가 파라미터로 받아온 id와 동일한 경우 해당 게시글을 board 객체로 조회한다.
•
조회된 board 객체가 반환된다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { BoardStatus } from './board-status.enum';
import { CreateBoardDto } from './dto/create-board.dto';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
...
// 특정 번호의 게시글 조회
getBoardDetailById(id: number): Board {
return this.boards.find((board) => board.id == id);
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
GET 방식 HTTP Method를 받을 수 있는 @Get() 데코레이터를 작성하고 ('/:id') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
•
파라미터 부분에서 @Param(’id’) 를 선언함으로써 Path Variable(경로 변수)를 사용 할 수 있다.
•
•
메서드 명칭은 getBoardDetailById()로 지정했다.
◦
메서드에 경로 변수로 받아온 id 값을 전달
•
요청에대한 반환(응답)은 boardsService의 getBoardDetailById(id) 메서드에 경로 변수로 받아온 id 값을 전달하며 호출하여 그 반환 데이터를 응답하게 된다. (→ 결과, 저장된 board 객체가 반환 되어 클라이언트로 응답하게 된다.)
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
...
// 특정 번호의 게시글 조회
@Get('/:id')
getBoardDetailById(@Param('id') id: number): Board{
return this.boardsService.getBoardDetailById(id);
}
}
TypeScript
복사
•
POSTMAN을 통한 HTTP 요청 테스트를 진행
5. READ 3 - 특정 키워드의 게시글을 조회(검색)하는 기능(Query String)
boards.service.ts
특정 keyword의 게시글을 조회하는 비지니스 로직이 추가되었다.
•
getBoardByAuthor() 메서드는 파라미터로 keyword를 받아서 함수에서 사용된다.
•
filter() 메서드를 통해서 데이터베이스에 저장된 게시글의 author가 파라미터로 받아온 author와 동일한 경우 해당 게시글들을 board 배열로 조회한다.
•
조회된 board 배열이 반환된다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { BoardStatus } from './board-status.enum';
import { CreateBoardDto } from './dto/create-board.dto';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
...
// 특정 작성자의 게시글 조회
getBoardByAuthor(author: string): Board[]{
return this.boards.filter((board) => board.author === author)
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
GET 방식 HTTP Method를 받을 수 있는 @Get() 데코레이터를 작성하고 ('/search') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
•
파라미터 부분에서 @Query(’author’) 를 선언함으로써 Query String을 사용 할 수 있다.
•
따라서 해당 메서드는 http://localhost:3000/api/boards/search/keyword?author=Sam 과 같은 게시글 작성자를 쿼리 스트링으로 받는 URL의 GET 요청을 처리하게 된다.
◦
/search 추가 경로를 안두고 ‘board?keyword=’처럼 처리하면 일반 조회 ’/board’와 충돌이 있음
•
메서드 명칭은 getBoardByAuthor()로 지정했다.
◦
메서드에 쿼리 스트링으로 받아온 author 값을 전달
•
요청에대한 반환(응답)은 boardsService의 getBoardByAuthor(author) 메서드에 경로 변수로 받아온 author 값을 전달하며 호출하여 그 반환 데이터를 응답하게 된다. (→ 결과, 저장된 board[] 배열이 반환 되어 클라이언트로 응답하게 된다.)
import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
...
// 특정 작성자의 게시글 조회
@Get('/search/:keyword')
getBoardByAuthor(@Query('author') author: string): Board[]{
return this.boardsService.getBoardByAuthor(author);
}
}
TypeScript
복사
•
POSTMAN을 통한 HTTP 요청 테스트를 진행
6. DELETE - 특정 게시글을 삭제하는 기능
boards.service.ts
특정 id의 게시글을 삭제하는 비지니스 로직이 추가되었다.
•
deleteBoardById() 메서드는 파라미터로 id를 받아서 함수에서 사용된다.
•
filter() 메서드를 통해서 데이터베이스에 저장된 게시글의 id가 파라미터로 받아온 id와 동일하지 않은 나머지 객체들을 다시 board[] 배열로 할당(저장)한다.
•
조회된 board 배열이 반환된다.
import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
...
// 특정 번호의 게시글 삭제
@Delete('/:id')
deleteBoardById(@Param('id') id: number): void{
this.boardsService.deleteBoardById(id);
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
DELETE 방식 HTTP Method를 받을 수 있는 @Delete() 데코레이터를 작성하고 ('/') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
•
파라미터 부분에서 @Param(’id’) 를 선언함으로써 Path Variable(경로 변수)를 사용 할 수 있다.
•
따라서 해당 메서드는 http://localhost:3000/api/boards/1 과 같은 게시글 번호를 Path Variable로 받는 URL의 DELETE 요청을 처리하게 된다.
•
메서드 명칭은 deleteBoardById()로 지정했다.
◦
메서드에 경로 변수로 받아온 id 값을 전달
•
deleteBoardById(id) 메서드에 경로 변수로 받아온 id 값을 전달하며 호출
import { Body, Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
...
// 특정 번호의 게시글 삭제
@Delete('/:id')
deleteBoardById(@Param('id') id: number): void{
this.boardsService.deleteBoardById(id);
}
}
TypeScript
복사
•
POSTMAN을 통한 HTTP 요청 테스트를 진행
7. UPDATE 1 - 특정 게시글을 일부 수정하는 기능(PATCH)
boards.service.ts
특정 id의 게시글의 일부 정보를 수정하는 비지니스 로직이 추가되었다.
•
updateBoardStatusById() 메서드는 파라미터로 id, @Body()를 통해서 status를 받아서 함수에서 사용된다.
•
특정 id의 게시글을 찾는 메서드를 재활용하여 getBoardDetailById() 메서드를 통해서 데이터베이스에 저장된 게시글의 id가 파라미터로 받아온 id와 동일한 수정 할 대상인 board 객체를 찾아낸다.
•
수정 대상 board 객체의 status값을 파라미터로 전달 받은 status값으로 할당(수정)한다.
•
수정된 board 객체가 반환된다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { BoardStatus } from './board-status.enum';
import { CreateBoardDto } from './dto/create-board.dto';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
...
// 특정 번호의 게시글 조회
getBoardDetailById(id: number): Board {
return this.boards.find((board) => board.id == id);
}
...
// 특정 번호의 게시글의 일부 수정
updateBoardStatusById(id: number, status: BoardStatus): Board {
const foundBoard = this.getBoardDetailById(id);
foundBoard.status = status;
return foundBoard;
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
Patch 방식 HTTP Method를 받을 수 있는 @Patch() 데코레이터를 작성하고 ('/:id/status') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
•
파라미터 부분에서 @Param(’id’) 를 선언함으로써 Path Variable(경로 변수)를 사용 할 수 있다.
•
따라서 해당 메서드는 localhost:3000/api/boards/1/status 과 같은 게시글 번호를 Path Variable로 받는 URL의 PATCH 요청을 처리하게 된다.
•
메서드 명칭은 updateBoardStatusById()로 지정했다.
◦
메서드에 경로 변수로 받아온 id 값을 전달
•
updateBoardStatusById(id) 메서드에 경로 변수로 받아온 id 값을 전달하며 호출
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
import { BoardStatus } from './board-status.enum';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
...
// 특정 번호의 게시글의 일부 수정
@Patch('/:id/status')
updateBoardStatusById(@Param('id') id: number, @Body('status') status: BoardStatus): Board {
return this.boardsService.updateBoardStatusById(id, status)
}
}
TypeScript
복사
•
POSTMAN을 통한 HTTP 요청 테스트를 진행
8. UPDATE 2 - 특정 게시글을 전체 수정하는 기능(PUT)
boards.service.ts
특정 id의 게시글의 일부 정보를 수정하는 비지니스 로직이 추가되었다.
•
updateBoardStatus() 메서드는 파라미터로 id, @Body()를 통해서 updateBoardDto를 받아서 함수에서 사용된다.
•
update-board.dto.ts 생성
import { BoardStatus } from "../board-status.enum";
export class UpdateBoardDto {
author: string;
title: string;
contents: string;
status: BoardStatus;
}
TypeScript
복사
•
특정 id의 게시글을 찾는 메서드를 재활용하여 getBoardDetailById() 메서드를 통해서 데이터베이스에 저장된 게시글의 id가 파라미터로 받아온 id와 동일한 수정 할 대상인 board 객체를 찾아낸다.
•
수정 대상 board 객체의 필드값을 파라미터로 전달 받은 updateBoardDto 객체의 각 필드값으로 할당(수정)한다.
•
수정된 board 객체가 반환된다.
import { Injectable } from '@nestjs/common';
import { Board } from './board.entity';
import { BoardStatus } from './board-status.enum';
import { CreateBoardDto } from './dto/create-board.dto';
import { UpdateBoardDto } from './dto/update-board.dto';
@Injectable()
export class BoardsService {
private boards: Board[] = []; // 임시 DB처럼 사용할 배열(로컬 메모리) // 저장되는 데이터 타입 Board[] 배열
...
// 특정 번호의 게시글 조회
getBoardDetailById(id: number): Board {
return this.boards.find((board) => board.id == id);
}
...
// 특정 번호의 게시글의 전체 수정
updateBoardById(id, updateBoardDto : UpdateBoardDto): Board {
const foundBoard = this.getBoardDetailById(id);
const {author, title, contents, status} = updateBoardDto;
foundBoard.author = author;
foundBoard.title = title;
foundBoard.contents = contents;
foundBoard.status = status;
return board;
}
}
TypeScript
복사
boards.controller.ts
•
요청을 받는 컨트롤러 부분이다.
•
Put 방식 HTTP Method를 받을 수 있는 @Put() 데코레이터를 작성하고 ('/:id') 처럼 해당 메서드의 추가 엔드포인트를 지정한다.
•
파라미터 부분에서 @Param(’id’) 를 선언함으로써 Path Variable(경로 변수)를 사용 할 수 있다.
•
@Body()로 DTO 객체를 활용하여 updateBoardDto 를 두번째 파라미터로 받는다.
•
따라서 해당 메서드는 localhost:3000/api/boards/1 과 같은 게시글 번호를 Path Variable로, 요청 바디의 값들은 DTO 객체로 변환되어 받는 URL의 PUT 요청을 처리하게 된다.
•
메서드 명칭은 updateBoardById()로 지정했다.
◦
메서드에 경로 변수로 받아온 id 값, 요청 바디의 데이터를 DTO로 변환한 updateBoardDto를 전달
•
updateBoardStatusById(id, updateBoardDto) 메서드에 경로 변수로 받아온 id, updateBoardDto 객체를 전달하며 호출, 수정된 board 객체를 반환받아 클라이언트로 응답한다.
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { Board } from './board.entity';
import { CreateBoardDto } from './dto/create-board.dto';
import { BoardStatus } from './board-status.enum';
import { UpdateBoardDto } from './dto/update-board.dto';
@Controller('api/boards')
export class BoardsController {
// 생성자 주입(DI)
constructor(private boardsService: BoardsService){}
...
// 특정 번호의 게시글의 전체 수정
@Put('/:id')
updateBoardById(@Param('id') id: number, @Body() updateBoardDto: UpdateBoardDto): Board {
return this.boardsService.updateBoardById(id, updateBoardDto)
}
}
TypeScript
복사
•
POSTMAN을 통한 HTTP 요청 테스트를 진행
9. 3-Layer-Architecture
현재 우리가 작성한 소스코드들이 담겨진 파일들을 살펴보자.
3계층 아키텍처?
애플리케이션을
•
프레젠테이션 계층 또는 사용자 인터페이스,
•
데이터가 처리되는 애플리케이션 계층
•
그리고 애플리케이션과 관련된 데이터가 저장 및 관리되는 데이터 계층
3개의 논리적이고 물리적인 컴퓨팅 계층으로 구성하는 확립된 소프트웨어 애플리케이션 아키텍처
- ibm
Related Posts
Search