정확하게는 Exception Filters라는 내장 기능이다. 보통 어떤 요청이 컨트롤러에 도착하기 전에 필터와 파이프를 거쳐 재가공 되어 도착하는데, 파이프는 이전에 학습한대로 데이터의 재가공이라던가 암호화, 복호화를 도와주는 계층이라면, 필터는 예외처리 즉, 요청코드를 나누는 계층이라고 보면 된다.
내장된 Exception을 활용해 필요한 Expcetion들의 경우를 나누어준다.
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from '@nestjs/common';
import { Response, Request } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const _ctx = host.switchToHttp();
const res = _ctx.getResponse<Response>();
const status = exception.getStatus();
const request = _ctx.getRequest<Request>();
res.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
커스텀 익셉션을 만들어서 사용할 수 있는데, ExcecptionFilter 를 상속받아 만들 수 있다. 그리고, 특히 ArgumentsHost 라는게 눈에 띄는데, 무엇인지 찬찬히 살펴보자.
ArgumentsHost는 다양한 타입의 컨텍스트를 처리할 수 있도록 도와주는 유틸리티입니다. NestJS는 기본적으로 세 가지 종류의 컨텍스트를 지원합니다:
ArgumentsHost는 이 세 가지 컨텍스트 타입을 전환(switch)할 수 있는 메서드를 제공합니다.
switchToHttp(): HTTP 컨텍스트로 전환합니다.switchToRpc(): RPC 컨텍스트로 전환합니다.switchToWs(): WebSocket 컨텍스트로 전환합니다.getArgs(): 원래의 인자 배열을 반환합니다.getArgByIndex(index: number): 인자의 특정 인덱스를 반환합니다라고 나와있다. 내장 http 통신은 express로 이루워지는 것 같다.
그래서,
const _ctx = host.switchToHttp();
const res = _ctx.getResponse<Response>();
const status = exception.getStatus();
const request = _ctx.getRequest<Request>();
를 통해서 현재 컨택스트를 바꾸고, res의 객체를 직접 수정하고, req을 참고 할 수 있게 되는것 같다.
private readonly logger = new Logger();
우리가 잘 아는 그 로거! 로그가 잘 찍힌다.
요것을 좀 더 뜯어서 살펴보자면,
private readonly logger = new Logger(MyappController.name);
을 통해서 컨텍스트를 주입 할 수 있다. (그냥 로그에 어느 컨트롤러인지, 어느 서비스인지 확인 할 수 있다는 뜻)
커스텀 로깅을 미들웨어를 통해 특정 컨트롤러에서 로깅을 찍을 수 있게 한다. 이제는 좀 지겹듯이, 이렇게 하는 이유는 당연히 결합도를 낮추고 유지보수를 용이하게 하기 위해!
import { Logger, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
export class LoggingMiddleware implements NestMiddleware {
private readonly logger = new Logger();
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl } = req;
const startTime = Date.now();
res.on('finish', () => {
const { statusCode } = res;
const repsonseTime = Date.now() - startTime;
this.logger.log(`${method} ${originalUrl} ${statusCode} ${repsonseTime}`);
});
next();
}
}
지금 눈치챈건데 여긴 오버라이드 데코레이터가 없어도 되네??
NestMiddleware를 상속받아 주고 use 라는 함수를 오버라이드 해주면 된다. 미들웨어 특징답게 req,res 그리고 next함수를 통해 중간에 가로챌 수 있게 설계되었다.
그후에, main.ts에서
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
//범용적으로 적용한다면 *, 아니면 특정 컨트롤러를 적으면 됨
consumer.apply(LoggingMiddleware).forRoutes('*');
}
}
AppModule에서 NestModule을 받아 미들웨어를 세팅해주면 된다.