nestjs HttpException-filter

양진영·2023년 2월 13일
0

nestJs

목록 보기
9/10

nestjs를 해보기전에 express로 개발해 보신 분들은 error handling시에 throw new Error()라는 키워드가 매우 친숙할것이다. 내가 컨트롤 할 수 있는 범위 내에서 일어나는 에러를 가공할때 쓰여진다. nestjs에서도 비슷한 기능이 있을까? 라고 물어본다면 당연히 있다. 물론 throw new Error()를 사용할수 있겠지만 기능이 좀더 세분화 됫다고 설명하는 편이 이해하기 쉬울것같다.

nest 내부적으로 제공하는 Http-exception-filter라는 기능이 있는데 http로 통신했을때 요청을 받거나 보내는 과정에서 일어나는 일에 대하여 global한 interceptor로 잡아서 에러를 내가 원하는 포맷으로 리턴 시켜줄수도 있다. 우선 http exception filter에 대해서 설명하기 전에 process 하나 설명하고 가야할것같다. async를 걸어둔 서비스단계의 함수에서 await 없이 exception filter를 사용한다면 에러를 '삼켜버린다.' 여기서 삼켜버린다는 표현은 에러를 컨트롤러로 전달하지 않고 콘솔에만 경고로서 나타내고 끝낸다. 우리가 만들 global exception filter는 interceptor의 한 종류이다. interceptor의 의미를 살짝 복습해보자면 컨트롤러에 요청이 전달되기전 혹은 후에 동작하는 기능인데 만약 에러가 컨트롤러에 전달되지 않는다면 interceptor 또한 동작하지 않을것이다.

서론이 길었다 바로 코드 예시로 보여주겠다.

httpException-filter.ts
import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    const err = exception.getResponse() as
      | string
      | { error: string; statusCode: 400; message: string[] }; // class validator가 이런식으로 mesg를 준다.

    if (typeof err !== 'string' && err.error === 'Bad Request') {
      return response.status(status).json({
        success: false,
        code: status,
        data: err.message,
      });
    }

    response.status(status).json({
      success: false,
      code: status,
      data: err,
    });
  }
}

위의 예시에서 if문을 왜썻고 어디에쓸수있는지 간단하게만 설명하고 넘어가자면 내가 컨트롤 하는 에러는 string으로 받을것이다. 예를 들면 아래 코드에서 요청으로 받은 email을 중복체크 하는 과정에서 이미 존재하는 email이라면 ConflictExecption이라는 에러객체로 그안에 담기는 메시지는 '중복되는 이메일입니다.' 라는 스트링이다. 즉 내가 string으로 넘기는 에러일경우 처리하는 방식과 nest에서 에러임을 인지하고 넘기는 에러(객체형태의 에러)에 다르게 대응할수도 있다는 뜻이다. 물론 내가 에러처리하여 보내는 메시지를 string으로 안하고 object로 할수도있다. 다만 여기서 하고싶은 말은 에러를 어떻게 처리하느냐를 내가 원하는 포맷으로 리턴할수도 있다는 예시를 보여주고싶었다.

async create(createUserDto: CreateUserDto) {
    const { email, password, ...rest } = createUserDto;
    const user = await this.userRepository.findOneBy({ email });
    if (user)
      throw new ConflictException('중복되는 이메일입니다.');

    const hashedPassowrd = await bcrypt.hash(password, 5);

    return await this.userRepository.save({
      password: hashedPassowrd,
      email,
      ...rest,
    });
  }

이렇게 설정을 했다면 적용을 해줘야한다. http exception filter를 만들었으면 main.ts에 app.useGlobalFilters(new '이름'())으로 적용해 주어여 한다.

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { HttpExceptionFilter } from 'exception.filter';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());

 app.useGlobalFilters(new HttpExceptionFilter())**;
  const config = new DocumentBuilder()
    .setTitle('practice')
    .setDescription('be nested')
    .setVersion('1.0')
    .addTag('nest practice')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();

사실 처음엔 구지 이걸 써줘야 하나 했는데 설정하고보니 생각보다 편했다. 특히 이거랑 같이 사용 하는 class-validator라는 dto validation 체크 기능과 함께 하면 좀더 편리하다. 그래서 다음 포스팅은 class validator에 대해서 포스팅 해보겠따.

profile
왜? 라는 질문을 중요시하는 서버 개발자입니다

0개의 댓글