# Exception filters

## Exception filters

Nest에는 애플리케이션에서 처리되지 않은 모든 예외를 처리하는 내장 **예외 레이어**가 제공됩니다. 응용 프로그램 코드에서 예외를 처리하지 않으면이 계층에서 예외를 포착하여 적절한 사용자 친화적인 응답을 자동으로 보냅니다.

![](https://docs.nestjs.com/assets/Filter_1.png)

기본적으로 이 동작은 내장 된 **전역 예외 필터**에 의해 수행되는데, 이 예외는 `HttpException` 유형 (및 그 하위 클래스)의 예외를 처리합니다. 예외가 **unrecognized** 인 경우 ( `HttpException` 또는 `HttpException`에서 상속되는 클래스가 아님) 클라이언트는 다음과 같은 기본 JSON 응답을받습니다.

```javascript
{
  "statusCode": 500,
  "message": "Internal server error"
}
```

## Base exceptions

내장 된 `HttpException`클래스는 `@nestjs/common` 패키지에서 공개됩니다.

`CatsController`에는 `findAll()` 메소드 (`GET` 경로 핸들러)가 있습니다. 어떤 이유로 이 라우트 핸들러에서 예외가 발생한다고 가정해 봅시다. 이를 설명하기 위해 다음과 같이 하드 코딩합니다.

```typescript
@@filename(cats.controller)
@Get()
async findAll() {
  throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
```

> info **힌트** 여기서는 `HttpStatus`를 사용했습니다. 이것은 `@nestjs/common` 패키지에서 가져온 헬퍼 열거형입니다.

클라이언트가 이 엔드 포인트를 호출하면 응답은 다음과 같습니다.

```javascript
{
  "statusCode": 403,
  "message": "Forbidden"
}
```

`HttpException` 생성자는 응답을 결정하는 두 가지 필수 인수를 사용합니다.

* `response` 인수는 JSON 응답 본문을 정의합니다. 아래에 설명 된대로 `문자열` 또는 `객체`일 수 있습니다.
* `status` 인수는 [HTTP 상태 코드](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)를 정의합니다.

기본적으로 JSON 응답 본문에는 두 가지 속성이 있습니다.

* `statusCode`: `status` 인수에 제공된 HTTP 상태 코드로 기본 설정
* `message`: `상태`에 기반한 HTTP 에러에 대한 간단한 설명

JSON 응답 본문의 메시지 부분 만 재정의하려면 `response` 인수에 문자열을 제공하십시오.

전체 JSON 응답 본문을 재정의하려면 `response` 인수에 객체를 전달하십시오. Nest는 객체를 직렬화하여 JSON 응답 본문으로 반환합니다.

두 번째 생성자 인자 `status`는 유효한 HTTP 상태 코드 여야합니다. 모범 사례는`@nestjs/common`에서 가져온 `HttpStatus` 열거 형을 사용하는 것입니다.

다음은 전체 응답 본문을 재정의 하는 예입니다.

```typescript
@@filename(cats.controller)
@Get()
async findAll() {
  throw new HttpException({
    status: HttpStatus.FORBIDDEN,
    error: 'This is a custom message',
  }, 403);
}
```

위의 방법을 사용하면 다음과 같이 응답이 나타납니다.

```javascript
{
  "status": 403,
  "error": "This is a custom message"
}
```

## Exceptions hierarchy

**예외 계층 구조**를 직접 만드는 것이 좋습니다. 이것은 사용자 정의 HTTP 예외가 기본 `HttpException` 클래스에서 상속되어야 함을 의미합니다. 결과적으로 Nest는 예외를 인식하고 자동으로 오류 응답을 처리합니다. 그러한 커스텀 예외를 구현해 봅시다 :

```typescript
@@filename(forbidden.exception)
export class ForbiddenException extends HttpException {
  constructor() {
    super('Forbidden', HttpStatus.FORBIDDEN);
  }
}
```

`ForbiddenException`은 기본 `HttpException`을 확장하므로 내장 예외 핸들러와 완벽하게 작동하므로 `findAll()` 메소드 내에서 사용할 수 있습니다.

```typescript
@@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`클래스의 인스턴스인 예외를 포착하고 이에 대한 사용자 정의 응답 로직을 구현하는 예외 필터를 작성해 봅시다.

```typescript
@@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`의 모습입니다 :

```typescript
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()`메소드에 묶어 봅시다.

```typescript
@@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`인스턴스를 작성했습니다. 또는 인스턴스 대신 클래스를 전달하여 프레임 워크에 인스턴스화에 대한 책임을 남기고 **종속 주입**을 활성화 할 수 있습니다.

```typescript
@@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()`라우트 핸들러에만 적용되어 메소드 범위가 됩니다. 예외 필터는 메소드 범위, 컨트롤러 범위 또는 전역 범위의 다른 수준으로 범위를 지정할 수 있습니다. 예를 들어, 필터를 컨트롤러 범위로 설정하려면 다음을 수행하십시오.

```typescript
@@filename(cats.controller)
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
```

이 구조는 `CatsController` 안에 정의된 모든 경로 핸들러에 대해 `HttpExceptionFilter`를 설정합니다.

전역 범위 필터를 만들려면 다음을 수행하십시오.

```typescript
@@filename(main)
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();
```

> warning **경고** `useGlobalFilters ()` 메소드는 게이트웨이 또는 하이브리드 애플리케이션에 대한 필터를 설정하지 않습니다.

전역 범위 필터는 모든 컨트롤러와 모든 경로 처리기에 대해 전체 응용 프로그램에서 사용됩니다. 의존성 주입의 관점에서, 모듈 외부에서 등록 된 전역 필터 (위의 예에서와 같이`useGlobalFilters()`로)는 모듈의 컨텍스트 외부에서 수행되기 때문에 의존성을 주입할 수 없습니다. 이 문제를 해결하기 위해 다음 구성을 사용하여 **모든 모듈에서 직접** 전역 필터를 등록 할 수 있습니다.

```typescript
@@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`가 아닙니다. [여기](https://app.gitbook.com/fundamentals/custom-providers)에 대해 자세히 알아보십시오.

이 기술을 사용하여 필요한만큼 필터를 추가 할 수 있습니다. 간단히 공급자 배열에 각각을 추가하십시오.

## Catch everything

처리되지 않은 예외 (예외 유형에 관계없이)를 모두 잡으려면 `@Catch()`데코레이터의 매개 변수 목록을 비워 두십시오 (예: `@Catch()`).

```typescript
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()`메소드를 호출해야합니다.

```typescript
@@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` 참조를 삽입하는 것입니다.

```typescript
async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const { httpAdapter } = app.get(HttpAdapterHost);
  app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));

  await app.listen(3000);
}
bootstrap();
```

두 번째 방법은  [여기에 표시된](https://app.gitbook.com/s/-LpOhNvIYtogx8UUAkWn/overview/exception-filters#binding-filters) `APP_FILTER`토큰을 사용하는 것입니다.
