RestControllerAdvice

개발새발·2023년 1월 8일
0

spring

목록 보기
15/26
post-custom-banner

Spring 개발을 하는데 분명 422 에러코드를 뱉어야하는데 400에러를 뱉는 것이다. 그래서 머임 ㅡㅡ 킹받네.. 하다가 찾아보니 다른분이 GlobalExceptionHandler라는 exceptionhandler를 @RestControllerAdvice 로 만들어놨다.


그럼 여기서 궁금해진 게 있었다. 스프링의 기본 흐름 처리가 어떻게 되더라? 🤔

WAS는 스프링 부트가 등록한 에러 설정(/error)에 맞게 요청을 전달한다고 한다. 기본적인 에러 처리 방식은 서비스를 위한 컨트롤러 이후에 에러 컨트롤러에서 한번 더 호출하게 된다.

WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러
-> 컨트롤러(예외발생) -> 인터셉터 -> 서블릿(디스패처 서블릿) -> 필터 -> WAS(톰캣)
-> WAS(톰캣) -> 필터 -> 서블릿(디스패처 서블릿) -> 인터셉터 -> 컨트롤러(BasicErrorController)

그러면 에러 처리 흐름은? 🤨

예외 처리기들은 스프링의 빈으로 등록되어 있고, 예외가 발생하면 순차적으로 다음의 Resolver들이 처리가능한지 판별한 후에 예외가 처리된다. 에러 흐름은 아래와 같다.

  1. ExceptionHandlerExceptionResolver
    : 에러 응답을 위한 Controller나 ControllerAdvice에 있는 ExceptionHandler를 처리한다
  2. ResponseStatusExceptionResolver
    : Http 상태 코드를 지정하는 @ResponseStatus 또는 ResponseStatusException를 처리한다.
  3. DefaultHandlerExceptionResolver
    : 스프링 내부의 기본 예외들을 처리한다.

내가 알고싶은 건 @RestControllerAdvice !

  • 정의
    ControllerAdvice는 여러 컨트롤러에 대해 전역적으로 ExceptionHandler를 적용해준다. ControllerAdvice가 선언된 클래스는 스프링 빈으로 등록된다. 때문에 전역적으로 에러를 핸들링하는 클래스를 만들어 어노테이션을 붙여주면 에러 처리를 위임할 수 있다.
    Spring은 스프링 예외를 미리 처리해둔 ResponseEntityExceptionHandler를 추상 클래스로 제공하고 있다. ResponseEntityExceptionHandler에는 스프링 예외에 대한 ExceptionHandler가 모두 구현되어 있으므로 ControllerAdvice 클래스가 이를 상속받게 하면 된다. 만약 이 추상 클래스를 상속받지 않는다면 스프링 예외들은 DefaultHandlerExceptionResolver가 처리하게 되는데, 그러면 예외 처리기가 달라지므로 클라이언트가 일관되지 못한 에러 응답을 받지 못하므로 ResponseEntityExceptionHandler를 상속시키는 것이 좋다.

  • 장점
    하나의 클래스로 모든 컨트롤러에 대해 전역적으로 예외 처리가 가능하다
    에러 응답에 일관성있다 별도의 try-catch문이 없어 코드의 가독성이 높아진다

  • 주의할 점
    한 프로젝트당 하나의 ControllerAdvice만 관리하는 것이 좋다
    만약 여러 ControllerAdvice가 필요하다면 basePackages나 annotations 등을 지정해야 한다
    직접 구현한 Exception 클래스들은 한 공간에서 관리한다

  • 사용법

서비스를 위한 컨트롤러

 @PostMapping("/test")
 public TestResponse addToProduct(
 @RequestBody @Validated(ValidatorGroup.Tag.class) Dto dto,
 HttpServletRequest request) {
 		myTagService.addTest( dto);
 		return TestResponse.ok( ResultCode.SUCCESS_TAG ,dto);
 }

exception handler

 @RestControllerAdvice
 public class GlobalExceptionHandler
 
 @ExceptionHandler(RuntimeException.class)
 @ResponseStatus(HttpStatus.BAD_REQUEST)
 public CollectionResponse handleRuntimeException(RuntimeException ex, HttpServletRequest request) {
 	return CollectionResponse.httpError(HttpResult.BAD_REQUEST);
 }

참고
https://mangkyu.tistory.com/204 (이분한테 다 배움..)

profile
발새발개
post-custom-banner

0개의 댓글