import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { tap, map } from 'rxjs/operators';
@Injectable()
export class CacheInterceptor implements NestInterceptor {
private readonly cache = new Map();
intercept(
context: ExecutionContext,
next: CallHandler<any>,
): Observable<any> | Promise<Observable<any>> {
const key = context.getHandler().name;
const cacheValue = this.cache.get(key);
const now = Date.now();
if (cacheValue) {
if (Date.now() < cacheValue.expirationTime) {
console.log('in?');
console.log(this.cache.get(key));
console.log(`Execution time: ${Date.now() - now}ms`);
return of(cacheValue.data);
}
this.cache.delete(key);
}
return next.handle().pipe(
tap((data) => {
const expirationTime = Date.now() + 1000 * 60 * 5;
this.cache.set(key, { data, expirationTime });
console.log(`Execution time: ${Date.now() - now}ms`);
}),
);
}
}
Interceptor를 이용한 캐싱은 메모리 내에 데이터를 저장합니다. 이 방법은 간단하고 빠르지만, 서버가 재시작되거나, 메모리가 해제되면 캐시된 데이터는 사라집니다. 또한 이 방식은 동일한 애플리케이션 인스턴스 내에서만 캐시가 유지됩니다. 따라서 분산 시스템이나 다중 서버 환경에서는 효과적이지 않을 수 있습니다.
Redis를 사용한다면 영구적인 스토리지를 제공하므로 서버가 재시작되거나 메모리가 해제되더라도 데이터가 유지됩니다. 또한, Redis는 네트워크를 통해 접근할 수 있으므로, 다양한 서버나 애플리케이션 인스턴스 간에 데이터를 공유할 수 있습니다. 이는 분산 시스템이나 클러스터링 환경에서 매우 유용합니다.
Interceptor와 Redis를 함께 사용해서 Interceptor를 사용하여 요청과 응답을 관리하고, 실제 캐싱 작업은 Redis와 같은 데이터베이스에 위임할 수 있습니다. 이렇게 하면 Interceptor의 기능성과 Redis의 확장성을 동시에 활용할 수 있습니다.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, throwError, TimeoutError } from 'rxjs';
import { timeout, catchError } from 'rxjs/operators';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next
.handle()
.pipe(
timeout(5000), // 5초가 지나면 타임아웃 에러 발생
catchError(err => {
if (err instanceof TimeoutError) {
return throwError(new Error('Timeout has occurred')); // 타임아웃 에러를 캐치하고 대체 에러를 반환
}
return throwError(err); // 그 외의 에러는 그대로 반환
}),
);
}
}