Nest.js Custom Decorator, Interceptor

이건선·2023년 6월 21일
0

Node.js

목록 보기
30/32

1. Custom Decorator

createParamDecorator


// 1번 코드

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const GoodLuckDecorator = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getResponse();
    request.on('finish', () => {
      if (typeof request.res?.data === 'object') {
        request.res.data['message'] = 'good luck';
      }
    });
  },
);

1번 코드에서는 Nest.js의 createParamDecorator를 사용합니다. 이 함수는 주로 라우트 핸들러에 있는 특정 파라미터를 가로채서 다르게 처리하는 데 사용되는 데코레이터를 만드는데 사용됩니다. 주로 @Req(), @Res(), @Body(), @Param() 등의 Nest.js에 내장된 데코레이터를 대체하는 형태로 사용됩니다.

이 경우에는 createParamDecorator를 통해 만든 데코레이터를 ExecutionContext를 통해 HTTP 응답 객체를 가져오는데 사용하고 있습니다. 이를 통해 모든 요청에 대한 응답이 완료되었을 때(finish 이벤트) 응답 데이터에 'good luck' 메시지를 추가하도록 합니다.


MethodDecorator

// 2번 코드

export function GoodLuck(): MethodDecorator {
  return (
    target: Object,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor,
  ) => {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      console.log('good luck');
      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

---

2번 코드에서는 기본 TypeScript의 메서드 데코레이터를 직접 사용하여 'good luck' 메시지를 콘솔에 출력합니다. 여기서는 descriptor.value를 수정하여 원래 메서드를 호출하기 전에 메시지를 출력합니다. 이 방법은 원래 메서드의 동작을 수정하거나, 메서드 호출 전후에 추가 동작을 수행하려는 경우에 적합합니다.

하지만 이 방법은 Nest.js의 HTTP 처리 플로우와는 별도로 동작하므로, 이 데코레이터를 통해 HTTP 요청이나 응답을 직접 조작할 수는 없습니다.

결론 : 이 두 방식 모두 메서드에 적용되는 데코레이터를 만들고 있지만, 적용되는 곳과 동작 방식에 차이가 있습니다.

  • HTTP 요청이나 응답을 직접 조작하려는 경우 = 1번 코드의 방식이 더 적합
  • 간단히 메서드 호출 전후에 추가 동작을 수행하려는 경우 = 2번 코드의 방식이 더 적합하겠습니다.

그러나 실제로는 미들웨어나 인터셉터를 사용하여 HTTP 요청이나 응답을 조작하는 것이 더 바람직한 방식일 수 있습니다.

데코레이터는 코드의 메타데이터를 추가하거나 변경하는 데 사용되며, 인터셉터는 특정 메소드 또는 컨트롤러의 실행 전후에 추가적인 로직을 실행하는 데 사용됩니다.

2. Interceptor

Middleware vs Interceptor

미들웨어와 인터셉터는 모두 요청과 응답의 처리 과정에서 중간에 개입하여 로직을 실행할 수 있는 구조입니다. 하지만 둘 사이에는 사용 방식과 적용 시점, 그리고 사용 사례에 있어서 몇 가지 차이점이 있습니다.

미들웨어 (Middleware):

  • 미들웨어는 요청과 응답 객체에 직접 접근할 수 있는 Express.js 스타일의 함수입니다. 즉, 미들웨어는 req, res, next 이 세 가지 파라미터를 가진 함수입니다.
  • 미들웨어는 HTTP 요청이 라우팅 핸들러에 도달하기 전에 실행됩니다. 즉, 요청이 라우팅 핸들러에 전달되기 전에 원하는 로직을 실행할 수 있습니다.
  • 미들웨어는 보통 로깅, 에러 핸들링, 요청 검증, 요청 본문 파싱 등의 목적으로 사용됩니다.
  • 미들웨어는 next 함수를 호출하여 요청을 다음 미들웨어로 전달하거나, 라우팅 핸들러로 전달합니다.

인터셉터 (Interceptor):

  • 인터셉터는 AOP(Aspect-Oriented Programming)에 기반한 요청/응답 처리 구조입니다.
  • 인터셉터는 요청을 핸들하는 컨트롤러가 실행되기 전과 후에 로직을 추가할 수 있습니다. 따라서 미들웨어보다 더 세밀한 제어가 가능합니다.
  • 인터셉터는 요청을 처리한 후에 변환하거나, 추가 데이터를 반환하거나, 예외를 변환하는 등의 로직을 수행합니다.
  • 또한 인터셉터는 타임아웃을 설정하거나, 응답을 캐시하는 등의 기능도 제공합니다.

둘 다 비슷한 역할을 하지만, 미들웨어는 주로 요청이 들어오는 단계에서 사용하고, 인터셉터는 요청 처리 전후에 더 세부적인 제어가 필요할 때 사용합니다. 따라서 미들웨어와 인터셉터를 적절히 활용하여 효율적인 요청 처리를 설계할 수 있습니다.


import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class GoodLuckInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map(data => ({ ...data, msg: 'good luck' })),
    );
  }
}

...

// 방법 1. 특정 메서드에만 적용하기
  @Get('/getAll')
  @UseInterceptors(GoodLuckInterceptor)

//방법 2. 해당 class 전체에 적용하기
@Module({
  imports: [StockSchema],
  controllers: [BoardsController],
  providers: [
    BoardsService,
    {
      provide: APP_INTERCEPTOR,
      useClass: GoodLuckIntercepotor,
    },
  ],
})
export class BoardsModule {}


profile
멋지게 기록하자

0개의 댓글