개발 공부/Java & Spring

스프링에서 예외처리를 하는 방법 (ExceptionHandler, RestControllerAdvice)

빵다희 2024. 8. 23. 00:47

💡주제 

예외처리를 위해 주렁 주렁 달은 try catch. 

catch 문 안에서 예외를 처리하다가 발생한 예외를 처리하기 위해 try catch 안에 try catch를 추가하는 상황이 발생하기도 한다.

가독성이 떨어져 결국 catch의 인자를 Exception으로 받아 모든 에러를 통으로 처리해버린다..

스프링 MVC 환경에서 좀 더 에러를 깔끔하고 잘 처리하기 위한 방법을 알아본다.


👾 ExceptionHandler

  • 컨트롤러 기반의 예외처리
    1. @Exceptionhandler의 인자값으로 exception 클래스를 넣어주면 해당 exception 발생했을때 이 @Exceptionhandler가 붙은 메소드가 동작한다.(리스트도 가능)
    2. @ResponseStatus 어노테이션을 통해 원하는 http 상태코드를 리턴해 줄 수 있다.
    3. ResponseEntity로 감싸서 응답 데이터를 보낼 수 있다.
      • ResponseEntity : ResponseEntity는 스프링에서 HTTP 응답을 표현하는 클래스로 HTTP 상태 코드, 헤더, 본문(body) 등을 포함할 수 있어, RESTful 웹 서비스에서 클라이언트에게 응답을 보다 유연하게 구성할 수 있게 해준다.
    4. Exception 객체를 인자값으로 받아와서 메소드 안에서 좀 더 구체적인 에러내용을 파악할 수 있고, 여러가지로 활용할 수 있다.
@Slf4j
@RestController
public class SampleController {
    
    // (1) 예외 클래스 지정 
    @ExceptionHandler(IllegalAccessException.class) 
    @ResponseStatus(value = HttpStatus.FORBIDDEN) // (2) HttpStatus 지정 
    public ResponseEntity<ErrorResponse> handleIllegalAccessException(IllegalAccessException e) {
        
        // (4) 예외 객체 활용 가능
        log.error("Illegal Exception : ", e);
        
        // ResponseEntity로 응답데이터 커스텀 가능
        return ResponseEntity
                .status(HttpStatus.FORBIDDEN)
                .body(new ErrorResponse(ErrorCode.TOO_BIG_ID_ERROR, "Illegal Exception occurred."));
        }
    }

 

 

대개 특정 exception 또는 exception리스트를 지정한 핸들러를 직접 만들어서 처리하지만 

어플리케이션에서 발생하는 Exception의 종류는 한가지가 아니라서 예외들을 커버하려면 handler가 여러개 있을 수 있다.

exception핸들러가 다수 있을때 처리되는 우선순위는 다음과 같다.

순위 handler
1 발생한 exception을 지정한 핸들러
2 발생한 exception의 부모 exception 클래스를 지정한 핸들러
3 모든 exception의 상위 클래스인 Exception 클래스를 지정한 핸들러

 

여러 handler를 만들었음에도 불구하고 예상치못하게 발생하는 exception 처리를 위해 Exception 클래스를 지정한 핸들러를 만들어 두기도 한다.

 

🧐 문제는 여기서 발생한다.

컨트롤러에 exception을 처리하기 위한 handler를 작성하였으나 Exceptionhandler은 컨트롤러 기반으로 작동하므로 

작성된 컨트롤러 외에는 예외를 처리하지 않는다.

그래서 어플리케이션 내에 있는 모든 컨트롤러에 handler를 복붙복붙 해야할 수도 있다.

하지만 스프링은 @RestControllerAdvice를 통해 해결방법을 제시한다.


😇 RestControllerAdvice

  • 어플리케이션 전역에 존재하는 컨트롤러들 한테 사용되는 조언
  • 컨트롤러에서 발생한 exception에 대한 처리가 어렵다면 대신 해줄 수 있는 어노테이션
  • 한번 작성해놓으면 많은 컨트롤러가 있어도 일관적인 예외 및 응답처리 가능.
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    // business methods
    @ExceptionHandler(IllegalAccessException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public ResponseEntity<ErrorResponse> handleIllegalAccessException(IllegalAccessException e) {
        log.error("Illegal Exception : ", e);
        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(new ErrorResponse(ErrorCode.TOO_BIG_ID_ERROR, "Illegal Exception occurred."));
    }
}

 

Exceptionhandler을 RestControllerAdvice 안에 작성해놓으면

어플리케이션 전역에 있는 컨트롤러에서 발생한 예외에 대해서 처리가 가능하다 🥹

RestControllerAdvice을 이용하여 코드의 양도 줄이고 정확하고 일관되게 예외처리를 하도록 해보자.

 

※  @ControllerAdvice vs @RestControllerAdvice

  1. 각각의 용도는 @Controller ,  @RestController와 동일하다
  2. 이전에는 컨트롤러를 통해서 화면을 내려주는 목적으로 많이 개발하였는데, 요즘에는 순수 백엔드 개발을 위해 @RestController를 사용해서 응답 객체를 직렬화하여 내려주는 용도로 개발하고 있음.
  3. 고로 @ControllerAdvice는 주로 화면을 내려주는 용도로 사용하고, @RestControllerAdvice는 RESTful api용으로 객체를 응답하는 방식을 사용할 때 많이 쓰인다.
728x90
반응형