while(1) work();
반응형

개요

비즈니스 로직을 작성하다 보면 예외를 발생시켜야 할 경우가 생긴다.

Handler나 Interceptor와 같이 DispatcherServlet 내에서 발생한 예외는 Spring Web MVC를 통해 처리할 수 있다.

(물론 Handler나 Interceptor 내에서 catch 절을 통해 처리할 수도 있다. 처리하지 못하고 throw 된 예외를 의미한다.)

HandlerExceptionResolver

DispatcherServlet은 초기화 시점에 HandlerExceptionResolver 인터페이스를 구현한 빈을 찾아 handlerExceptionResolvers에 추가해둔다.

//org.springframework.web.servlet.DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {

    List<HandlerExceptionResolver> handlerExceptionResolvers;

    private void initHandlerExceptionResolvers(ApplicationContext context) {
        //ApplicationContext에서 HandlerExceptionResolver 구현체를 찾아 handlerExceptionResolvers에 추가하는 코드
    }

}

따라서 HandlerExceptionResolver 인터페이스를 구현하면 예외를 처리하는 로직을 작성할 수 있다.

public interface HandlerExceptionResolver {

	@Nullable
	ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}

resolveException 메서드는 예외 처리가 가능한 경우 적절한 ModelAndView를 만들어 반환해주면 된다.

만약 처리가 불가능한 예외인 경우 null을 리턴해야 하며, DispatcherServlet에 등록된 다른 HandlerExceptionResolver 구현체가 이를 처리하게 된다.

org.springframework.core.Ordered 인터페이스를 구현하면 우선순위(값이 낮을수록 먼저 처리)를 지정할 수 있다.

또, 이전 글에서 설명한 바와 같이, 기본적으로는 filter 에서 발생한 예외는 처리할 수 없다.

기본 HandlerExceptionResolver 구현체

Spring Web MVC는 이미 4개의 HandlerExceptionResolver 구현체를 만들어두었으며, 이 중 3개의 구현체가 기본적으로 등록된다. (SimpleMappingExceptionResolver 제외)

따라서 일반적으로 HandlerExceptionResolver를 직접 구현하기 보다 구현체를 사용해 예외를 처리한다.

  • ExceptionHandlerExceptionResolver : @ExceptionHandler와 @ControllerAdvice 어노테이션을 이용해 예외 처리 (가장 많이 사용)
  • ResponseStatusExceptionResolver : @ResponseStatus를 이용해 예외처리
  • DefaultHandlerExceptionResolver : Spring MVC 내부 예외처리 (사용하지 않음)
  • SimpleMappingExceptionResolver : 예외 클래스명과 오류 페이지를 맵핑해두고 예외 발생 시 오류 페이지 View(ModelAndView) 반환

ExceptionHandlerExceptionResolver

@ExceptionHandler

Controller 클래스 내부에 @ExceptionHandler 어노테이션을 가진 메서드를 작성함으로써, 해당 클래스의 handler에서 발생한 예외를 공통 처리할 수 있다.

@Controller
public class CommentController {

    @GetMapping("/throwing")
    public String throwing(HttpServletResponse resp) {
        throw new IllegalArgumentException("Something wrong..");
    }

    @ExceptionHandler({IllegalArgumentException.class})
    public ModelAndView handle(IllegalArgumentException ex, Model model) {
        System.out.println("CommentController.handle() called");

        ModelAndView response = new ModelAndView("/WEB-INF/views/error.jsp");
        response.setStatus(HttpStatus.BAD_REQUEST);
        model.addAttribute("message", ex.getMessage());

        return response;
    }

}

아래는 error.jsp 코드이다.

<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"%>
<!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>ERROR</title>
    </head>
    <body style="text-align:center;">
        <div style="font-size:100px">ERROR!</div>
        <div>${message}</div>
    </body>
</html>

@ControllerAdvice

공통적으로 발생하는 예외를 클래스 내부가 아니라 여러 클래스를 통틀어 한번에 처리하고자 할 경우 @ControllerAdvice 어노테이션을 가진 클래스를 만들어 예외를 일괄적으로 처리할 수 있다.

package com.lickthespring.web.error;

import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class MyControllerAdvice {
    
    @ExceptionHandler({RuntimeException.class})
    public ModelAndView handle(RuntimeException ex, Model model) {
        System.out.println("MyControllerAdvice.handle() called");

        ModelAndView response = new ModelAndView("/WEB-INF/views/error.jsp");
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
        model.addAttribute("message", ex.getMessage());

        return response;
    }

}
Controller
public class CommentController {

    @GetMapping("/throwing2")
    public String throwing2(HttpServletResponse resp) {
        throw new RuntimeException("Something wrong..");
    }

}

메서드 인수와 반환 타입

RequestMappingHandlerAdapter와 마찬가지로 ExceptionHandlerExceptionResolver도 가변 메서드 인수와 다양한 반환 타입을 지원한다.

지원하는 메서드 인수와 반환 타입은 RequestMappingHandlerAdapter와 유사하므로 자세한 설명은 생략하고 아래 링크로 대체한다.

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler-args

반응형
profile

while(1) work();

@유호건

❤️댓글은 언제나 힘이 됩니다❤️ 궁금한 점이나 잘못된 내용이 있다면 댓글로 남겨주세요.

검색 태그