Interceptors는 또 뭐고?

HanSH·2024년 1월 21일

NestJS

목록 보기
10/29

@Injectable() 데코레이터를 사용하고 NestInterface를 구현해서 만든다.
@Injectable() 데코레이터를 사용하는거 보면 얘도 프로바이더.

Aspect Oriented Programming 기법을 사용하여 아래를 수행할 수 있다.

  • 추가 로직을 메서드 실행 전/후에 바인딩 가능
  • 함수에서 반환된 결과 변환
  • 함수에서 던져진 예외 변환
  • 기본 함수 동작 확장
  • 특정 조건에 따라 함수를 완전히 재정의(e.g., 캐싱)

기본

NestInterfaceintercept(context: ExecutionContext, next: CallHandler)을 구현한다.
이 경우 next.handle().pipe()를 반환하는데, 이를 설명하면 다음과 같다.

// route handler 호출 전
return 
	next.handle() // route handler 호출
  		// router 종료 후 실행. 아래의 내용이 없다면 라우트 종료 후 실행되는 코드 없음.
		.pipe(map((data) => (data === undefined ? null : data)));

이때, pipe에서는 RxJS tap(), map() 연산자를 사용할 수 있다. 둘 다 Observable의 데이터 작업을 수행하지만 차이점은 다음과 같다.

  • tap: 실행 결과는 Observable의 출력에 영향 주지 않음.
  • map: 실행 결과는 Observable의 출력을 수정함

다른 연산자로는 timeout, catchError()이 있다.

return next.handle().pipe(
  timeout(5000), // 5000ms, 즉 5초가 지나면 error 발생
  catchError(err => { // error가 발생했다면 실행
    if (err instanceof TimeoutError) {
      return throwError(() => new RequestTimeoutException());
    }
    return throwError(() => err);
  }),
);

더 많은 연산자가 있지만, 이 부분은 공식 문서를 참고하길 바란다.

ExecutionContext - context

이곳을 참고하자. 공식 문서의 Overview에는 자세하게 나와있지 않다.
대충 ArgumentsHost 인터페이스를 상속하는데, 클라이언트의 요청 정보를 받아온다 생각하면 될 것 같다.

CallHandler - next

next 변수는 handle() 메소드를 가지고 있다. 이 메소드가 호출되지 않는다면 라우트 핸들러는 실행되지 않는다.

이 방식은 intercept() 메소드가 req/res 흐름을 효과적으로 감싼다는 것을 의미한다. 결과적으로 최종 라우트 핸들 실행 전/후에 사용자 정의 로직을 넣을 수 있다는 뜻이다!
CallHandler#handle 메소드는 Observable을 반환하므로 옵저버 패턴을 사용한다는 것을 추측할 수 있다. 즉 실행이 되고 값을 수정할 수 있다는 뜻.
해당 반환값은 RxJS 연산자를 통해 응답을 추가로 조작할 수 있다.
handle()이 호출되는 지점을 Pointcut이라고 하는데, 이는 추가 로직이 삽입되는 지점임을 알린다.

사용

  • Router/Controller - @UseInterceptors(<Interceptor>)
  • Global - app.useGlobalInterceptors(new <Interceptor>());
    글로벌 인터셉터의 경우도 의존성 문제가 있다. 결국 모든 글로벌 미들웨어는 의존성 문제가 있다 볼 수 있다.
@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
  ],
})
export class AppModule {}

provide만 다르지 사용법은 다 같다.

profile
저는 말하는 싹 난 감자입니다

0개의 댓글