[NestJS 공식문서 정독하기] Overview - Exception filters
2022. 7. 31. 22:21ㆍWeb/NestJS
반응형
🔗 https://docs.nestjs.com/exception-filters
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac
docs.nestjs.com
- NestJS는 처리되지 않은 exception을 처리하는
exception layer
를 내장하고 있음 - 해당 레이어는 애플리케이션 코드에서 처리되지 않은 exception을 catch해서 정제된 응답을 전송함
- 기본적으로 내장된 global exception filter가
HttpException
타입과 그 하위 타입의 예외들을 처리함 - 알 수 없는 예외의 경우(
HttpException
이 아님)에는500 Internal server error
를 디폴트로 응답함 - 기본 exception filter는 statusCode와 message가 존재하는 exception이면 정상적으로 catch하고 응답함 (
http-errors
라이브러리를 일부 지원함)
Standard exceptions
- NestJS는 기본적으로
HttpException
이라는 클래스를 제공함 - 전형적인 REST/GraphQL API 기반 애플리케이션에서는 해당 클래스를 사용하는 것을 추천함
HttpException
의 생성자는 response와 status 두 가지 파라미터를 받음- response에 string을 넣으면 응답의 message로 전달되고, object를 넣으면 response 전체가 해당 object가 됨
- status는 유효한 HTTP 상태 코드여야함 (HTTP 상태 코드 enum인
HttpStatus
를 활용하는 것이 best practice임)
Custom exceptions
- 대부분의 경우에는 만들 필요 없음 (내장 exception 목록)
- 다만 만들어야 할 경우에는 자체 위계 구조를 만들고, 모두 최상위 부모로
HttpException
을 상속 받는 구조를 추천함 - 그러면 기본 exception filter가 custom exception을 인식하고 처리할 수 있음
Exception filters
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();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
- exception layer의 동작 방식을 모두 직접 조작하고 싶으면 exception filter를 직접 구현할 수 있음
- 모든 exception filter는
ExceptionFilter<T>
인터페이스를 구현해야 함 (T는 예외의 타입) @Catch()
는 해당 exception filter에 metadata를 바인딩 해줌 (파라미터로 하나 이상의 예외 타입을 받을 수 있음)
Arguments host
- 위에서 구현하는
catch()
메소드의 파라미터 중 host는ArgumentsHost
객체임 ArugmentsHost
는 강력한 유틸리티 객체로 execution context 챕터에서 더 자세하게 다룸- 위 코드에서는 본래의 요청 핸들러 함수에 전달된
Request
와Response
객체를 가져오기 위해 사용함
Binding filters
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
- 위와 같이 Controller의 메소드, Controller 클래스, 혹은 글로벌하게 exception filter의 scope를 적용할 수 있음
@UseFilter()
데코레이터에는 인스턴스 혹은 클래스 자체를 파라미터로 전달할 수 있는데, 클래스를 전달하면 NestJS의 DI를 통해 인스턴스를 재사용할 수 있게 되기 때문에 클래스를 전달하는 것을 추천함 (메모리 사용량 감소)
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
useGlobalFilters()
를 사용해서 글로벌 scope의 exception filter를 설정하면 DI를 활용할 수 없음- 따라서, 위와 같이 모듈에 직접 등록하여 DI를 활용할 수도 있음
- 다만, 어떤 모듈에 등록을 하더라도 exception filter는 글로벌 scope로 등록됨 (모듈과 무관)
Catch everything
- 처리되지 않은 모든 예외를 catch하기 위해서는
@Catch()
파라미터로 아무것도 전달하지 않으면 됨
Inheritance
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
super.catch(exception, host);
}
}
- 내장된 global exception filter를 extend해서 사용하고 싶은 경우에는
BaseExceptionFilter
를 extend하여 사용할 수 있음
반응형