[NestJS 공식문서 정독하기] Overview - Interceptors
2022. 8. 3. 21:13ㆍWeb/NestJS
반응형
🔗 https://docs.nestjs.com/interceptors
Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Progamming), FP (Functional Programming), and FRP (Functional Reac
docs.nestjs.com
Interceptors
- interceptor는 NestInterceptor 인터페이스를 구현하며 @Injectable() 데코레이터가 적용되어 있는 클래스임
- interceptor는 AOP에서 영감을 받은 여러가지 역할을 할 수 있음
- 추가 로직을 메소드 호출 이전/이후에 바인딩
- 함수의 반환값을 조작
- 함수에서 던져진 예외를 조작
- 기존 함수의 기능을 확장
- 특정 함수를 완전 덮어씌우기 (용도: 캐싱 등등...)
- 모든 interceptor는
intercept
메소드를 구현해야 함 - 매개변수
ExecutionContext
는 guard의 그것과 동일함
Call handler
intercept
의 두번째 매개변수는CallHandler
인터페이스이며handle
메소드를 갖고 있음handle
메소드는 route handler를 호출하는 함수임- 따라서, interceptor의 로직상의 특정 시점에 handler를 동작시킬 수 있으며
handle
메소드를 호출하지 않으면 handler를 무시할 수도 있음 handle
메소드는 rxjs의Observable
타입을 반환하므로 이를 활용할 수 있음- AOP에서 route handler를 호출하는 것을 Pointcut이라고 함
Aspect interception
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`)),
);
}
}
Binding interceptors
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
@UseInterceptors(new LoggingInterceptor())
export class CatsController {}
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
- pipe, guard와 동일하게 controller-scoped, method-scoped, global-scoped 모두 가능
- 적용 방법 및 특성 또한 동일
Response mapping
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 })));
}
}
handle
메소드에서 반환하는Observable
은 route handler에서 처리한 데이터가 들어있음map
operator로 손쉽게 해당 데이터를 조작할 수 있음- 위 코드는 handler에서 반환한 데이터를 새로운 객체의 data라는 키에 매핑하는 interceptor임
- intercept 메소드는 동기/비동기 모두 가능
Exception mapping
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())),
);
}
}
- rxjs의
catchError
메소드로 예외를 catch해서 조작할 수도 있음
Stream overriding
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();
}
}
- 아예 handler 호출을 하지 않고 다른 데이터를 반환하는 케이스도 있음
- 위와 같이 캐싱 처리를 하기 위해 캐싱된 데이터를 반환하는 경우는 handler를 스킵할 수도 있음
More operators
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from '@nestjs/common';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { catchError, timeout } from 'rxjs/operators';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
timeout(5000),
catchError(err => {
if (err instanceof TimeoutError) {
return throwError(() => new RequestTimeoutException());
}
return throwError(() => err);
}),
);
};
};
- 다양한 rxjs의 operator를 활용할 수 있음
- 위와 같이
timeout
operator를 활용해서 일정 시간 동안 응답이 없을 경우 error 처리를 할 수도 있음
반응형