내장된 예외 처리 레이어가 존재
HttpException
을 내장된 전역 예외 처리 필터에서 처리됨
unrecognized
로 해당 응답 반환{
"statusCode": 500,
"message": "Internal server error"
}
전역 예외 필터는
http-errors
라이브러리를 부분적으로 지원합니다.statusCode
와message
를 포함한 에러는 적절하게 합쳐지고 응답(확인되지 않은InternalServerErrorException
예외 대신에)으로써 반환됩니다
내장된 @nestjs/common
패키지에서 비롯된 HttpException
클래스를 제공
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
@nestjs/common
패키지에서 비롯된 helper enum 입니다.
{
"statusCode": 403,
"message": "Forbidden"
}
HttpException
생성자는 response 와 status 라는 두 가지 인자를 받음
response
은 JSON 응답 본문이며, string
혹은 object
가능statusCode
은 HTTP status code message
는 HTTP error 에 대한 짧은 설명response
에 문자열을 넘김으로써 대체 가능status
는 HTTP Status Code 를 의미@nestjs/common
의 HttpStatus
enum 을 쓰는게 제일 바람직options
속성은 응답 객체에 들어가진 않으나, 로깅에 매우 유용@Get()
async findAll() {
try {
await this.service.findAll()
} catch (error) {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, HttpStatus.FORBIDDEN, {
cause: error
});
}
}
{
"status": 403,
"error": "This is a custom message"
}
HttpException
을 상속하는 개인화된 예외 계층 만들기
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
@Get()
async findAll() {
throw new ForbiddenException();
}
일반적인 예외 처리와 동일하게 적용
HttpException
으로부터 상속받고, @nestjs/common
패키지에 비롯
예외 계층에 대한 완전한 컨트롤
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,
});
}
}
모든 예외 필터는 일반
ExceptionFilter<T>
인터페이스를 구현해야 합니다. 이를 위해서는catch(exception: T, host: ArgumentsHost)
함수에 표시된 서명을 제공해야 합니다 .T
는 예외 유형을 나타냅니다.
@nestjs/platform-fastify
를 사용하는 경우response.json()
대신에response.send()
를 사용할 수 있습니다.fastify
를 위해 정확한 타입을 가져오는 걸 명심하세요
@Catch(HttpException)
데코레이터는 HttpException
에 해당하는 특정 예외만 본다는 얘기
catch()
함수의 인자
exception
은 현재 진행 중인 예외 객체host
는 ArgumentsHost
객체Nest 모든 context 에 있기 때문에 추상화 수준 필요했음
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
@UseFilters()
데코레이터는@nestjs/common
패키지에 있습니다.
가능할 경우 인스턴스보다 클래스를 통해 필터를 적용허세요. 동일한 클래스의 인스턴스를 재사용할 수 있어
메모리 사용량
이 줄어듭니다.
함수 레벨, 컨트롤러 레벨, 전역 레벨 에서 스코핑 가능
// 컨트롤러 레벨
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
// 전역 레벨
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
useGlobalFilters()
함수는 게이트웨이 나 하이브리드 앱에는 적용되지 않습니다.
useGlobalFilters()
를 사용하는 경우 DI 를 적용할 수 없음
// DI 문제를 해결한 방식
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
DI 문제를 해결한 방식은 모듈에 상관없이 필터는 전역적입니다. 필터가 적용된 모듈을 선택하세요. 그리고,
useClass
만이 커스텀 provider 를 등록하는 방법은 아닙니다. 여기
@Catch()
에 변수를 주지 않으면 됌import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: unknown, host: ArgumentsHost): void {
// In certain situations `httpAdapter` might not be available in the
// constructor method, thus we should resolve it here.
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const httpStatus =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const responseBody = {
statusCode: httpStatus,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(ctx.getRequest()),
};
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
}
}
모든 것을 포착하는 예외 필터와 특정 유형에 바인딩된 필터를 결합할 때 특정 필터가 바인딩된 유형을 올바르게 처리할 수 있도록 "Catch everything" 필터를 먼저 선언해야 합니다.
내장된 전역 예외 필터 를 확장해 재정의 하고 싶은 경우
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
super.catch(exception, host);
}
}
BaseExceptionFilter
를 확장하는 함수-레벨, 컨트롤러-레벨 필터는 new 로 인스턴스화 하면 안됩니다. 대신에, 프레임워크가 자동으로 인스턴스화 하게 하세요.
전역 필터는 기본 필터를 확장할 수 있음
1. HttpAdapter
주입
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
await app.listen(3000);
}
bootstrap();
APP_FILTER