// 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' 메시지를 추가하도록 합니다.
// 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 요청이나 응답을 조작하는 것이 더 바람직한 방식일 수 있습니다.
데코레이터는 코드의 메타데이터를 추가하거나 변경하는 데 사용되며, 인터셉터는 특정 메소드 또는 컨트롤러의 실행 전후에 추가적인 로직을 실행하는 데 사용됩니다.
미들웨어와 인터셉터는 모두 요청과 응답의 처리 과정에서 중간에 개입하여 로직을 실행할 수 있는 구조입니다. 하지만 둘 사이에는 사용 방식과 적용 시점, 그리고 사용 사례에 있어서 몇 가지 차이점이 있습니다.
미들웨어 (Middleware):
req
, res
, next
이 세 가지 파라미터를 가진 함수입니다.next
함수를 호출하여 요청을 다음 미들웨어로 전달하거나, 라우팅 핸들러로 전달합니다. 인터셉터 (Interceptor):
둘 다 비슷한 역할을 하지만, 미들웨어는 주로 요청이 들어오는 단계에서 사용하고, 인터셉터는 요청 처리 전후에 더 세부적인 제어가 필요할 때 사용합니다. 따라서 미들웨어와 인터셉터를 적절히 활용하여 효율적인 요청 처리를 설계할 수 있습니다.
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 {}