예외처리

seongha_h·2024년 5월 8일

Spring

목록 보기
2/6

spring boot는 다음과 같은 기본적인 에러페이지를 제공하고 있습니다.
기본적으로 Exception이 발생하고 별다른 처리를 하지 않으면 기본으로 등록된 다음과같은 error.html 또는 error.jsp 파일에 맵핑되어 에러 페이지가 보여지게 됩니다.
RESTful API 요청을 만들어 보냈다면 스프링은 다음 그림과 같은 화이트라벨 에러 페이지를 보여줍니다.

또한, error.html을 수정하여 원하는 정보를 담은 에러페이지를 만들 수도 있습니다.


HTTP Status Code

스프링에서 핸들링되지 않은 예외들은 보통 500에러로 응답하게 됩니다.
그러나 @ResponseStatus 어노테이션을 통해 사용자가 원하는 에러(상태코드와 메세지)로 만들수 있습니다.
그리고 이 에러는 다른곳에서 처리되지 않는 이상 해당 상태코드와 메세지로 응답합니다.

 @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order")  // 404
 public class OrderNotFoundException extends RuntimeException {
     // ...
 }

@ExceptionHandler

@ExceptionHandler 어노테이션을 통해 해당 컨트롤러에서 발생하는 에러를 공통으로 처리할 수 있습니다.

하지만 범위가 해당 컨트롤러만 해당됩니다.
따라서 컨트롤러 마다 @ExceptionHandler를 작성해야 하기에 코드의 중복이 발생합니다.

@Controller
public class ExceptionHandlingController {

  // @RequestHandler methods
  ...

  // Exception handling methods
 @ExceptionHandler({SQLException.class,DataAccessException.class})
  public String databaseError() {
    return "databaseError";
  }


  @ExceptionHandler(Exception.class)
  public ModelAndView handleError(HttpServletRequest req, Exception ex) {
    logger.error("Request: " + req.getRequestURL() + " raised " + ex);

    ModelAndView mav = new ModelAndView();
    return mav;
  }
}

주의할 점

  1. 예외 클래스 설정.
    위 코드에서 Exception.class는 모든 예외의 조상입니다.
    따라서 DataAccessException.class 은 Exception.class의 자식입니다.
    이 경우에 DataAccessException 이 발생한다면 두 핸들러 모두 호출 대상이됩니다.
    하지만, 자세한 것일 수록 우선순위가 높기 때문에 DataAccessException 핸들러가 호출되게 됩니다.
  2. 모델에 예외 정보 추가할 때.
    모델에 예외를 추가할 때는 주의해야 합니다.
    사용자는 자바 예외 세부 정보와 스택 추적이 포함된 웹 페이지는 필요하지도 않고, 보안상의 이슈가 될 수 있습니다.
    따라서 위의 그림과 같은 화이트라벨 오류 페이지를 반드시 재정의해야 합니다.

Controller Advice

Controller Advice는 ExceptionHandler를 전체 컨트롤러에 적용하는 것입니다. 마치 인터셉터 처럼 동작한다고 이해하면 될 것입니다.

또한, @ControllerAdvice와 @RestControllerAdvice는 Bean으로 등록되어 관리됩니다.

주의점

여러 ControllerAdvice가 있을 때 @Order어노테이션으로 순서를 지정하지 않는다면 Spring은 ControllerAdvice를 임의의 순서로 호출합니다. 즉, 사용자가 예상하지 못한 예외 처리가 발생할 수 있습니다.

basePackages
만약 여러 ControllerAdvice를 세분화하고 싶다면 basePackages 속성을 이용할 수 있습니다.

작성된 패키지와 하위 패키지에서 발생하는 예외는 해당 ControllerAdvice에서 처리하도록 지정할 수 있습니다.

assignableTypes
assignableTypes 속성을 이용하면 클래스 단위로도 ControllerAdvice를 적용할 수 있습니다.

@ControllerAdvice(basePackages = {"com.example.demo"})
// @ControllerAdvice(assignableTypes ={SimpleController.class})

class GlobalDefaultExceptionHandler {
	@ResponseStatus(HttpStatus.CONFLICT)  // 409
    @ExceptionHandler(DataIntegrityViolationException.class)
    public void handleConflict() {
        // Nothing to do
    }
}



더 자세히 보기

HandlerExceptionResolver

HandlerExceptionResolver 는 MVC 시스템에서 발생하는 모든 예외를 차단
하고 처리하는 데 사용됩니다.

public interface HandlerExceptionResolver {
    ModelAndView resolveException(HttpServletRequest request, 
            HttpServletResponse response, Object handler, Exception ex);
}

HandlerExceptionResolver는 내부에 3가지 ExceptionResolver들을 사용하고, 우선순위는 다음과 같습니다.

우선순위

1번부터 예외처리가 체인화 되어 진행됩니다.

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

내부적으로 스프링은 이 작업을 수행할 전용 빈(HandlerExceptionResolverComposite)을 만듭니다.

ExceptionHandlerExceptionResolver

@ExceptionHandler를 이용하여 예외처리를 진행합니다.
ExceptionResolver 중 우선순위가 가장높고 위에서 살펴본 방식이 이 방식입니다.

ResponseStatusExceptionResolver

@ResponseStatus 를 이용한 예외처리를 진행합니다.

DefaultHandlerExceptionResolver

표준 스프링 예외를 변환하여 HTTP 상태 코드로 변환합니다.(Spring MVC 내부)
/error.html 에 해당합니다.


결론적으로 위와 같은 우선순위로 에러처리가 동작하게 됩니다.
사용자는 이 우선순위에 따라 예외 처리를 만들어서 사용할 수 있습니다.

실무에서는 @ExceptionHandler 와 @ControllerAdvice를 이용한 방식을 가장 많이 사용한다고 합니다.




참고

https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
https://tecoble.techcourse.co.kr/post/2023-05-03-ExceptionHandler-ControllerAdvice/
https://parkmuhyeun.github.io/woowacourse/2023-04-19-Exception-Handler/

profile
https://github.com/Fixtar

0개의 댓글