🛠️NestJS의 lifecycle
요청이 왔을 때 NestJS에서의 lifecycle을 보면 다음과 같다.
1. Requset
2. Middleware
2.1. Globally bound middleware
2.2. Module bound middleware
3.Guards
4. Interceptor(컨트롤러 앞단)
4.1. Golbal interceptor
4.2 Controller interceptor
4.3 Route interceptor
5. Pipes
7. Service(존재한다면)
8. Interceptor(컨트롤러 뒷단)
8.1. Route interceptor
8.2 Controller interceptor
8.3 Global interceptor
9. Exception 📌 오늘 공부할 내용
9.1. Route
9.2. Controller
9.3. Global
10. Server response
Exception Filter란?
NestJS는 기본적으로 다양한 HTTP 예외 처리를 내장하고 있다.
그러나 기본적인 예외 처리 방식만으로는 복잡한 요구 사항을 충족하기 어려울 때가 있다.
이럴 때 Exception Filter를 활용하면 유연한 예외 처리를 구현할 수 있다.
이번 글에서는 Exception Filter가 무엇인지, 어떻게 사용하는지, 실제로 구현하는 방법까지 하나씩 살펴보겠다.
Nest 에는 애플리케이션에서 처리되지 않은 모든 예외를 처리하는 내장 예외 계층이 있다
애플리케이션 코드에서 예외를 처리하지 않으면 이 계층에서 이를 포착한 다음 적절한 에러 응답을 자동으로 보낸다.
NestJS는 예외 발생 시 자동으로 적절한 응답을 클라이언트에게 반환한다.
예를 들어, HttpException을 발생시키면 HTTP 상태 코드와 메시지가 포함된 JSON 응답이 반환된다.
에러
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
첫번째 인자(response) : string 또는 객체로 전달 (json응답에서 error로 반환)
두번째 인자(status) : 상태 코드 전달
세번째 인자(선택 사항) : 오류 원인 제공
응답
{
"statusCode": 403,
"message": "Forbidden"
}
이를 커스터마이징하고 싶다면 Exception Filter를 사용하면 된다.
필터 클래스 작성
Exception Filter를 구현하려면 @Catch() 데코레이터와 ExceptionFilter 인터페이스를 사용한다.
http.exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException) //@Catch()필터가 처리할 특정 예외 유형을 지정, 특정 예외를 지정하지 않으면 모든 예외를 처리하는 필터를 구현할 수 있다.
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp(); // context -> 실행 환경
const response = ctx.getResponse<Response>(); //실행 환경에 대한 response/request
const request = ctx.getRequest<Request>();
const status = exception.getStatus(); //예외에서 가져온 status
const error = exception.getResponse(); //예외에서 가져온 response
//express의 res.status(400).send({abcd}) 문법과 유사
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message || null,
error,
});
}
}
필터 등록
이 필터를 특정 컨트롤러 또는 글로벌하게 등록할 수 있다.
특정 컨트롤러에 적용
특정 컨트롤러에만 적용하려면 @UseFilters() 데코레이터를 사용해서 해당하는 필터를 인자로 넘겨주면 된다.
이렇게 되면 해당 함수 안에서 exception발생 시 해당 exception에 대한 내용이 필터로 전달되어 반환.
@Post()
@UseFilters(HttpExceptionFilter) // 함수 또는 컨트롤러 위에 사용
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException(); //이런식으로 해도 되고
throw new HttpException('api broken', 401); //이런 식으로 예외를 던지면 필터단에서 exception.getResponse로 에러메세지를 꺼내 쓸 수도 있다.
}
전역으로 적용
네스트 자체에서 에러처리
앱 전체에서 사용할 필터는 main.ts에서 userGlobalFilter를 통해 전역으로 등록할 수 있다.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter()); //app에 대해 필터링 추가
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
=> 응답
{
"success" : false,
"timestamp" : "2021~~~".
"path" : "/cats",
"error" : {
"statusCode" : 404,
"message" : "Cannot GET /cats",
"error" : "Not Found"
}
}
요 에러가 object로 전달된다.
이걸 커스텀 하고 싶으면 필터단에서 이런 식으로 에러를 string으로 처리해서 사용해줄 수도 있다.
const error = exception.getResponse() as
| string
| { error: string; statusCode: number; message: string | string[] }; // 메세지가 배열로 찍히는 경우도 있기 때문에
if (typeof error === 'string') { //에러가 스트링일 경우(직접 만들어준경우)
response.status(status).json({
success: false,
timestamp: new Date().toISOString(),
path: request.url,
error,
});
} else { //에러가 객체일 경우(nest자체에서 발생)
response.status(status).json({
success: false,
timestamp: new Date().toISOString(),
...error,
});
}
}
-> 응답
{
"success" : false,
"timestamp" : "2021~~~".
"path" : "/cats",
"error" : "Not Found"
}
참고
NestJS 공식문서 - Exception filters
'NestJS' 카테고리의 다른 글
[NestJS] Pipes (2) | 2024.12.02 |
---|---|
[NestJS] Middleware (0) | 2024.11.21 |
[NestJS] Provider, Module (0) | 2024.11.20 |
[NestJS] Controller (0) | 2024.11.20 |
[Express] 라우터 분리, 모듈화 (0) | 2024.11.14 |