NestJS-예외처리

효딩딩·2023년 7월 18일
0

HTTPException

@Controller('cats')
export class CatsController {

@Get()
getAllCat() {  return 'get all cat api'}
  
}

-----------------------------------------------------
http://localhost:8000/cats/sdf
{
 "statusCode" : 404,
 "meessage" : "Catnot GET / 112", 
 "error" : "Not Found"  
  
}

위와 같이 에러가 발생할 때 nestjs만에 규칙으로 자동으로 만들어준다. 실제로 서비스에 사용할 때 추가로 필요한 정보값도 같이 에러코드에 보내고 싶은 경우 nest에서 Http에 대한 에러는 HttpException으로 처리한다.

throw new HttpException() // nestJS에서 제공해주는 인터페이스를 사용
@Controller('cats')
export class CatsController {

@Get()
getAllCat() {
 // (1) throw new HttpException('api is broken', 401) 
	throw new HttpException({ success: false, message: 'api is broken'}, 401) // 커스텀해서 오버라이딩을 해서 출력할 수 도 있다.
  	 return 'get all cat api'
}
}

-----------------------------------------------------
http://localhost:8000/cats
//  (1) {
//        "statusCode" : 401,
//         "meessage" : "api is broken", 
//      }

{
 "success": false
 "meessage" : "api is broken", 
  
}
  • 이렇게 강제 되어서 에러 메세지를 반환해준다.
  • nestJS에서 제공해주는 대로 에러를 처리해줄 수 있지만 서비스의 형태에 따라 에러를 핸들링 할때 커스텀을 해줄 필요가 있다.

에러형식 변경하기

  • requset가 있다면 exception 처리가 하나로 모여 재사용성을 고려해서 필터링을 거쳐서 response로 반환해주는 형식으로 만들기
  • 모든 파일에 공통적으로 사용할것이기 때문에
    common/exceptions/http-exception.filter.ts 예외처리 만들기
    http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
   // ctx (context) : 실행 환경에 대한 컨택스트
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>(); // response 가져오기 
    const request = ctx.getRequest<Request>(); //  request 가져오기
    const status = exception.getStatus();
   
    const error = exception.getResponse()

     // res.status(400).send({"에러"}) express 와 같은형식
// (1) response
//      .status(status)  // 위에 보내준 status 받아서 보내주고 
//      .json({     // 위에 send가 json으로 한정으로 표현해준다.
//        statusCode: status, 
//        timestamp: new Date().toISOString(),
//        path: request.url,
//      });
   
    
 // 에러 메세지도 같이 보내주고 싶을 경우 : 'api broken', exception 안에 getResponse() 메서드가 있다. 에러메세지 인자가 이안으로 전달되서 에러메세지가 찍힌다.    
    
       response
      .status(status)  // 위에 보내준 status 받아서 보내주고 
      .json({     // 위에 send가 json으로 한정으로 표현해준다.
        success : false,
        timestamp: new Date().toISOString(),
        path: request.url,
        error,  //  error: error, 키와 벨류가 같은 경우 벨류 생략한다.  
      }); 

  }
}

http-exception.filter.ts 적용하는 방법

1. 각각에 대해 적용하기

  • @UseFilters() 데코레이터를 이용하여 해당하는 필터를 인자로 넘겨주고 함수안에서 exception이 발생하면 exception에 대한 내용이 HttpExceptionFilter() 이 필터로 넘어가게 되서 반환이 된다.
    cats.controller.ts
import { HttpExceptionFilter } from 'src/common/exceptions/http-exception.filter';

@Controller('cats')
export class CatsController {

@Get()
@UseFilters(HttpExceptionFilter) 
getAllCat() {
	throw new HttpException('api broken', 401) 
  	 return 'get all cat api'
}
}
-------------------------------------
http://localhost:8000/cats  
- 필터링을 거쳤기 때문에 timestamp가 찍힌다. 

//  (1) {
//     "statusCode": 401,
//     "timestamp" : "2023-07-18-27T08:56:31.4472" 
//     "path" : "/cats",
//       }

 {
 "seccess": false,
 "timestamp" : "2023-07-18-27T08:56:31.4472" 
 "path" : "/cats", 
 "error" : "api broken"  
  
}
 
  • 각각의 controller에 @UseFilters()를 적용하는것
@Controller('cats')
export class CatsController {

@Get()
@UseFilters(HttpExceptionFilter)  <<
getAllCat() {
	throw new HttpException('api broken', 401) 
  	 return 'get all cat api'
}
}
  • 동일하게 다 쓰인다고 하면 class 위에 데코레이터로 사용할 수 있다.
@Controller('cats')
@UseFilters(HttpExceptionFilter)  <<
export class CatsController {

@Get()
getAllCat() {
	throw new HttpException('api broken', 401) 
  	 return 'get all cat api'
}
}

2. 전역에서 적용하기

  • 직접 만든 에러 말고 404 에러는 nest 자체에서 에러처리를 해주는데 이것은 글로벌에서 등록해서 처리해줄 수 있다.
    main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 글로벌로 전역에 등록을 해주면된다. 앱에 대해서 필터링을 하나 추가해준것임.
  app.useGlobalFilters(new HttpExceptionFilter()); 
  await app.listen(3000);
}
bootstrap();

http-exception.filter.ts

  • error 형식 커스텀하기
import {
  Catch,
  ExceptionFilter,
  HttpException,
  ArgumentsHost,
} from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();
    const error = exception.getResponse() as
      | string
    // message 같은 경우 string으로 찍힐 수도 있지만 배열로 찍힐 수도 있다.
      | { error: string; statusCode: number; message: string | string[] }; 
   // error 가 string 일 경우(인자값으로 넘겨주는 경우)와 아닐경우 분기처리하기  
    if (typeof error === 'string') {
      response.status(status).json({
        success: false,
        timestamp: new Date().toISOString(),
        path: request.url,
        error,
      });
    } else { 
      // 404에러 처럼 nest 자체에서 발생하는 에러 비구조할당을 통해 보내주기 
      response.status(status).json({
        success: false,
        timestamp: new Date().toISOString(),
        ...error,
      });
    }  
  }
}

---------------------------------
 {
 "seccess": false,
 "timestamp" : "2023-07-18-27T08:56:31.4472" 
 "statusConde" : 404,
 "message" : "Cannot GET /112", 
 "error" : "Not Found"  
  
}

미들웨어 → 컨트롤러 → 서비스 → 예외처리 순으로 사용자 응답에 도달하게 된다.

profile
어제보다 나은 나의 코딩지식

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

너무 좋은 글이네요. 공유해주셔서 감사합니다.

답글 달기