[Spring] 예외처리

2025. 3. 27. 17:33·개발/Spring

1. 기본 try-catch 블록

  • 가장 기본적인 자바의 예외 처리 방식.
  • 메소드 내에서 특정 로직 수행 중 발생할 수 있는 예외를 직접 잡아서 처리.
  • 단점: 컨트롤러 메소드마다 유사한 try-catch 코드가 반복될 수 있어 코드가 지저분해지고 유지보수가 어려워질 수 있다.

2. 스프링의 데이터 접근 예외(Data Access Exception) 계층

  • 스프링은 데이터 접근 기술(JDBC, JPA, Hibernate 등)에 상관없이 일관된 방식으로 예외를 처리할 수 있도록 DataAccessException이라는 런타임 예외 계층을 제공.
  • 각 데이터 접근 기술에서 발생하는 고유한 예외(예: SQLException)를 스프링이 DataAccessException의 하위 예외 (예: DataIntegrityViolationException, DataAccessResourceFailureException 등)로 변환하여 던져준다.
  • 장점: 개발자는 특정 데이터 접근 기술에 종속되지 않고, 스프링이 제공하는 추상화된 예외를 사용하여 데이터 관련 오류를 처리할 수 있다. 서비스 계층 등에서 기술 독립적인 예외 처리가 가능해진다.

3. @ExceptionHandler 애노테이션 (컨트롤러 레벨)

  • 특정 컨트롤러 클래스 내에서 발생하는 예외를 처리하는 메소드를 지정할 때 사용.
  • 해당 컨트롤러 내에서 지정된 타입의 예외가 발생하면 @ExceptionHandler가 붙은 메소드가 호출됨.
  • 장점: 컨트롤러 내에서 발생하는 특정 예외에 대한 처리를 해당 컨트롤러 내에 응집시킬 수 있다.
  • 단점: 여러 컨트롤러에서 동일한 예외 처리 로직이 필요할 경우 코드 중복이 발생.
Java
 
@Controller
public class MyController {

    @GetMapping("/some-resource")
    public String getResource() {
        if (/* some error condition */) {
            throw new MyCustomException("Something went wrong!");
        }
        return "successView";
    }

    // MyCustomException 발생 시 이 메소드가 처리
    @ExceptionHandler(MyCustomException.class)
    public ResponseEntity<String> handleMyCustomException(MyCustomException ex) {
        // 로깅, 오류 응답 생성 등
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
    }

    // 다른 종류의 예외 처리
    @ExceptionHandler(Exception.class)
    public String handleGenericException(Exception ex, Model model) {
        model.addAttribute("errorMessage", "An unexpected error occurred.");
        return "errorView"; // 오류 페이지 뷰 이름 반환
    }
}

 

4. @ControllerAdvice / @RestControllerAdvice (글로벌 레벨)

  • 여러 컨트롤러에 걸쳐 전역적으로 발생하는 예외를 처리하기 위한 클래스를 지정할 때 사용.
  • @ControllerAdvice는 주로 View를 반환하는 경우, @RestControllerAdvice는 @ControllerAdvice와 @ResponseBody가 합쳐진 것으로 주로 REST API에서 JSON/XML 등의 응답 본문을 반환하는 경우 사용됨.
  • 이 클래스 내부에 @ExceptionHandler를 사용하여 특정 예외를 처리하는 메소드를 정의.
  • 장점: 애플리케이션 전반의 예외 처리 로직을 한 곳에서 관리하여 중복을 제거하고 일관성을 유지할 수 있다. 특정 패키지나 특정 애노테이션이 붙은 컨트롤러만 대상으로 지정할 수도 있다.
  • 가장 권장되는 방식 중 하나.
Java
 
@RestControllerAdvice // 또는 @ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
        ErrorResponse response = new ErrorResponse("INVALID_INPUT", ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

    @ExceptionHandler(NoSuchElementException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND) // HTTP 상태 코드를 직접 지정할 수도 있음
    public ErrorResponse handleNoSuchElementException(NoSuchElementException ex) {
        return new ErrorResponse("RESOURCE_NOT_FOUND", ex.getMessage());
    }

    // 처리되지 않은 모든 예외를 처리 (가장 마지막에 위치시키는 것이 좋음)
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
        // 중요한: 실제 운영 환경에서는 보안 상 스택 트레이스 전체를 노출하지 않도록 주의
        ErrorResponse response = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred.");
        // 로깅은 필수!
        log.error("Unhandled exception occurred:", ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
    }

    // 필요에 따라 ErrorResponse DTO 정의
    @Getter
    @RequiredArgsConstructor
    static class ErrorResponse {
        private final String code;
        private final String message;
    }
}

 

5. HandlerExceptionResolver 인터페이스

  • 스프링 MVC에서 예외 처리를 위한 핵심 전략 인터페이스.
  • 컨트롤러(핸들러) 실행 중 발생한 예외를 받아 처리하고, 그 결과로 ModelAndView (오류 페이지 렌더링), HttpServletResponse 직접 조작 (상태 코드 설정 등), 또는 null (다른 Resolver에게 처리 위임) 등을 반환할 수 있다.
  • 스프링은 기본적으로 여러 HandlerExceptionResolver 구현체를 등록하여 사용.
    • ExceptionHandlerExceptionResolver: @ExceptionHandler 애노테이션을 처리. (가장 높은 우선순위)
    • ResponseStatusExceptionResolver: @ResponseStatus 애노테이션이 달린 예외나 ResponseStatusException을 처리하여 HTTP 상태 코드를 설정.
    • DefaultHandlerExceptionResolver: 스프링 내부의 특정 예외를 HTTP 상태 코드로 변환. (예: MissingServletRequestParameterException -> 400 Bad Request)
  • 개발자가 직접 HandlerExceptionResolver를 구현하여 커스텀 예외 처리 로직을 추가할 수도 있지만, 보통은 @ControllerAdvice를 사용하는 것이 더 간편하고 일반적.

6. @ResponseStatus 애노테이션

  • 사용자 정의 예외 클래스에 이 애노테이션을 붙이면, 해당 예외가 발생했을 때 지정된 HTTP 상태 코드와 이유메시지를 응답에 설정.
  • ResponseStatusExceptionResolver가 이 애노테이션을 처리.
  • @ExceptionHandler 메소드에도 사용하여 해당 메소드가 처리하는 예외에 대한 기본 응답 상태를 지정할 수 있다.
Java
 
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "요청한 리소스를 찾을 수 없습니다.")
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

7. 스프링 부트의 기본 오류 처리 (BasicErrorController)

  • 스프링 부트는 별도의 예외 처리를 설정하지 않았을 경우를 대비해 기본적인 오류 처리 기능을 자동 구성.
  • BasicErrorController가 /error 경로로의 요청을 처리.
  • 요청의 Accept 헤더에 따라 HTML 오류 페이지(주로 src/main/resources/templates/error.html 등)를 보여주거나, JSON 형태의 오류 정보를 응답.
  • application.properties 또는 application.yml에서 server.error.whitelabel.enabled=false 등으로 일부 동작을 제어하거나, ErrorAttributes 빈을 커스터마이징하여 오류 응답 내용을 변경할 수 있다.

예외 처리 전략 및 Best Practice

  1. 전역 처리 우선: @ControllerAdvice 또는 @RestControllerAdvice를 사용하여 애플리케이션 전반의 예외 처리 로직을 중앙에서 관리하는 것이 좋다. 코드 중복을 줄이고 일관성을 확보할 수 있다.
  2. 구체적인 예외 먼저 처리: @ExceptionHandler 메소드를 정의할 때는 구체적인 예외 타입을 먼저 처리하고, 그 부모 타입이나 Exception과 같은 일반적인 예외는 나중에 처리하도록 순서를 고려.
  3. 커스텀 예외 활용: 비즈니스 로직 상 의미있는 예외 상황에 대해서는 명확한 이름의 커스텀 예외 클래스를 정의하여 사용하는 것이 가독성과 유지보수 측면에서 유리.
  4. 적절한 HTTP 상태 코드 반환: REST API의 경우, 예외 상황에 맞는 HTTP 상태 코드(4xx, 5xx 등)를 반환하는 것이 중요. @ResponseStatus, ResponseEntity 등을 활용.
  5. 사용자 친화적인 오류 메시지: 최종 사용자에게 노출되는 오류 메시지는 이해하기 쉽고 문제 해결에 도움이 되는 정보를 제공해야 한다. 상세한 기술 정보나 스택 트레이스는 사용자에게 직접 노출하지 않도록 주의.
  6. 충분한 로깅: 예외 발생 시 원인 분석을 위해 반드시 로그를 남겨야 한다. 스택 트레이스 전체와 함께 예외 발생 시점의 주요 파라미터, 사용자 정보 등 컨텍스트 정보를 포함하는 것이 좋다. (Logback, Log4j2 등 로깅 프레임워크 사용)
  7. 트랜잭션 고려: 서비스 계층 등에서 예외 발생 시 트랜잭션 롤백 처리가 제대로 이루어지는지 확인해야 한다. 스프링의 @Transactional은 기본적으로 런타임 예외 발생 시 롤백을 수행.

 

저작자표시 비영리 변경금지 (새창열림)

'개발 > Spring' 카테고리의 다른 글

[Spring] @valid  (0) 2025.03.27
[Spring] 컨트롤러 반환타입  (0) 2025.03.27
[Spring]Model vs RedirectAttributes  (0) 2025.03.16
[Spring] Spring MVC 주요 어노테이션  (0) 2025.03.16
[Spring] SpEL  (0) 2025.03.16
'개발/Spring' 카테고리의 다른 글
  • [Spring] @valid
  • [Spring] 컨트롤러 반환타입
  • [Spring]Model vs RedirectAttributes
  • [Spring] Spring MVC 주요 어노테이션
함수형 인간
함수형 인간
잘 까먹는 개발자의 두뇌 확장 장치
  • 함수형 인간
    개발 기록 노트
    함수형 인간
  • 글쓰기 관리
  • 전체
    오늘
    어제
    • 글 목록 (84)
      • 기타 (1)
      • 개발 (82)
        • Java (6)
        • Javascript (1)
        • Spring (20)
        • jQuery (0)
        • Git (0)
        • servlet (11)
        • JSP (6)
        • HTML (0)
        • CSS (10)
        • SQL (9)
        • JSTL (3)
        • Mybatis (4)
        • Design Patterns (0)
        • HTTP (2)
        • Devtools (0)
        • IntelliJ (5)
        • JDBC (1)
        • Lombok (3)
        • Logging (1)
      • 책 리뷰 (0)
  • hELLO· Designed By정상우.v4.10.3
함수형 인간
[Spring] 예외처리
상단으로

티스토리툴바