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?