인터셉터는 요청을 처리하는 동안, 요청과 응답을 가로채서 로직을 추가하거나 변형하는 데 사용되는 기능입니다.
인터셉터는 메서드 실행 전후에 추가적인 동작을 수행할 수 있으며, 주로 로깅, 응답 변형, 캐싱, 트랜잭션 관리 등의 목적으로 사용됩니다.
정의: 요청을 처리하는 메서드 호출을 가로채어 그 전후에 특정 동작을 수행할 수 있는 클래스입니다.
NestInterceptor 인터페이스를 구현해야 하며, 요청 처리 로직을 확장할 수 있습니다.
용도
요청 및 응답 변형
로깅 및 오류 처리
응답 시간 측정
캐싱
트랜잭션 관리
응답 시간을 측정하는 인터셉터를 구현할 수 있습니다.
아래는 간단한 응답 시간 측정 인터셉터의 예입니다.
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class ResponseTimeInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const start = Date.now();
return next
.handle()
.pipe(
tap(() => {
const end = Date.now();
const responseTime = end - start;
console.log(`Response Time: ${responseTime}ms`);
}),
);
}
}
intercept 메서드에서 ExecutionContext와 CallHandler를 사용하여 요청과 응답을 처리합니다.
next.handle()을 호출하여 다음 핸들러로 요청을 전달합니다.
tap 연산자를 사용하여 응답이 처리된 후 응답 시간을 계산하고 출력합니다.
제공하는 기본 캐시 인터셉터를 사용할 수도 있지만, 직접 구현할 수도 있습니다. 아래는 간단한 캐시 인터셉터의 예입니다.
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements NestInterceptor {
private cache = new Map();
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
const cacheKey = request.url;
// 캐시된 데이터가 있으면 반환
if (this.cache.has(cacheKey)) {
return new Observable((observer) => {
observer.next(this.cache.get(cacheKey));
observer.complete();
});
}
return next.handle().pipe(
tap((data) => {
// 응답 데이터를 캐시에 저장
this.cache.set(cacheKey, data);
}),
);
}
}
###설명
요청 URL을 키로 사용하여 데이터를 캐싱합니다.
요청이 들어올 때 캐시에 데이터가 있는지 확인하고, 있으면 캐시된 데이터를 반환합니다.
없으면 핸들러를 호출하여 데이터를 받아오고, 받은 데이터를 캐시에 저장합니다.
트랜잭션을 관리하는 인터셉터를 구현하여 데이터베이스 작업이 원자성을 가지도록 할 수 있습니다. TypeORM과 함께 사용하는 예시입니다.
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { DataSource } from 'typeorm';
import { Observable } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
@Injectable()
export class TransactionInterceptor implements NestInterceptor {
constructor(private readonly dataSource: DataSource) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const queryRunner = this.dataSource.createQueryRunner();
return Observable.create((observer) => {
queryRunner.connect();
queryRunner.startTransaction();
next.handle().pipe(
tap((data) => {
observer.next(data);
observer.complete();
return queryRunner.commitTransaction();
}),
catchError((err) => {
queryRunner.rollbackTransaction();
observer.error(err);
}),
).subscribe();
});
}
}
각 인터셉터는 필요에 따라 추가적인 로직을 삽입하여 더욱 복잡한 기능을 수행할 수 있습니다.
필요한 경우, 인터셉터를 모듈에 등록하여 사용할 수 있습니다.