[Nest.JS] 인터셉터의 응답 시간, 캐시, 트랜잭션 관리 구현하기

궁금하면 500원·2024년 8월 17일
0

인터셉터는 요청을 처리하는 동안, 요청과 응답을 가로채서 로직을 추가하거나 변형하는 데 사용되는 기능입니다.

인터셉터는 메서드 실행 전후에 추가적인 동작을 수행할 수 있으며, 주로 로깅, 응답 변형, 캐싱, 트랜잭션 관리 등의 목적으로 사용됩니다.

1. Nest.js의 Interceptor

  • 정의: 요청을 처리하는 메서드 호출을 가로채어 그 전후에 특정 동작을 수행할 수 있는 클래스입니다.
    NestInterceptor 인터페이스를 구현해야 하며, 요청 처리 로직을 확장할 수 있습니다.

  • 용도

  • 요청 및 응답 변형

  • 로깅 및 오류 처리

  • 응답 시간 측정

  • 캐싱

  • 트랜잭션 관리

2. Response Time Interceptor 구현

응답 시간을 측정하는 인터셉터를 구현할 수 있습니다.

아래는 간단한 응답 시간 측정 인터셉터의 예입니다.

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 메서드에서 ExecutionContextCallHandler를 사용하여 요청과 응답을 처리합니다.

  • next.handle()을 호출하여 다음 핸들러로 요청을 전달합니다.

  • tap 연산자를 사용하여 응답이 처리된 후 응답 시간을 계산하고 출력합니다.

3. Cache Interceptor 구현

제공하는 기본 캐시 인터셉터를 사용할 수도 있지만, 직접 구현할 수도 있습니다. 아래는 간단한 캐시 인터셉터의 예입니다.

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을 키로 사용하여 데이터를 캐싱합니다.

  • 요청이 들어올 때 캐시에 데이터가 있는지 확인하고, 있으면 캐시된 데이터를 반환합니다.

  • 없으면 핸들러를 호출하여 데이터를 받아오고, 받은 데이터를 캐시에 저장합니다.

4. Transaction Interceptor 구현

트랜잭션을 관리하는 인터셉터를 구현하여 데이터베이스 작업이 원자성을 가지도록 할 수 있습니다. 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();
    });
  }
}
  • ###설명
  • DataSource에서 쿼리 러너를 생성하여 데이터베이스와의 연결을 관리합니다.
  • startTransaction()으로 트랜잭션을 시작하고, 다음 핸들러의 결과에 따라 커밋하거나 롤백합니다.
  • catchError를 사용하여 오류 발생 시 트랜잭션을 롤백합니다.

결론

  1. Interceptor는 NestJS에서 요청과 응답을 가로채서 추가 로직을 수행하는 데 사용됩니다.
  2. Response Time Interceptor는 응답 시간을 측정하여 로그로 출력합니다.
  3. Cache Interceptor는 요청 URL에 대한 응답 데이터를 캐시하여 성능을 개선합니다.
  4. Transaction Interceptor는 데이터베이스 트랜잭션을 관리하여 데이터 일관성을 유지합니다.

각 인터셉터는 필요에 따라 추가적인 로직을 삽입하여 더욱 복잡한 기능을 수행할 수 있습니다.
필요한 경우, 인터셉터를 모듈에 등록하여 사용할 수 있습니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글