🌿SPRING/🌱연습[SPRING]

[SPRING] AOP 예외 처리 / Global Exception Handler 전역 예외 처리

디카페인라떼 2022. 10. 6. 12:07

2022.09.01 - [SPRING] - [SPRING] AOP 개념

 

[SPRING] AOP 개념

참고블로그 우아한테크 유튜브 Aspect Oriented Programming 관점지향프로그래밍 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점에서 나누어서 보고 그 관점을 기준으로 각각 모듈화 하겠다는 것

wearegolden.tistory.com

더보기

매번 코드를 짤때마다 그리고 유효성 검증을 추가할때마다 try/catch 문을 사용하다보니 가독성이 낮아지는 게 느껴졌다.

다들 어떻게 저렇게 깔끔하게 짜는 걸까 했는데 전역 예외처리를 했기 때문이었다.

 

이를 위해서 알아야 할 개념

  1. @ControllerAdvice 와 @RestControllerAdvice
  2. @ExceptionHandler
  3. Spring의 예외 처리 흐름

 


@ControllerAdvice 와 @RestControllerAdvice
  • @ControllerAdvice 
    • 클래스에 선언만 하면 됨
    • 모든 @Controller에 대한 전역적 예외를 잡아서 처리
    • @Component가 선언되어있어 Bean으로 관리 되어 사용
  • @RestControllerAdvice
    •  @ControllerAdvice + @ResponseBody 
    • @ResponseBody 를 따로 붙여주지 않아도 객체 리턴함
@ExceptionHandler
  • 메소드에 선언하고 특정 예외 클래스를 지정해주면 해당 예외 발생 시 메소드에 정의한 로직을 처리 가능
    • 즉, 내가 처리하고 싶은 Exception을 정의한다음 (CustomException) 해당 예외 발생 시 원하는 대로 처리가 가능
  • Exception 클래스들을 속성으로 받아 처리할 예외를 지정
  • @ResponseStatus와 달리 에러 응답(payload)을 자유롭게 다룰 수 있다는 점에서 유연하다
Spring의 예외 처리 흐름

  • 예외가 발생하면 HandlerExceptionResolver가 동작하고 HandlerInterceptor를 통해서 DispatcherServlet을 통해 Response가된다

  1. HandlerExceptionResolver가 동작함
    1. 예외가 발생한 컨트롤러 안에 적합한 @ExceptionHandler가 있는지 검사함
    2. 컨트롤러의 @ExceptionHandler에서 처리가능하다면 처리하고, 그렇지 않으면 ControllerAdvice로 넘어감
    3. ControllerAdvice안에 적합한 @ExceptionHandler가 있는지 검사하고 없으면 다음 처리기로 넘어감
  2. ResponseStatusExceptionResolver가 동작함
    1. @ResponseStatus가 있는지 또는 ResponseStatusException인지 검사함
    2. 맞으면 ServletResponse의 sendError()로 예외를 서블릿까지 전달되고, 서블릿이 BasicErrorController로 요청을 전달함
  3. DefaultHandlerExceptionResolver가 동작함
    1. Spring의 내부 예외인지 검사하여 맞으면 에러를 처리하고 아니면 넘어감
  4. 적합한 ExceptionResolver가 없으므로 예외가 서블릿까지 전달되고, 서블릿은 SpringBoot가 진행한 자동 설정에 맞게 BasicErrorController로 요청을 다시 전달함

 


써먹어 보기
  • 기존에 만들어 둔 CustomException Class
@Getter
public class CustomErrorException extends RuntimeException {
  private final ErrorCode errorCode;

  public CustomErrorException(ErrorCode errorCode) {
    super(errorCode.getMessage());
    this.errorCode = errorCode;
  }
}
  • RuntimeException을 상속 받아 ErrorCode를 출력한다.

 

  • GlobalExceptionHandler
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

  @ExceptionHandler
  public ResponseEntity<ResponseDto<?>> customExceptionHandler(CustomErrorException exception) {
    log.error(exception.getMessage());
    return new ResponseEntity<>(ResponseDto.fail(exception.getErrorCode()), HttpStatus.BAD_REQUEST);
  }
}

👉 ResponseEntity<Object> 객체는 HTTP Status를 다르게해 Client에게 보낼때 사용하는 객체이다. 보낼 Object와 두 번째 인자로 아래와 같이 HTTP Status를 지정할 수 있다.

 ~ 끝 ~

...... handler 클래스 하나만으로 그동안에 계속 해왔던 try catch 문을 삭제해도 예외처리가 가능해졌다.

 

  • Before - Controller

  • After - Controller

한결 가독성이 좋아진걸 확인할 수 있다.