[Spring] Exception Handler

배창민·2025년 10월 16일
post-thumbnail

Exception Handler

스프링 MVC는 컨트롤러에서 발생한 예외를 DispatcherServlet → HandlerExceptionResolver 체인으로 위임해 처리한다. 상황에 맞는 전략을 선택해 화면 응답, 상태 코드 지정, 전역 처리를 구현할 수 있다.


1) HandlerExceptionResolver 종류와 역할

Resolver핵심 역할
SimpleMappingExceptionResolver예외 타입별로 에러 뷰 이름 매핑 후 forward
DefaultHandlerExceptionResolver스프링이 제공하는 기본 예외 처리(표준 스프링 예외 → 상태 코드 매핑 등)
ResponseStatusExceptionResolver@ResponseStatus 가 붙은 예외를 HTTP 상태 코드로 변환
ExceptionHandlerExceptionResolver@ExceptionHandler 메서드를 찾아 컨트롤러(또는 전역)에서 처리

여러 Resolver가 함께 등록되어 있으며, 순서대로 시도되어 먼저 처리 가능한 곳이 책임진다.


2) 컨트롤러 지역 처리: @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 등 유연하게 가능

3) 전역 처리: @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";
    }
}
  • 범위: 애플리케이션 전역
  • 장점: 중복 제거, 일관된 에러 UX 제공
  • 구성 팁: 책임(도메인/레이어)별로 클래스 분리해 관리

4) 상태 코드 응답: 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 "/";
    }
}
  • 용도: REST 스타일의 간단한 오류 응답
  • 특징: 별도 뷰 없이 상태 코드 + reason 전송

5) 뷰 매핑 방식: 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;
    }
}
  • 장점: 설정만으로 손쉽게 뷰 매핑
  • 제약: 세밀한 로직은 어렵고, 정적 매핑이 주 목적

6) 구현 가이드

  • 지역 vs 전역

    • 화면 특화/도메인 전용이면 @ExceptionHandler(컨트롤러 내부)
    • 일괄/공통이면 @ControllerAdvice
  • 뷰 vs 상태 코드

    • 페이지 렌더링 목적이면 뷰 반환
    • API/REST 목적이면 @ResponseStatus 또는 ResponseEntity
  • 포괄 처리

    • 마지막에 @ExceptionHandler(Exception.class)기본 처리 준비
  • 뷰 네이밍

    • 에러 타입별 템플릿을 분리해 유지보수성을 높인다. 예) error/4xx.html, error/5xx.html, error/memberRegist.html

7) 최소 예시 모음

// 컨트롤러 지역 처리
@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"; }
}

핵심 요약

  • 예외 처리는 Resolver 체인으로 진행되며, 상황에 맞는 전략을 고른다.
  • 컨트롤러별로는 @ExceptionHandler, 전역으로는 @ControllerAdvice.
  • 상태 코드 중심이면 @ResponseStatus, 뷰 매핑 중심이면 SimpleMappingExceptionResolver.
  • 마지막 안전망으로 Exception.class 포괄 처리를 둔다.
profile
개발자 희망자

0개의 댓글