
스프링 MVC는 컨트롤러에서 발생한 예외를 DispatcherServlet → HandlerExceptionResolver 체인으로 위임해 처리한다. 상황에 맞는 전략을 선택해 화면 응답, 상태 코드 지정, 전역 처리를 구현할 수 있다.
| Resolver | 핵심 역할 |
|---|---|
| SimpleMappingExceptionResolver | 예외 타입별로 에러 뷰 이름 매핑 후 forward |
| DefaultHandlerExceptionResolver | 스프링이 제공하는 기본 예외 처리(표준 스프링 예외 → 상태 코드 매핑 등) |
| ResponseStatusExceptionResolver | @ResponseStatus 가 붙은 예외를 HTTP 상태 코드로 변환 |
| ExceptionHandlerExceptionResolver | @ExceptionHandler 메서드를 찾아 컨트롤러(또는 전역)에서 처리 |
여러 Resolver가 함께 등록되어 있으며, 순서대로 시도되어 먼저 처리 가능한 곳이 책임진다.
@ExceptionHandler특정 컨트롤러 내부에서 발생한 예외를 해당 컨트롤러 클래스 내에서 처리한다.
@GetMapping("controller-null")
public String nullPointerExceptionTest() {
String str = null;
str.charAt(0); // NullPointerException 발생
return "/";
}
@GetMapping("controller-user")
public String userExceptionTest() throws MemberRegistException {
throw new MemberRegistException("회원으로 받을 수 없습니다!");
}
// 같은 컨트롤러 클래스 내부
@ExceptionHandler(NullPointerException.class)
public String handleNpe() {
return "error/nullPointer";
}
@ExceptionHandler(MemberRegistException.class)
public String handleUserEx(Model model, MemberRegistException ex) {
model.addAttribute("exception", ex);
return "error/memberRegist";
}
ModelAndView, ResponseEntity 등 유연하게 가능@ControllerAdvice + @ExceptionHandler여러 컨트롤러에서 발생하는 예외를 한 곳에서 일괄 처리한다.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NullPointerException.class)
public String handleNpe() {
return "error/nullPointer";
}
@ExceptionHandler(MemberRegistException.class)
public String handleUserEx(Model model, MemberRegistException ex) {
model.addAttribute("exception", ex);
return "error/memberRegist";
}
// 기본(포괄) 처리
@ExceptionHandler(Exception.class)
public String handleDefault(Exception ex) {
return "error/default";
}
}
ResponseStatusExceptionResolver예외 클래스에 @ResponseStatus 를 지정하면 해당 예외 발생 시 상태 코드와 reason을 자동으로 응답한다.
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "리소스를 찾을 수 없습니다.")
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) { super(message); }
}
@Controller
public class ResourceController {
@GetMapping("/resource/{id}")
public String getResource(@PathVariable int id) {
if (id <= 0) throw new ResourceNotFoundException("유효하지 않은 리소스 ID");
return "/";
}
}
SimpleMappingExceptionResolver예외 → 뷰 이름 매핑을 설정 기반으로 처리한다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("java.lang.Exception", "error");
mappings.put("java.lang.RuntimeException", "runtimeError");
resolver.setExceptionMappings(mappings);
resolver.setDefaultErrorView("defaultError");
resolver.setExceptionAttribute("exception"); // 뷰에서 사용할 모델 키
return resolver;
}
}
지역 vs 전역
@ExceptionHandler(컨트롤러 내부)@ControllerAdvice뷰 vs 상태 코드
@ResponseStatus 또는 ResponseEntity포괄 처리
@ExceptionHandler(Exception.class) 로 기본 처리 준비뷰 네이밍
error/4xx.html, error/5xx.html, error/memberRegist.html// 컨트롤러 지역 처리
@ExceptionHandler(IllegalArgumentException.class)
public String handleBadRequest(Model model, IllegalArgumentException ex) {
model.addAttribute("message", ex.getMessage());
return "error/400";
}
// 전역 기본 처리
@ControllerAdvice
class GlobalEx {
@ExceptionHandler(Exception.class)
public String defaultError() { return "error/default"; }
}
@ExceptionHandler, 전역으로는 @ControllerAdvice.@ResponseStatus, 뷰 매핑 중심이면 SimpleMappingExceptionResolver.Exception.class 포괄 처리를 둔다.