Interceptors

@Injectable 데코레이터가 달려서 의존성 주입이 가능했음.

AOP에서 영향을 받은거라고 함.

AOP(Aspect Oriented Programming) : 관점 지향 프로그래밍

이게 뭐냐…

AOP에 관하여

만약 어떤 프로그램에 4개의 기능이 있다고 친다.

그러면 그 4개의 기능 하나당 모듈이 하나씩이라고 생각해보자.

그러나 그 4개의 모듈을 아우르는 공통된 기능이 필요할 수 있다. (가장 대표적으로 logging)

우리는 지난시간에 middleware를 통해서 logging을 구현한바 있다.

그러나 Interceptor를 통해서도 그런 공통된 기능을 구현할 수 있다는 것이 요지이다.

Request LifeCycle

Incoming request
Globally bound middleware
Module bound middleware
Global guards
Controller guards
Route guards
Global interceptors (pre-controller)
Controller interceptors (pre-controller)
Route interceptors (pre-controller)
Global pipes
Controller pipes
Route pipes
Route parameter pipes
Controller (method handler)
Service (if exists)
Route interceptor (post-request)
Controller interceptor (post-request)
Global interceptor (post-request)
Exception filters (route, then controller, then global)
Server response

위와 같은데

  1. Request가 오면
  2. Middleware를 타고
  3. guard를 탄다고 하고
  4. 그다음이 Interceptors 이다. (precontroller)

그리고

  1. pipes
  2. Controller
  3. Service
  4. 다시 Interceptors (post intercept)
  5. Filter
  6. Respose (최종)

이런 순서임을 알 수 있다.

그러면 구현해보자

지난번에 실패했을 때 전용 Filter를 만들었으니 이번에는 성공했을 때 전용 Logger를 만들어보자

success.intercepters.ts

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class SuccessInterceptor 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`)),
      );
  }
}

이렇게 올려두고

cats.controllers.ts 에서

@Controller('cats')
@UseInterceptors(SuccessInterceptor)

위와 같이 설정해주면

Before After를 가져오게 된다.

다음과 같이 코딩하면..

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';

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

    return next.handle().pipe(map((data) => ({
      success: true,
      data,
    })));
  }
}

응답하는 데이터를 감싸서 success:true와 함께 보낼 수 있게 된다.

그렇게 되면 성공시에는 무조건 success:true를 넣어서 보내는 공통 로직이 구현이 되는 것이다.

너무 복잡하게 생각하지 말고 일단 형식만 볼 것.