Exception filters
Exception filters
Nestμλ μ ν리μΌμ΄μ μμ μ²λ¦¬λμ§ μμ λͺ¨λ μμΈλ₯Ό μ²λ¦¬νλ λ΄μ₯ μμΈ λ μ΄μ΄κ° μ 곡λ©λλ€. μμ© νλ‘κ·Έλ¨ μ½λμμ μμΈλ₯Ό μ²λ¦¬νμ§ μμΌλ©΄μ΄ κ³μΈ΅μμ μμΈλ₯Ό ν¬μ°©νμ¬ μ μ ν μ¬μ©μ μΉνμ μΈ μλ΅μ μλμΌλ‘ 보λ λλ€.

κΈ°λ³Έμ μΌλ‘ μ΄ λμμ λ΄μ₯ λ μ μ μμΈ νν°μ μν΄ μνλλλ°, μ΄ μμΈλ HttpException
μ ν (λ° κ·Έ νμ ν΄λμ€)μ μμΈλ₯Ό μ²λ¦¬ν©λλ€. μμΈκ° unrecognized μΈ κ²½μ° ( HttpException
λλ HttpException
μμ μμλλ ν΄λμ€κ° μλ) ν΄λΌμ΄μΈνΈλ λ€μκ³Ό κ°μ κΈ°λ³Έ JSON μλ΅μλ°μ΅λλ€.
{
"statusCode": 500,
"message": "Internal server error"
}
Base exceptions
λ΄μ₯ λ HttpException
ν΄λμ€λ @nestjs/common
ν¨ν€μ§μμ 곡κ°λ©λλ€.
CatsController
μλ findAll()
λ©μλ (GET
κ²½λ‘ νΈλ€λ¬)κ° μμ΅λλ€. μ΄λ€ μ΄μ λ‘ μ΄ λΌμ°νΈ νΈλ€λ¬μμ μμΈκ° λ°μνλ€κ³ κ°μ ν΄ λ΄
μλ€. μ΄λ₯Ό μ€λͺ
νκΈ° μν΄ λ€μκ³Ό κ°μ΄ νλ μ½λ©ν©λλ€.
@@filename(cats.controller)
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
info ννΈ μ¬κΈ°μλ
HttpStatus
λ₯Ό μ¬μ©νμ΅λλ€. μ΄κ²μ@nestjs/common
ν¨ν€μ§μμ κ°μ Έμ¨ ν¬νΌ μ΄κ±°νμ λλ€.
ν΄λΌμ΄μΈνΈκ° μ΄ μλ ν¬μΈνΈλ₯Ό νΈμΆνλ©΄ μλ΅μ λ€μκ³Ό κ°μ΅λλ€.
{
"statusCode": 403,
"message": "Forbidden"
}
HttpException
μμ±μλ μλ΅μ κ²°μ νλ λ κ°μ§ νμ μΈμλ₯Ό μ¬μ©ν©λλ€.
response
μΈμλ JSON μλ΅ λ³Έλ¬Έμ μ μν©λλ€. μλμ μ€λͺ λλλ‘λ¬Έμμ΄
λλκ°μ²΄
μΌ μ μμ΅λλ€.status
μΈμλ HTTP μν μ½λλ₯Ό μ μν©λλ€.
κΈ°λ³Έμ μΌλ‘ JSON μλ΅ λ³Έλ¬Έμλ λ κ°μ§ μμ±μ΄ μμ΅λλ€.
statusCode
:status
μΈμμ μ 곡λ HTTP μν μ½λλ‘ κΈ°λ³Έ μ€μ message
:μν
μ κΈ°λ°ν HTTP μλ¬μ λν κ°λ¨ν μ€λͺ
JSON μλ΅ λ³Έλ¬Έμ λ©μμ§ λΆλΆ λ§ μ¬μ μνλ €λ©΄ response
μΈμμ λ¬Έμμ΄μ μ 곡νμμμ€.
μ 체 JSON μλ΅ λ³Έλ¬Έμ μ¬μ μνλ €λ©΄ response
μΈμμ κ°μ²΄λ₯Ό μ λ¬νμμμ€. Nestλ κ°μ²΄λ₯Ό μ§λ ¬ννμ¬ JSON μλ΅ λ³Έλ¬ΈμΌλ‘ λ°νν©λλ€.
λ λ²μ§Έ μμ±μ μΈμ status
λ μ ν¨ν HTTP μν μ½λ μ¬μΌν©λλ€. λͺ¨λ² μ¬λ‘λ@nestjs/common
μμ κ°μ Έμ¨ HttpStatus
μ΄κ±° νμ μ¬μ©νλ κ²μ
λλ€.
λ€μμ μ 체 μλ΅ λ³Έλ¬Έμ μ¬μ μ νλ μμ λλ€.
@@filename(cats.controller)
@Get()
async findAll() {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, 403);
}
μμ λ°©λ²μ μ¬μ©νλ©΄ λ€μκ³Ό κ°μ΄ μλ΅μ΄ λνλ©λλ€.
{
"status": 403,
"error": "This is a custom message"
}
Exceptions hierarchy
μμΈ κ³μΈ΅ ꡬ쑰λ₯Ό μ§μ λ§λλ κ²μ΄ μ’μ΅λλ€. μ΄κ²μ μ¬μ©μ μ μ HTTP μμΈκ° κΈ°λ³Έ HttpException
ν΄λμ€μμ μμλμ΄μΌ ν¨μ μλ―Έν©λλ€. κ²°κ³Όμ μΌλ‘ Nestλ μμΈλ₯Ό μΈμνκ³ μλμΌλ‘ μ€λ₯ μλ΅μ μ²λ¦¬ν©λλ€. κ·Έλ¬ν 컀μ€ν
μμΈλ₯Ό ꡬνν΄ λ΄
μλ€ :
@@filename(forbidden.exception)
export class ForbiddenException extends HttpException {
constructor() {
super('Forbidden', HttpStatus.FORBIDDEN);
}
}
ForbiddenException
μ κΈ°λ³Έ HttpException
μ νμ₯νλ―λ‘ λ΄μ₯ μμΈ νΈλ€λ¬μ μλ²½νκ² μλνλ―λ‘ findAll()
λ©μλ λ΄μμ μ¬μ©ν μ μμ΅λλ€.
@@filename(cats.controller)
@Get()
async findAll() {
throw new ForbiddenException();
}
HTTP exceptions
μμ©κ΅¬ μ½λλ₯Ό μμ±ν νμμ±μ μ€μ΄κΈ° μν΄ Nestλ μ½μ΄ HttpException
μμ μμλλ μ¬μ© κ°λ₯ν μμΈ μΈνΈλ₯Ό μ 곡ν©λλ€. μ΄λ€ λͺ¨λλ @nestjs/common
ν¨ν€μ§μμ 곡κ°λ©λλ€:
BadRequestException
UnauthorizedException
NotFoundException
ForbiddenException
NotAcceptableException
RequestTimeoutException
ConflictException
GoneException
PayloadTooLargeException
UnsupportedMediaTypeException
UnprocessableEntityException
InternalServerErrorException
NotImplementedException
BadGatewayException
ServiceUnavailableException
GatewayTimeoutException
Exception filters
κΈ°λ³Έ (λ΄μ₯) μμΈ νν°κ° λ§μ κ²½μ°λ₯Ό μλμΌλ‘ μ²λ¦¬ ν μ μμ§λ§ μμΈ λ μ΄μ΄λ₯Ό μμ ν μ μ΄ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, μΌλΆ λμ μμΈμ λ°λΌ λ‘κΉ μ μΆκ°νκ±°λ λ€λ₯Έ JSON μ€ν€λ§λ₯Ό μ¬μ©ν μ μμ΅λλ€. μμΈ νν°λ μ΄ λͺ©μ μ μν΄ μ€κ³λμμ΅λλ€.
HttpException
ν΄λμ€μ μΈμ€ν΄μ€μΈ μμΈλ₯Ό ν¬μ°©νκ³ μ΄μ λν μ¬μ©μ μ μ μλ΅ λ‘μ§μ ꡬννλ μμΈ νν°λ₯Ό μμ±ν΄ λ΄
μλ€.
@@filename(http-exception.filter)
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,
});
}
}
@@switch
import { Catch, HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter {
catch(exception, host) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
info ννΈ λͺ¨λ μμΈ νν°λ μΌλ°
ExceptionFilter <T>
μΈν°νμ΄μ€λ₯Ό ꡬνν΄μΌ ν©λλ€. μ΄λ₯Ό μν΄μλcatch (exception: T, host: ArgumentsHost)
λ©μλμ νμλ μλͺ μ μ 곡ν΄μΌ ν©λλ€.T
λ μμΈ μ νμ λνλ λλ€.
@Catch (HttpException)
λ°μ½λ μ΄ν°λ νμν λ©ν λ°μ΄ν°λ₯Ό μμΈ νν°μ λ°μΈλ©νμ¬ μ΄ νΉμ νν°κ° HttpException
μ νμ μμΈλ₯Ό μ°Ύκ³ μμμ Nestμκ² μλ €μ€λλ€. @Catch()
λ°μ½λ μ΄ν°λ λ¨μΌ λ§€κ° λ³μ λλ μΌνλ‘ κ΅¬λΆλ λͺ©λ‘μ μ¬μ©ν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μ¬λ¬ μ νμ μμΈμ λν νν°λ₯Ό ν λ²μ μ€μ ν μ μμ΅λλ€.
Arguments host
catch()
λ©μλμ λ§€κ° λ³μλ₯Ό 보μ. exception
λ§€κ° λ³μλ νμ¬ μ²λ¦¬μ€μΈ μμΈ κ°μ²΄μ
λλ€. host
λ§€κ° λ³μλ ArgumentsHost
κ°μ²΄μ
λλ€. ArgumentsHost
λ original μμ² νΈλ€λ¬ (μμΈκ° λ°μν κ³³)μ μ λ¬λ μΈμλ₯Ό κ°μΈλ λνΌμ
λλ€. μ¬κΈ°μλ μμ© νλ‘κ·Έλ¨ λ° μ¬μ©μ€μΈ νλ«νΌμ μ νμ λ°λΌ νΉμ μΈμ λ°°μ΄μ΄ ν¬ν¨λ©λλ€. λ€μμ ArgumentsHost
μ λͺ¨μ΅μ
λλ€ :
export interface ArgumentsHost {
getArgs<T extends Array<any> = any[]>(): T;
getArgByIndex<T = any>(index: number): T;
switchToRpc(): RpcArgumentsHost;
switchToHttp(): HttpArgumentsHost;
switchToWs(): WsArgumentsHost;
}
ArgumentsHost
λ λ€μν μ ν리μΌμ΄μ
컨ν
μ€νΈμμ κΈ°λ³Έ λ°°μ΄μμ μ¬λ°λ₯Έ μΈμλ₯Ό μ ννλ λ° λμμ΄ λλ νΈλ¦¬ν λ©μλ μΈνΈλ₯Ό μ 곡ν©λλ€. λ€μ λ§ν΄, ArgumentsHost
λ μΈμ λ°°μ΄μ μ§λμ§ μμ΅λλ€. μλ₯Ό λ€μ΄, HTTP μ ν리μΌμ΄μ
컨ν
μ€νΈ λ΄μμ νν°λ₯Ό μ¬μ©νλ©΄ ArgumentsHost
μ [request, response]
λ°°μ΄μ΄ ν¬ν¨λ©λλ€. κ·Έλ¬λ νμ¬ μ»¨ν
μ€νΈκ° μΉ μμΌ μ ν리μΌμ΄μ
μΈ κ²½μ° ν΄λΉ 컨ν
μ€νΈμ μ ν©ν [client, data]
λ°°μ΄μ΄ ν¬ν¨λ©λλ€. μ΄ λ°©λ²μ μ¬μ©νλ©΄ μ¬μ©μ μ μ catch()
λ©μλμμ μλ νΈλ€λ¬λ‘ μ λ¬λλ μΈμμ μ‘μΈμ€ ν μ μμ΅λλ€.
Binding filters
μλ‘μ΄ HttpExceptionFilter
λ₯Ό CatsController
μ create()
λ©μλμ λ¬Άμ΄ λ΄
μλ€.
@@filename(cats.controller)
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
@@switch
@Post()
@UseFilters(new HttpExceptionFilter())
@Bind(Body())
async create(createCatDto) {
throw new ForbiddenException();
}
info ννΈ
@UseFilters()
λ°μ½λ μ΄ν°λ@nestjs/common
ν¨ν€μ§μμ κ°μ Έμ΅λλ€.
μ°λ¦¬λ@UseFilters()
λ°μ½λ μ΄ν°λ₯Ό μ¬μ©νμ΅λλ€. @Catch()
λ°μ½λ μ΄ν°μ μ μ¬νκ² λ¨μΌ νν° μΈμ€ν΄μ€ λλ μΌνλ‘ κ΅¬λΆλ νν° μΈμ€ν΄μ€ λͺ©λ‘μ μ¬μ©ν μ μμ΅λλ€. μ¬κΈ°μλ HttpExceptionFilter
μΈμ€ν΄μ€λ₯Ό μμ±νμ΅λλ€. λλ μΈμ€ν΄μ€ λμ ν΄λμ€λ₯Ό μ λ¬νμ¬ νλ μ μν¬μ μΈμ€ν΄μ€νμ λν μ±
μμ λ¨κΈ°κ³ μ’
μ μ£Όμ
μ νμ±ν ν μ μμ΅λλ€.
@@filename(cats.controller)
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
@@switch
@Post()
@UseFilters(HttpExceptionFilter)
@Bind(Body())
async create(createCatDto) {
throw new ForbiddenException();
}
info ννΈ κ°λ₯ν κ²½μ° μΈμ€ν΄μ€ λμ ν΄λμ€λ₯Ό μ¬μ©νμ¬ νν°λ₯Ό μ μ©νλ κ²μ΄ μ’μ΅λλ€. Nestκ° μ 체 λͺ¨λμμ λμΌν ν΄λμ€μ μΈμ€ν΄μ€λ₯Ό μ½κ² μ¬μ¬μ©ν μ μμΌλ―λ‘ λ©λͺ¨λ¦¬ μ¬μ©λμ΄ μ€μ΄ λλλ€.
μμ μμμ HttpExceptionFilter
λ λ¨μΌ create()
λΌμ°νΈ νΈλ€λ¬μλ§ μ μ©λμ΄ λ©μλ λ²μκ° λ©λλ€. μμΈ νν°λ λ©μλ λ²μ, 컨νΈλ‘€λ¬ λ²μ λλ μ μ λ²μμ λ€λ₯Έ μμ€μΌλ‘ λ²μλ₯Ό μ§μ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, νν°λ₯Ό 컨νΈλ‘€λ¬ λ²μλ‘ μ€μ νλ €λ©΄ λ€μμ μννμμμ€.
@@filename(cats.controller)
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
μ΄ κ΅¬μ‘°λ CatsController
μμ μ μλ λͺ¨λ κ²½λ‘ νΈλ€λ¬μ λν΄ HttpExceptionFilter
λ₯Ό μ€μ ν©λλ€.
μ μ λ²μ νν°λ₯Ό λ§λ€λ €λ©΄ λ€μμ μννμμμ€.
@@filename(main)
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
warning κ²½κ³
useGlobalFilters ()
λ©μλλ κ²μ΄νΈμ¨μ΄ λλ νμ΄λΈλ¦¬λ μ ν리μΌμ΄μ μ λν νν°λ₯Ό μ€μ νμ§ μμ΅λλ€.
μ μ λ²μ νν°λ λͺ¨λ 컨νΈλ‘€λ¬μ λͺ¨λ κ²½λ‘ μ²λ¦¬κΈ°μ λν΄ μ 체 μμ© νλ‘κ·Έλ¨μμ μ¬μ©λ©λλ€. μμ‘΄μ± μ£Όμ
μ κ΄μ μμ, λͺ¨λ μΈλΆμμ λ±λ‘ λ μ μ νν° (μμ μμμμ κ°μ΄useGlobalFilters()
λ‘)λ λͺ¨λμ 컨ν
μ€νΈ μΈλΆμμ μνλκΈ° λλ¬Έμ μμ‘΄μ±μ μ£Όμ
ν μ μμ΅λλ€. μ΄ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ λ€μ ꡬμ±μ μ¬μ©νμ¬ λͺ¨λ λͺ¨λμμ μ§μ μ μ νν°λ₯Ό λ±λ‘ ν μ μμ΅λλ€.
@@filename(app.module)
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
info ννΈ μ΄ μ κ·Ό λ°©μμ μ¬μ©νμ¬ νν°μ λν μμ‘΄μ± μ£Όμ μ μνν λ μ΄ κ΅¬μ±μ΄ μ¬μ©λλ λͺ¨λμ κ΄κ³μμ΄ νν°λ μ€μ λ‘ μ μμ μ λλ€. μ΄λμμ ν΄μΌ ν©λκΉ? νν° (μ μμμ
HttpExceptionFilter
)κ° μ μ λ λͺ¨λμ μ ννμμμ€. λν 컀μ€ν νλ‘ λ°μ΄λ λ±λ‘μ λ€λ£¨λ μ μΌν λ°©λ²μuseClass
κ° μλλλ€. μ¬κΈ°μ λν΄ μμΈν μμ보μμμ€.
μ΄ κΈ°μ μ μ¬μ©νμ¬ νμνλ§νΌ νν°λ₯Ό μΆκ° ν μ μμ΅λλ€. κ°λ¨ν 곡κΈμ λ°°μ΄μ κ°κ°μ μΆκ°νμμμ€.
Catch everything
μ²λ¦¬λμ§ μμ μμΈ (μμΈ μ νμ κ΄κ³μμ΄)λ₯Ό λͺ¨λ μ‘μΌλ €λ©΄ @Catch()
λ°μ½λ μ΄ν°μ λ§€κ° λ³μ λͺ©λ‘μ λΉμ λμμμ€ (μ: @Catch()
).
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
μμ μμμ νν°λ μ ν (ν΄λμ€)μ κ΄κ³μμ΄ λ°μ λ κ° μμΈλ₯Ό ν¬μ°©ν©λλ€.
Inheritance
μΌλ°μ μΌλ‘ μμ© νλ‘κ·Έλ¨ μꡬ μ¬νμ μΆ©μ‘±νλλ‘ μ μλ μμ ν μ¬μ©μ μ§μ λ μμΈ νν°λ₯Ό λ§λλλ€. κ·Έλ¬λ λ΄μ₯λ κΈ°λ³Έ μ μ μμΈ νν°λ₯Ό λ¨μν νμ₯νκ³ νΉμ μμΈμ λ°λΌ λμμ μ¬ μ μνλ €λ κ²½μ° μ¬μ© μ¬λ‘κ° μμ μ μμ΅λλ€.
μμΈ μ²λ¦¬λ₯Ό κΈ°λ³Έ νν°μ μμνλ €λ©΄ BaseExceptionFilter
λ₯Ό νμ₯νκ³ μμ λ catch()
λ©μλλ₯Ό νΈμΆν΄μΌν©λλ€.
@@filename(all-exceptions.filter)
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);
}
}
@@switch
import { Catch } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
@Catch()
export class AllExceptionsFilter extends BaseExceptionFilter {
catch(exception, host) {
super.catch(exception, host);
}
}
warning κ²½κ³
BaseExceptionFilter
λ₯Ό νμ₯νλ λ©μλ λ²μ λ° μ»¨νΈλ‘€λ¬ λ²μ νν°λnew
λ‘ μΈμ€ν΄μ€ννλ©΄ μλ©λλ€. λμ νλ μ μν¬μμ μλμΌλ‘ μΈμ€ν΄μ€ννμμμ€.
μμ ꡬνμ μ κ·Ό λ°©μμ 보μ¬μ£Όλ μμΌλΏμ λλ€. νμ₯ μμΈ νν° κ΅¬νμλ λ§μΆ€ν λΉμ¦λμ€ λ Όλ¦¬ (μ: λ€μν 쑰건 μ²λ¦¬)κ° ν¬ν¨λ©λλ€.
μ μ νν° λ κΈ°λ³Έ νν°λ₯Ό νμ₯ ν μ μμ΅λλ€. μ΄κ²μ λ κ°μ§ λ°©λ² μ€ νλλ‘ μν ν μ μμ΅λλ€.
첫 λ²μ§Έ λ°©λ²μ 컀μ€ν
κΈλ‘λ² νν°λ₯Ό μΈμ€ν΄μ€ν ν λ HttpServer
μ°Έμ‘°λ₯Ό μ½μ
νλ κ²μ
λλ€.
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
ν ν°μ μ¬μ©νλ κ²μ
λλ€.
Last updated
Was this helpful?