본문 바로가기

dev/nestjs

인터셉터

인터셉터는 @Injectable() 데코레이터가 달린 클래스이며 NestInterceptor 인터페이스를 구현한다.

인터셉터는 클라이언트 요청이 컨트롤러 전, 후에 사용된다.

 

 

인터셉터는 AOP (Aspect Oriented Programming) 기술을 따라 이러한 기능이 있다고 한다.

  • 메서드 실행 전/후에 추가 로직 바인딩
  • 함수에서 반환된 결과 변환
  • 함수에서 발생한 예외 변환
  • 기본 기능 동작 확장
  • 특정 조건에 따라 함수를 완전히 재정의합니다(예: 캐싱 목적).

 

AOP 란 관점 지향 프로그래밍이라고 한다.

여러 기능이 있으면 공통으로 수행하는 기능도 존재할 것인데, 이 공통적인 기능을 수평적으로 묶어서 모듈화한 것이 AOP 패턴으로 수평적관심사를 분리하여 모듈성을 높이는 것이라고 한다.

 

인터셉터는 의존성 주입 대상이 되므로 @Injectable() 데코레이터를 선언한다.



// logging.intercepter.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } 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`)),
      );
  }
}

 

그리고 NestInterceptor 를 상속한다. NestInterceptor 의 intercept() 메서드를 가지는데 이 메서드는 ExecutionContext, CallHandler 인수가 필요하다. 

ExecutionContext는 ArgumentsHost 클래스를 상속하는데 이 클래스로 핸들러에게 전달되는 인수 등 실행되는 프로세스 정보를 파악할 수 있다. CallHandler는 요청을 라우터로 이동시켜준다.  

 

Before... 부분이 요청이 컨트롤러에 닿기 전 실행되는 부분이고, After... 부분이 컨트롤러에서 실행되고 난 뒤 부분이다.

 

 

인터셉터 적용

 

특정 라우터 핸들러에 적용

 

@Get()
@UseInterceptors(LoggingInterceptor) // 인터셉터 적용
getHelloUser(): string {
  return this.userService.getHelloUser('nestjs');
}

 

컨트롤러 전체에 적용

 

import { UserService } from './user.service';
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { LoggingInterceptor } from '../intercepter/logging.intercepter';

@Controller('user')
@UseInterceptors(LoggingInterceptor) // 인터셉터 적용
export class UserController {
  constructor(
    private readonly userService: UserService,
    private readonly configService: ConfigService,
  ) {}

  @Get()
  getHelloUser(): string {
    return this.userService.getHelloUser('nestjs');
  }
}

 

프로젝트 전체에 적용

 

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './intercepter/logging.intercepter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalInterceptors(new LoggingInterceptor()); // 인터셉터 글로벌 적용
  await app.listen(3000);
}
bootstrap();

 

종속성 주입을 위해 AppModule에 인터셉터 설정

 

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { TestModule } from './test/test.module';
import { AuthModule } from './auth/auth.module';
import { ConfigurationModule } from './config/config.module';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from './intercepter/logging.intercepter';

@Module({
  imports: [UserModule, TestModule, AuthModule, ConfigurationModule],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_INTERCEPTOR, // 인터셉터 적용
      useClass: LoggingInterceptor,
    },
  ],
})
export class AppModule {}

 

인터셉터 적용 후 로그

 

인터셉터 응답 매핑, 예외 처리

 

// response.interceptor.ts
import { BadGatewayException, CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable, catchError, throwError } from 'rxjs';
import { tap, map } from 'rxjs/operators';

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

    return next.handle().pipe(
      tap(() => console.log('After...')),
      map((res) => ({ res })), // 컨트롤러 응답값
      catchError((err) => throwError(() => new BadGatewayException())), // 예외 처리
    );
  }
}

 

응답은 이렇게 나온다.

'dev > nestjs' 카테고리의 다른 글

FileFieldsInterceptor  (0) 2023.03.16
MariaDB 연동  (0) 2023.02.16
HTTP module  (0) 2023.02.13
환경 변수  (0) 2023.02.12
passport, jwt  (0) 2023.02.10