728x90

- Interceptor란?

NestJS에서 유일하게 요청이 들어올 때 그리고 응답이 나갈 때 모두 로직을 실행할 수 있는 미들웨어다.

  • 함수 실행 전/후에 추가 로직을 바인딩한다.
  • 함수에서 반환된 값을 변환한다.
  • 함수에서 던진 에러를 변환한다
  • 함수의 기본 기능에서 추가 기능을 연장한다.
  • 조건에 따라 함수의 기능을 override한다.

Interceptor Response 핸들링은 기본적으로 RxJS를 사용한다.


- Interceptor 구현방법

@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`)));
  }
}

로직 실행 시간 체크하는 예시


- 예시

- 시간 체크

// response-time.interceptor.ts

@Injectable()
export class ResponseTimeInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Observable<any> | Promise<Observable<any>> {
    const req = context.switchToHttp().getRequest();

    const reqTime = Date.now();

    return next.handle().pipe(
      delay(1000),
      tap(() => {
        const resPTime = Date.now();
        const diff = resPTime - reqTime;

        if (diff > 1000) {
          console.log(`[!!!TIMEOUT!!! ${req.method} ${req.path}] ${diff}ms`);

          throw new InternalServerErrorException(
            '시간이 너무 오래 걸렸습니다!',
          );
        } else {
          console.log(`[${req.method} ${req.path}] ${diff}ms`);
        }
      }),
    );
  }
}

1초 이상 걸리면 프론트에 에러 던져주는 코드 (중간에 delay()를 넣어서 일부러 에러 던졌음)

// app.module.ts

providers: [
    { provide: APP_INTERCEPTOR, 
      useClass: ResponseTimeInterceptor },
  ],

전체 적용

 

- 캐시

// cache.interceptor.ts

@Injectable()
export class CacheInterceptor implements NestInterceptor {
  private cache = new Map<string, any>();

  intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Observable<any> | Promise<Observable<any>> {
    const request = context.switchToHttp().getRequest();

    // GET /movie
    const key = `${request.method}-${request.path}`;

    if (this.cache.has(key)) {
      return of(this.cache.get(key));
    }

    return next.handle().pipe(tap((response) => this.cache.set(key, response)));
  }
}

같은 method & path로 요청오면 캐시에 저장해뒀던거 그대로 반환

실제로는 redis 연결해서 쓰고, pagination같이 요청에 따라 결과값이 달라져야한다면 문제 발생 등의 이유로 예시 코드일뿐임

 

// movie.controller.ts

@Get()
  @Public()
  @UseInterceptors(CacheInterceptor)
  getMoives(
    @Query() dto: GetMoviesDto, //
  ) {
    return this.movieService.findAll(dto);
  }

특정 라우터에 적용

 

- Transaction

// transaction.interceptor.ts

@Injectable()
export class TransactionInterceptor implements NestInterceptor {
  constructor(private readonly dataSource: DataSource) {}

  async intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Promise<Observable<any>> {
    const req = context.switchToHttp().getRequest();

    const qr = this.dataSource.createQueryRunner();

    await qr.connect();
    await qr.startTransaction();

    req.queryRunner = qr;

    return next.handle().pipe(
      catchError(async (e) => {
        await qr.rollbackTransaction();
        await qr.release();

        throw e;
      }),
      tap(async () => {
        await qr.commitTransaction();
        await qr.release();
      }),
    );
  }
}

qr 만들어서 req에 넣어주고, qr 시작부분 실행된 상태로 로직 돌고 나올 때 뒷 부분 실행됨.

// movie.service.ts

return await qr.manager.findOne(Movie, {
      where: {
        id: movieId,
      },
      relations: ['director', 'genres'],
    });

나가는 인터셉터에서 commit과 release가 이루어지기 때문에 기존 코드의 끝이 qr.manager로 바뀜


https://fastcampus.co.kr/classroom/239666

 

커리어 성장을 위한 최고의 실무교육 아카데미 | 패스트캠퍼스

성인 교육 서비스 기업, 패스트캠퍼스는 개인과 조직의 실질적인 '업(業)'의 성장을 돕고자 모든 종류의 교육 콘텐츠 서비스를 제공하는 대한민국 No. 1 교육 서비스 회사입니다.

fastcampus.co.kr

 

728x90

'코딩 > Nest.js' 카테고리의 다른 글

Custom Decorator  (1) 2024.10.24
Exception Filter  (1) 2024.10.23
Pagination  (0) 2024.10.23
RBAC (Role Based Access Control)  (0) 2024.10.23
Guard 실사용적  (0) 2024.10.23

+ Recent posts