# Interceptors

## Interceptors

인터셉터는 `@Injectable()`데코레이터로 주석이 달린 클래스입니다. 인터셉터는 `NestInterceptor` 인터페이스를 구현해야 합니다.

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

&#x20;인터셉터에는 [Aspect Oriented Programming](https://en.wikipedia.org/wiki/Aspect-oriented_programming) (AOP) 기술에서 영감을 얻은 유용한 기능 세트가 있습니다. 그들은 다음을 가능하게 합니다.

* 메소드 실행 전/후에 **추가 로직** 바인딩
* 함수에서 반환 된 결과를 **변환**
* 함수에서 발생 된 예외를 **변환**&#x20;
* 기본 기능 **확장**
* 특정 조건에 따라 기능을 완전히 **재정의** (예 : 캐싱 목적)

## Basics

각 인터셉터는 두 개의 인수를받는 `intercept()`메소드를 구현합니다. 첫 번째는 `ExecutionContext`인스턴스입니다 ([guards](https://app.gitbook.com/guards)와 정확히 같은 객체). `ExecutionContext`는 `ArgumentsHost`에서 상속받습니다. 예외 필터 챕터에서 앞서 `ArgumentsHost`를 보았습니다. 여기서 우리는 원래 핸들러로 전달된 인수를 감싸는 래퍼이며 응용 프로그램의 유형에 따라 다른 인수 배열을 포함한다는 것을 알았습니다. 이 주제에 대한 자세한 내용은 [예외 필터](https://docs.nestjs.com/exception-filters#arguments-host)를 다시 참조하십시오.

## Execution context

`ExecutionContext`는 `ArgumentsHost`를 확장하여 현재 실행 프로세스에 대한 추가 정보를 제공합니다. 그 모습은 다음과 같습니다.

```typescript
export interface ExecutionContext extends ArgumentsHost {
  getClass<T = any>(): Type<T>;
  getHandler(): Function;
}
```

`getHandler()`메소드는 호출될 경로 핸들러에 대한 참조를 리턴합니다. `getClass()`메소드는 이 특정 핸들러가 속하는 `Controller` 클래스의 유형을 리턴합니다. 예를 들어, 현재 처리 된 요청이 `CatsController`의`create()`메소드로 예정된 `POST` 요청인 경우, `getHandler()`는 `create()` 메소드에 대한 참조를 리턴하고 `getClass()`는 `CatsController` **type** (인스턴스 아님)을 반환합니다.

## Call handler

두 번째 인수는 `CallHandler`입니다. `CallHandler` 인터페이스는 `handle()`메소드를 구현하는데, 인터셉터의 어느 시점에서 경로 핸들러 메소드를 호출하는 데 사용할 수 있습니다. `intercept()`메소드 구현에서 `handle()`메소드를 호출하지 않으면 라우트 핸들러 메소드가 전혀 실행되지 않습니다.

이 접근법은 `intercept()`메소드가 효과적으로 요청/응답 스트림을 **랩핑**한다는 것을 의미합니다. 결과적으로 최종 라우트 핸들러 실행 **전후**에 커스텀 로직을 구현할 수 있습니다. `handle()`을 호출하기 **전**에 실행되는 `intercept()` 메소드에 코드를 작성할 수 있다는 것이 분명하지만, 나중에 어떻게 되는지에 어떤 영향을 미칩니까? `handle()`메소드는 `Observable`을 리턴하므로, 강력한 [RxJS](https://github.com/ReactiveX/rxjs) 연산자를 사용하여 응답을 추가로 조작 할 수 있습니다. Aspect Oriented Programming 용어를 사용하여 라우트 핸들러의 호출 (즉, `handle()`호출)을 [Pointcut](https://en.wikipedia.org/wiki/Pointcut)이라고합니다. 추가 로직이 삽입됩니다.

예를 들어, 들어오는`POST/cats` 요청을 고려하십시오. 이 요청은 `CatsController` 안에 정의된 `create()`핸들러를 대상으로합니다. `handle()`메소드를 호출하지 않는 인터셉터가 도중에 호출되면 `create()`메소드가 실행되지 않습니다. `handle()`이 호출되고 (그리고 그것의 `Observable`이 리턴되면) `create()`핸들러가 트리거됩니다. 응답 스트림이 `Observable`을 통해 수신되면 추가 작업을 스트림에서 수행할 수 있으며 최종 결과는 호출자에게 반환됩니다.

## Aspect interception

우리가 살펴볼 첫번째 사용 사례는 인터셉터를 사용하여 사용자 상호 작용을 기록하는 것입니다 (예: 사용자 호출 저장, 비동기 적으로 이벤트 디스패치 또는 타임 스탬프 계산). 우리는 아래에 간단한 `LoggingInterceptor`를 보여줍니다:

```typescript
@@filename(logging.interceptor)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
}
@@switch
import { Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor {
  intercept(context, next) {
    console.log('Before...');

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
}
```

> info **힌트** `NestInterceptor<T,R>`는 `T`가 `Observable<T>`(응답 스트림을 지원)의 유형을 나타내고 `R`은`Observable<R>`에 의해 랩핑된 값의 유형인 일반 인터페이스입니다.
>
> warning **알림** 컨트롤러, 프로 바이더, 가드 등과 같은 인터셉터는`생성자`를 통해 **종속성을 주입 할 수 있습니다**.

`handle()`은 RxJS `Observable`을 반환하므로 스트림을 조작하는 데 사용할 수 있는 연산자를 다양하게 선택할 수 있습니다. 위의 예제에서 우리는 `tap()`연산자를 사용했는데, 이는 관찰 가능 스트림이 정상적으로 종료되거나 예외적으로 종료될 때 익명 로깅 기능을 호출하지만 응답주기를 방해하지는 않습니다.

## Binding interceptors

인터셉터를 설정하기 위해 `@nestjs/common` 패키지에서 가져온 `@UseInterceptors()`데코레이터를 사용합니다. [pipes](https://app.gitbook.com/%20pipes) 및 [guards](https://app.gitbook.com/%20guards)와 같이 인터셉터는 컨트롤러 범위, 방법 범위 또는 전역 범위 일 수 있습니다.

```typescript
@@filename(cats.controller)
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
```

> info **힌트** `@UseInterceptors()`데코레이터는 `@nestjs/common` 패키지에서 가져옵니다.

위의 구성을 사용하여 `CatsController`에 정의된 각 경로 핸들러는 `LoggingInterceptor`를 사용합니다. 누군가 `GET /cats` 엔드 포인트를 호출하면 표준 출력에 다음 출력이 표시됩니다.

```typescript
Before...
After... 1ms
```

인스턴스 대신 `LoggingInterceptor` 유형을 전달하여 프레임 워크의 인스턴스화와 종속성 주입을 가능하게 합니다. 파이프, 가드 및 예외 필터와 마찬가지로 내부 인스턴스도 전달할 수 있습니다.

```typescript
@@filename(cats.controller)
@UseInterceptors(new LoggingInterceptor())
export class CatsController {}
```

언급한 바와 같이, 위의 구성은 인터셉터를이 컨트롤러가 선언한 모든 핸들러에 연결합니다. 인터셉터의 범위를 단일 방법으로 제한하려면 **메소드 수준**에서 데코레이터를 적용하면됩니다.

전역 인터셉터를 설정하기 위해 Nest 애플리케이션 인스턴스의 `useGlobalInterceptors()`메소드를 사용합니다.

```typescript
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
```

전역 인터셉터는 모든 컨트롤러와 모든 경로 핸들러에 대해 전체 애플리케이션에서 사용됩니다. 의존성 주입의 관점에서, 모듈 외부에서 등록된 전역 인터셉터 (위의 예에서와 같이 `useGlobalInterceptors()`로)는 의존성이 주입될 수 없습니다. 이는 모듈의 컨텍스트 외부에서 수행되기 때문입니다. 이 문제를 해결하기 위해 다음 구성을 사용하여 **모든 모듈에서 직접 인터셉터**를 설정할 수 있습니다.

```typescript
@@filename(app.module)
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
  ],
})
export class AppModule {}
```

> info **힌트** 인터셉터에 대한 의존성 주입을 수행하기 위해이 접근법을 사용할 때, 이 구성이 사용되는 모듈에 관계없이, 인터셉터는 실제로는 전역적입니다. 어디에서 해야 합니까? 인터셉터 (위 예에서 `LoggingInterceptor`)가 정의된 모듈을 선택하십시오. 또한 커스텀 프로 바이더 등록을 다루는 유일한 방법은 `useClass`가 아닙니다. [여기](https://app.gitbook.com/fundamentals/custom-providers)에 대해 자세히 알아보십시오.

## Response mapping

우리는 `handle()`이 `Observable`을 반환한다는 것을 이미 알고 있습니다. 이 스트림에는 경로 처리기의 **반환** 값이 포함되어 있으므로 RxJS의 `map()`연산자를 사용하여 쉽게 변경할 수 있습니다.

> warning **경고** 응답 매핑 기능은 라이브러리 별 응답 전략에서 작동하지 않습니다 (`@Res()`객체를 직접 사용하는 것은 금지됨).

프로세스를 시연하기 위해 사소한 방식으로 각 응답을 수정하는 `TransformInterceptor`를 만들어 봅시다. RxJS의 `map()`연산자를 사용하여 응답 객체를 새로 생성된 객체의 `data` 속성에 할당하여 새 객체를 클라이언트에 반환합니다.

```typescript
@@filename(transform.interceptor)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface Response<T> {
  data: T;
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
    return next.handle().pipe(map(data => ({ data })));
  }
}
@@switch
import { Injectable } from '@nestjs/common';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor {
  intercept(context, next) {
    return next.handle().pipe(map(data => ({ data })));
  }
}
```

> info **힌트** Nest 인터셉터는 동기 및 비동기 `intercept()`메소드와 함께 작동합니다. 필요한 경우 단순히 메소드를 `비동기`로 전환 할 수 있습니다.

위의 구성에서 누군가가 `GET /cats` 엔드 포인트를 호출하면 응답은 다음과 같습니다 (라우트 핸들러가 빈 배열`[]`을 리턴한다고 가정).

```javascript
{
  "data": []
}
```

인터셉터는 전체 애플리케이션에서 발생하는 요구 사항에 대한 재사용 가능한 솔루션을 작성하는 데 큰 가치가 있습니다. 예를 들어, 각각의 `null` 값을 빈 문자열 `''`로 변환해야 한다고 상상해보십시오. 한 줄의 코드를 사용하여 인터셉터를 전역적으로 바인딩하여 등록된 각 핸들러에서 자동으로 사용할 수 있습니다.

```typescript
@@filename()
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class ExcludeNullInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next
      .handle()
      .pipe(map(value => value === null ? '' : value ));
  }
}
@@switch
import { Injectable } from '@nestjs/common';
import { map } from 'rxjs/operators';

@Injectable()
export class ExcludeNullInterceptor {
  intercept(context, next) {
    return next
      .handle()
      .pipe(map(value => value === null ? '' : value ));
  }
}
```

## Exception mapping

또 다른 흥미로운 사용 사례는 RxJS 의`catchError()`연산자를 사용하여 발생된 예외를 무시하는 것입니다.

```typescript
@@filename(errors.interceptor)
import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  BadGatewayException,
  CallHandler,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next
      .handle()
      .pipe(
        catchError(err => throwError(new BadGatewayException())),
      );
  }
}
@@switch
import { Injectable, BadGatewayException } from '@nestjs/common';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorsInterceptor {
  intercept(context, next) {
    return next
      .handle()
      .pipe(
        catchError(err => throwError(new BadGatewayException())),
      );
  }
}
```

## Stream overriding

핸들러 호출을 완전히 막고 대신 다른 값을 반환하려는 몇가지 이유가 있습니다. 명백한 예는 응답 시간을 개선하기 위해 캐시를 구현하는 것입니다. 캐시에서 응답을 반환하는 간단한 **캐시 인터셉터**를 살펴 보겠습니다. 실제 예에서 우리는 TTL, 캐시 무효화, 캐시 크기 등과 같은 다른 요소를 고려하고 싶지만 이 논의의 범위를 벗어납니다. 여기에서는 기본 개념을 보여주는 기본 예제를 제공합니다.

```typescript
@@filename(cache.interceptor)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, of } from 'rxjs';

@Injectable()
export class CacheInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const isCached = true;
    if (isCached) {
      return of([]);
    }
    return next.handle();
  }
}
@@switch
import { Injectable } from '@nestjs/common';
import { of } from 'rxjs';

@Injectable()
export class CacheInterceptor {
  intercept(context, next) {
    const isCached = true;
    if (isCached) {
      return of([]);
    }
    return next.handle();
  }
}
```

우리의 `CacheInterceptor`는 하드 코딩된 `isCached` 변수와 하드 코딩된 응답 `[]`을 가지고 있습니다. 여기서 주목할 점은 RxJS `of()`연산자에 의해 생성된 새로운 스트림을 반환하므로 경로 핸들러는 **전혀 호출되지 않습니다**. 누군가 `CacheInterceptor`를 사용하는 엔드 포인트를 호출하면 응답 (하드 코딩된 빈 배열)이 즉시 리턴됩니다. 일반적인 솔루션을 만들기 위해 `Reflector`를 활용하고 사용자 정의 데코레이터를 만들 수 있습니다. `Reflector`는 [guards](https://app.gitbook.com/guards) 챕터에 잘 설명되어 있습니다.

## More operators

RxJS 연산자를 사용하여 스트림을 조작할 수 있으므로 많은 기능이 제공됩니다. 다른 일반적인 사용 사례를 고려해 봅시다. 경로 요청에서 **시간 초과**를 처리한다고 가정해 보십시오. 일정 시간 후에 엔드 포인트가 아무것도 리턴하지 않으면 오류 응답으로 종료하려고합니다. 다음과 같은 구성으로 가능합니다.

```typescript
@@filename(timeout.interceptor)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { timeout } from 'rxjs/operators';

@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(timeout(5000))
  }
}
@@switch
import { Injectable } from '@nestjs/common';
import { timeout } from 'rxjs/operators';

@Injectable()
export class TimeoutInterceptor {
  intercept(context, next) {
    return next.handle().pipe(timeout(5000))
  }
}
```

5 초 후에 요청 처리가 취소됩니다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jakekwak.gitbook.io/nestjs/overview/interceptors.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
