ExceptionFilter
NestJS 예외 필터는 애플리케이션 전반에서 처리되지 않은 모든 예외를 처리하는 역할을 하는 내장된 예외 처리 계층이다.
- 애플리케이션 코드에서 예외를 잡아서 처리하지 못하면, 이 예외 필터 계층이 해당 예외를 포착하여 자동으로 사용자에게 적절한 응답을 보낸다.
기본 예외 처리
- 기본적으로
NestJS는 HttpException 타입 (및 그 하위 클래스)의 예외를 처리하는 Global Exception Filter를 내장 하고 있다.
- 만약 처리되지 않은 예외가
HttpException이 아니거나 HttpException을 상속 받지 않은 알 수 없는 예외일 경우 다음과 같은 기본 JSON 응답을 생성한다.
{
"statusCode": 500,
"message": "Internal server error"
}
표준 예외 처리 (Throwing Standard Exceptions)
- 일반적인 HTTP 기반 애플리케이션에서는 특정 오류 조건이 발생했을 때 표준 HTTP 응답 객체를 보내는 것이 가장 좋다.
NestJS는 @nestjs/common 패키지에서 HttpException 클래스를 제공하여 이를 쉽게 처리할 수 있도록 지원한다.
@Get()
async getError() {
throw new HttpException('Forbiddne', HttpStatus.FORBIDDEN);
}
{
"statusCode": 403,
"message": "Forbidden"
}
HttpException 생성자는 응답 본문과 HTTP 상태 코드를 인자로 받으며, 응답 본문 전체를 커스텀 객체로 덮어 쓸 수도 있다.
커스텀 예외 (Custom Exceptions)
- 명확한 에러 처리를 위해 자신만의 예외 계층을 만드는 것이 좋다.
Custom Exception은 NestJS의 기본 HttpException을 상속 받아 만들 수 있다.
NestJS는 BadRequestException, UnauthorizedException, NotFoundException 등 일반적인 HTTP 예외에 대한 클래스들을 내장하고 있어 편리하게 사용할 수 있다.
export class ForbiddentException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
throw new ForbiddenException();
예외 필터 제작
- 내장된 예외 필터만으로 부족할 경우, 예외 처리 계층을 제어할 수 있다.
- 로깅을 추가하거나 동적인 요소에 따라 다른 JSON 스키마를 사용하고자 할때 예외 필터를 직접 구현하여 사용할 수 있다.
- 예외 필터는
ExceptionFilter 인터페이스를 구현해야 하며, catch(exception: T, host ArgumentsHost) 메서드를 가진다.
@Catch(HttpException) 데코레이터를 사용하여 특정 타입의 예외만 처리하도록 지정할 수 있다.
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 request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode :status,
timestamp: new Date().toISOString(),
path: request.url
})
}
}
필터 바인딩 (Binding Filters)
- 제작한 예외 필터는 다양한 레벨(
Method, Controller, Global 등) 에서 적용할 수 있다.
메서드 범위
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
컨트롤러 범위
- 컨트롤러 내의 모든 라우트 핸들러에 필터를 적용
@Controller('cats')
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
전역 범위
- 애플리케이션 전체에 필터를 적용하는 것으로
main.ts 파일에서 설정
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilter(new HttpExceptionFilter())
await app.listen(3000)
}
- 전역 필터에 의존성 주입이 필요한 경우, 모듈의
providers에 APP_FILTER 토큰을 사용하여 등록 할 수 있다.
모든 예외 처리 (Catch EveryThing)
- 처리되지 않은 모든 종류의 예외를 잡으려면
@Catch() 데코레이터의 인자를 비워두면 된다.
- 이를 통해 어떤 타입의 예외든 모두 처리하는 필터를 만들 수 있다.
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
}
상속 (Inheritance)
- 기본 전역 예외 필터의 동작을 확장하고자 할떄
BaseExceptionFilter를 상속 받고 catch()메서드에서 super.catch()를 호출하여 기본 필터에 예외 처리를 위힘할 수 있다.
@Catch()
export class AllExceptionFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
super.catch(exception, host)
}
}