생명주기?

(가정) 게시물이 생성되었을때 카테고리는 무조건 선택해야하고 해쉬태그는 선택적으로 해야한다.

 

게시물과 게시물과 카테고리 관계는 같은 생명주기

→ 게시물과 카테고리의 관계는 게시물이 생성될때 같이 생성되고 게시물이 삭제 될때 같이 삭제된다.

 

게시물과 해쉬태그의 관계는 다른 생명주기

 게시물이 생성되었다고 해서 무조건 게시물과 해쉬태그의 관계가 생성되지 않는다.

 

이와 같은 개념으로 src에 있는 routes 에 만들어야할 폴더를 구성해보자.

 

폴더구성을 변경하기 전

├── board
│   ├── board.controller.ts
│   ├── board.module.ts
│   ├── board.service.ts
│   ├── dtos
│   └── functions
├── board_category
│   ├── boardToCategory.module.ts
│   ├── boardToCategory.service.ts
│   └── dto
├── category
│   ├── category.controller.ts
│   ├── category.module.ts
│   ├── category.service.ts
│   └── dto
├── hashtag
│   ├── dto
│   ├── hashtag.controller.ts
│   ├── hashtag.module.ts
│   └── hashtag.service.ts
├── hashtag_board
│   ├── dto
│   ├── hashtagToBoard.controller.ts
│   ├── hashtagToBoard.index.ts
│   ├── hashtagToBoard.module.ts
│   └── hashtagToBoard.service.ts
└── reply
    ├── dto
    ├── reply.controller.ts
    ├── reply.module.ts
    └── reply.service.ts

 

폴더구성을 변경한 후 : board_category 삭제

.
├── board
│   ├── board.controller.ts
│   ├── board.module.ts
│   ├── board.service.ts
│   ├── boardToCategory.service.ts
│   ├── dto
│   └── functions
├── category
│   ├── category.controller.ts
│   ├── category.module.ts
│   ├── category.service.ts
│   └── dto
├── hashtag
│   ├── dto
│   ├── hashtag.controller.ts
│   ├── hashtag.module.ts
│   └── hashtag.service.ts
├── hashtag_board
│   ├── dto
│   ├── hashtagToBoard.controller.ts
│   ├── hashtagToBoard.index.ts
│   ├── hashtagToBoard.module.ts
│   └── hashtagToBoard.service.ts
└── reply
    ├── dto
    ├── reply.controller.ts
    ├── reply.module.ts
    └── reply.service.ts

가정:

Category entity에 있는 여러가지  Category를 찾는 서비스를 구현한다

Category 는 Animall, Zoo, IT, Book 가 있다

 

입력 : 

// json
{
	"names" : [ 1, "earth", 3]
}

 

service code

....
async findCategories(findCategoriesRequestDto: FindCategoriesRequestDto): Promise<any> {
    const { ids } = findCategoriesRequestDto;
    const categoryList: number[] = [];
    const results = await Promise.allSettled(ids.map((id) => this.categoryRepository.findOne({ where: { id } })));
    results.forEach((result) => {
      console.log(result)
      categoryList.push(result['value']);
    });
    return categoryList;
  }
...

 

 

콘솔창 출력:

{
  status: 'fulfilled',
  value: Category {
    createdAt: 2024-04-30T07:14:41.809Z,
    updatedAt: 2024-04-30T07:14:41.809Z,
    deletedAt: null,
    id: 1,
    name: 'Animal'
  }
}
{ status: 'fulfilled', value: null }
{
  status: 'fulfilled',
  value: Category {
    createdAt: 2024-04-30T07:14:41.819Z,
    updatedAt: 2024-04-30T07:14:41.819Z,
    deletedAt: null,
    id: 3,
    name: 'IT'
  }
}

  

- entity 에 해당이름이 있던지 없던지 해당 요청은 실행된다.

- entity 에 해당이름이 없는 경우를 처리하려면 vlaue에 접근해서 해야한다. 

1. 아래와 같이 breakpoint 를 표시

 

2. package.json 에 들어가서  "start:debug" 왼쪽에 있는 을 클릭하여  디버그를 클릭

 

3.실행시킨후 중단점을 찍은 부분을 포함하는 라우트를 실행한다.

-  예를 들어. 

     - 중단점을 게시물을 생성하는 service에 중단점을 찍었으면 Post man 을 이용하여 게시물 요청을 전송하여 둔다.

http://localhost:3000/boards 전송

 

전송후 결과 예시

 

Webstrom 코드창

첫 중단점에 멈춰져있는 예

Webstrom 디버그창

 

 

디버그창에 있는 버튼을 이용하는 방법

 

다음 중단 점으로 이동,

없으면 디버그 중단

 

한 라인 단위로 이동, 한 행씩 코드를 실행하면서 아래로 이동

이때 for 문 과 같은 반복문 구간은 한 번에 실행시키고 넘어간다.

 

함수 및 반복문 구간으로 들어가서 좀 더 자세하게 확인하기.

계속 누르면 사용하고 있는 라이블러리 함수까지 들어가게된다.
누른 후 라인단위로 이동하는 버튼을 눌러서 반복구간을 체크 할 수 있다.

 

관련된 함수의 링크를 타고 해당 함수가있는 코드로 들어갔을때  빠져나올때 사용하는 버튼

 

Post 를 이용하여 게시물을 만들때

import { IsNotEmpty, Length } from 'class-validator';

export class CreatePostRequestDto {
  @IsNotEmpty()
  @Length(1, 15)
  title: string;

  @IsNotEmpty()
  @Length(1, 1000)
  content: string;
  
  @IsNotEmpty()
  categories: string[];
  
  @@IsNotEmpty()
  hashtags: string[];
}

 

 

Patch 를 이용하여 게시물을 업데이트 할때

-  전부다 수정하지 않고 원하는 요소만 수정하고 싶다 : @IsOptional(),  ? 입력해주면 된다. 

import { IsOptional, Length } from 'class-validator';

export class UpdateRequestDto {
  @IsOptional()
  @Length(1, 15)
  title?: string;

  @IsOptional()
  @Length(1, 1000)
  content?: string;

  @IsOptional()
  categories?: string[];

  @IsOptional()
  hashtags?: string[];
}

 

NestJS 에  있는 "ValidationPipe" 를 이용하면 유효성 검사를 할 수 있다.

 

"ValidationPipe" 를 이용하기 위해서 아래 두개의 라이브러리를 설치해야한다.

$ npm i --save class-validator class-transformer

 

자동으로 적용하기 위해 main.ts 에 아래와 같이 입력

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { initializeTransactionalContext } from 'typeorm-transactional';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  initializeTransactionalContext(); // Transactional 을 이용하기 위해 선언
  const app = await NestFactory.create(AppModule);
  // 자동으로 validation을 확인하기 위해 선언
  app.useGlobalPipes(
    new ValidationPipe({
      transform: true, //class-transform 을 사용하기위한 설정
    }),
  );
  await app.listen(3000);
}
bootstrap();

 

 

- option 설명

app.useGlobalPipes(new ValidationPipe({
    whitelist: true,  // 미리 정의되지 않은 필드를 자동으로 제거합니다.
    transform: true,  // 클라이언트로부터 받은 데이터를 DTO 인스턴스로 자동 변환합니다.
    forbidNonWhitelisted: true,  // DTO에 정의되지 않은 필드가 포함되어 있을 경우 요청을 거부합니다.
    disableErrorMessages: false,  // 개발 중 오류 메시지를 표시하여 디버깅을 도움니다.
  }));

 

다음으로 Entity에 설정하여 유효성 테스트하는 방법

예시 1

import { validate, validateOrReject, Contains, Length,} from 'class-validator';
import { PrimaryGeneratedColumn, Column} from 'typeorm' 
import { Transactional } from 'typeorm-transactional';
import { plainToClass } from 'class-transformer';

// entity
export class Board {
  @PrimaryGeneratedColumn()
  id: number;
  
  @Length(10, 20)
  @Column
  title: string;

  @Contains('hello')
  @Column
  text: string;
}

// dto
export class CreatePostRequestDto{
  title: string;
  text: string;
}

@Transactional()
async create(createPostRequestDto: CreatePostRequestDto) {

    const newPost = plainToClass(Board, createPostRequestDto); // transformer 이용

    const postValidation = await validate(newPost); //validation 확인
    if (postValidation.length > 0) {
      throw new BadRequestException(`validation failed. errors: ${postValidation}`);
    }
 }

 

입력예시

createPostRequestDto = {
   title: "hi",
   text: "validation test"
}

 

에러출력 

{
    "message": "validation failed. errors: An instance of Board has failed the validation:\n - property title has failed the following constraints: isLength \n",
    "error": "Bad Request",
    "statusCode": 400
}

 

예시 2 : Dto 에 제약을 설정

....

export CreatePostRequestDto{
  @Length(10, 20) // 추가
  title: string;
  
  text: string;
}

@Transactional()
  async create(createPostRequestDto: CreatePostRequestDto) {

    ....

 }

 

같은 입력을 주면 아래에 따로 validation을 확인 할 필요없이 에러문구가 아래와 같이 뜬다.

{
    "message": [
        "title must be longer than or equal to 5 characters"
    ],
    "error": "Bad Request",
    "statusCode": 400
}

 

 

참고자료

https://docs.nestjs.com/techniques/validation

+ Recent posts