Nestjs를 배워보자 6일차 - Pipe, 예외처리 (John ahn님 강의)

3

Nestjs

목록 보기
6/9

Nest 파이프와 예외처리

본 강의는 'john ahn'님의 강의를 정리한 내용입니다.
https://www.youtube.com/watch?v=3JminDpCJNE

1. Nestjs Pipes

파이프란 무엇인가? @Injectable()데코레이터로 주석이 달린 클래스이다.

또한, 파이프는 컨트롤러 경로 처리기에 의해 처리되는 인수에 대해서 작동하는데 data transformationdata validation를 위해서 사용된다.

다음과 같이 URL 요청이 왔을 때, 먼저 해당 URL에 대한 데이터들을 처리해주는 것이다. 만약 문제가 생기면 통과하지 못하고 Error 처리되며, 통과한다면 데이터를 처리해준 채로 handler에게 가게 된다.

  • Data Transformation
    입력 데이터를 원하는 형식으로 변환하는 것을 말한다. 가령 문자열에서 정수로 바꾸는 것을 의미한다.

  • Data Validation
    유효성 체크로서, 입력 데이터를 평가하고 유효한 경우 변경되지 않은 상태로 전달된다. 그렇지 않으면 데이터가 올바르지 않을 때 예외를 발생시킨다.

만약 이름의 길이가 10자 이하여야 하는데 10자 이상이 되면 에러를 발생시킨다.

2. PIPE 사용하는 방법(Binding Pipes)

Handler-level Pipes, Parameter-level Pipes, Global-level Pipes 3가지로 나뉜다.

  1. Handler-level Pipes
    route handler 하나에만 @UsePipes(pipe) 데코레이터를 남겨 파이프를 사용한다.
@Post()
@UsePipes(pipe)
createBoard(
    @Body() createBoardDto : CreateBoardDto 
) : Board {
    return this.boardService.createBoard(createBoardDto);  
}

이런식으로 사용한다.

  1. Parameter-level Pipes
    handler 하나 전체에 작용하는 것이 아니라, 파라미터 하나에만 작용하는 것이 파라미터 레벨 파이프이다. 파라미터 레벨이기 때문에 특정한 파라미터에게만 적용이 된다.
@Patch('/:id/status')
updateBoardStatus(
    @Param('id') id : string,
    @Body('status', pipe) status : BoardStatus
){
    return this.boardService.updateBoardStatus(id, status);
}

다음과 같이 status라는 파라미터에 @Body('status', pipe)가 적용된 것을 볼 수 있다. 파라미터 레벨로 작동시키고 싶다면 @Body()데코레이터의 두번째 인자로 파이프를 넘겨주면 되는 것이다.

  1. Global-level Pipes
    글로벌 파이프로서 어플리케이션 레벨의 파이프이다. 클라이언트에서 들어오는 모든 요청에 적용이 되어, mian.tsappuseGlobalPipes()을 적용시켜주면 된다.
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(globalPipe)
  await app.listen(3000);
}
bootstrap();

다음과 같이 사용할 수 있다.

2.1 Built-in Pipes

Nestjs의 pipe는 빌트인과, 커스텀이 있는데, 우리가 직접 만드는 파이프가 커스텀 파이프이고 미리 만들어진 것이 빌트인 파이프이다.

  • Built-in pipes은 다음과 같다.
  1. ValidationPipe
  2. ParseIntPipe
  3. ParseFloatPipe
  4. ParseBoolPipe
  5. ParseArrayPipe
  6. ParseUUIDPipe
  7. ParseEnumPipe
  8. DefaultValuePipe

하나만 보자면, ParseIntPipe를 사용해보면

@Get(':id')
findOne(@Param('id', ParseIntPipe) id : number){
    return;
}

다음의 예제에서 localhost:3000/boards/abc와 같이 id를 abc와 같은 값으로 보내면 파이프에서 먼저 처리하여number 타입이 아니기 때문에 에러를 발생시킨다.

3. Pipe를 이용한 유효성 체크

게시물을 생성할 때, 파이프를 이용하여 유효성 체크를 해보도록 하자

  • 필요한 모듈들이 있다.
    class-validator, class-transformer 를 설치해보자

다음의 명령어를 입력해주자

npm install class-validator class-transformer --save

https://github.com/typestack/class-validator
https://github.com/typestack/class-transformer

사용법은 해당 docs를 보면 된다.

먼저 class-validator를 이용하여 url로 들어오는 파라미터를 검증해보자, 사용 방법은 매우 단순한데, docs에 있는 데코레이터들을 클래스의 프로퍼티 위에다 적어주면 된다.

createBoard(
    @Body() createBoardDto : CreateBoardDto 
) : Board {
    return this.boardService.createBoard(createBoardDto);  
}

그래서 우리는 controller에서 createBoard의 CreateBoardDto를 손보도록 해보자

boards/dto/create-board.dto.ts

import { IsNotEmpty } from 'class-validator'

export class CreateBoardDto{
    @IsNotEmpty()
    title : string;

    @IsNotEmpty()
    description : string;
}

다음과 같이 url로 보내지는 데이터에 title, description이 없으면 안된다고 IsNotEmpty() 데코레이터를 이용하여 표시해두도록 하자

이는 data validation 기능을 표시해둔 것일 뿐이며, 파이프가 적용된 것은 아니다. 파이프를 적용하기 위해서는 위에서 소개한 것처럼, 핸들러 레벨, 파라미터 레벨, 글로벌 레벨에서 파이프를 사용하는 데코레이터를 사용해야 data validation이 적용되는 것이다.

다시 controller 부분으로 가서, createBoard 메서드에 Pipe를 적용해보도록 하자

@Post()
@UsePipes(ValidationPipe)
createBoard(
    @Body() createBoardDto : CreateBoardDto 
) : Board {
    return this.boardService.createBoard(createBoardDto);  
}

@UsePipes(ValidationPipe)을 적용하려는 핸들러에 적어주면 된다. 참고로 ValidationPipe는 이미 있는 빌트인 파이프이기 때문에, 자동적으로 nest에서 import해준다.

이제 잘 적용되는 지 확인해보도록 하자

post만에 다음과 같이 post 요청을 보내보자

{
    "statusCode": 400,
    "message": [
        "title should not be empty",
        "description should not be empty"
    ],
    "error": "Bad Request"
}

이와 같이 제대로된 응답이 온 것을 확인할 수 있다.

4. 예외처리

@nestjs/common 안에는 예외처리에 대한 Exception들이 정의되어있다. 이를 이용해서 예외를 처리해보자

service의 getBoardById() 메서드에서 만약 원하는 특정 게시물이 없다면 예외처리를 해주도록 하자

getBoardById(id: string) : Board {
    const found = this.boards.find((board) => board.id === id);
    if(!found){
        throw new NotFoundException("can't find any id : " + id);
    }
    return found;
}

postman으로 http://localhost:3000/boards/eqweqw에 GET요청을 하면

{
    "statusCode": 404,
    "message": "can't find any id : eqweqw",
    "error": "Not Found"
}

다음과 같은 결과가 나올 것이다.

이번에는 없는 게시물을 지우려고 할 때를 처리해주도록 하자, 이와 같은 경우는 getBoardById()를 먼저 호출하고 delete해서 처리해주면 된다.

deleteBoard(id : string){
    const found = this.getBoardById(id);
    this.boards = this.boards.filter( (board) => board.id !== found.id)
}

getBoardById() 메서드를 호출하고, 해당 found에서 board 객체가 있다면 id가 같을 것이고 없다면 id값이 틀릴 것이다.

이렇게 삭제할 객체가 있는지 없는 지 확인할 수 있다.

0개의 댓글