인터셉터는 @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 |