ExceptionHandler 

 

 

 

 

자바에서는 예외를 처리하기 위한 try catch 구문이 있다.

하지만, 우리가 스프링으로 넘어와서 웹 관련 프로젝트를 하게되면 예외를 처리해야되는 부분이 굉장히 많아질 수 있다.

그 경우, 모든 예외 상황마다 일일이 try와 catch로 감쌀 것인가?

그렇게되면 코드의 가독성 나쁠 수 있다.

 

스프링에서는 이러한 문제를 해결하기 위한 예외처리 방법이 있다.

어노테이션의 이름부터 ExceptionHandler, 예외처리다.

 

ExceptionHandler 어노테이션을 사용하면 @Controller 혹은 @RestController로 지정된 클래스에서

특정 예외상황마다 처리방식을 우리가 지정해 줄 수 있다.

만약, 여기저기 흩어진 IllegalAccessException 예외가 여러 개 있을 경우, ExceptionHandler 하나로 해당 예외상황에서 처리방법을 한꺼번에 정할 수 있는 것이다.

 

처리 순위는 다음과 같다.

1. 해당 예외처리로 등록된 @ExceptionHandler

2. 부모 예외를 처리도록 등록된 @ExceptionHandler

(최종적으로는 @ExceptionHandler(Exception.class) 으로 모든 예외 처리가능)

@ResponseStatus(HttpStatus.FORBIDDEN) // 403 에러
@ExceptionHandler(IllegalAccessException.class)

public ErrorResponse handleIllegalAccessException(
        IllegalAccessException e) {
        
    log.error("IllegalAccessException is occurred", e);
    return new ErrorResponse("INVALID_ACCESS",
            "IllegalAccessException is occurred.");
            
}
    
@AllArgsConstructor // 생성자 자동생성
@Data // getter, setter 자동생성
public class ErrorResponse {
     private String errorCode;
     private String message;
}

ExceptionHandler 어노테이션 안에 처리하길 원하는 예외타입을 작성했다.

log.error로 잘 작동하는지 확인하기 위해 로그를 찍어주고, 별도로 만든 메서드를 이용해 Json 타입으로 리턴해보았다.

추가로, HttpStatus.FORBIDDEN (403 에러) 으로 Http 에러타입까지 지정해주었다.

 

아래와 같이 작성할 수도 있다.

@ExceptionHandler(IllegalAccessException.class)

public ResponseEntity<ErrorResponse> handleIllegalAccessException(
        IllegalAccessException e) {
        
    log.error("IllegalAccessException is occurred", e);
    return ResponseEntity
            .status(HttpStatus.FORBIDDEN)
            .header("newHeader", "Some Value")
            .body(new ErrorResponse("INVALID_ACCESS",
                    "IllegalAccessException is occurred."));
                    
}

어노테이션으로 지정했던 ResponseStatus를 지우고, 반환값으로 ResponseEntity를 지정하여 위에서 만든 ErrorResponse 메서드를 매개변수 타입으로 넣었다.

그리고 반환부분에 FORBIDDEN으로 동일하게 에러타입을 정했다.

위에서 반환값이었던 Json타입의 데이터를 body 부분에 넣고 이번에는 header도 추가해보았다.

public enum HttpStatus

 

좌 - ResponseStatus 방식                                                                                      우 = ResponseEntity 방식

두 방식 모두 출력 결과가 동일하며, ResponseEntity를 사용할 때 작성했던 header까지 추가된 것을 볼 수 있다.

(윗부분이 header고 엔터키를 구분으로 아래 Json 부분이 body다.)

 

 

localhost

 

IllegalAccessException 예외가 발생했을 때 출력화면과 로컬호스트 모두에서 예외처리가 잘 작동하였다.

 

이렇게 특정 예외의 갯수가 많을 때는 위 처럼 ExceptionHandler를 한 두 개 지정해주면 끝날 일이다.

그런데 만약, 우리가 처리해야 될 예외의 타입이 많다고 가정해보자.

이 경우에는 특정 중요한 예외 몇가지를 처리하고, 나머지를 @ExceptionHandler(IllegalAccessException.class) 로 처리하면 된다.

왜냐하면, Exception클래스는 모든 예외의 부모이기 때문이다.

@ExceptionHandler(Exception.class)

public ResponseEntity<ErrorResponse> handleException(
        Exception e) {
        
    log.error("Exception is occurred", e);
    return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR) // 이 경우 에러코드 500이 일반적으로 사용된다.
            .body(new ErrorResponse(ErrorCode.INTERNAL_SERVER_ERROR, // Json으로 출력을 위해 별도로 생성한 Enum
                    "Exception is occurred."));
                    
}

@AllArgsConstructor // 생성자 자동생성
@Data // getter, setter 자동생성
public class ErrorResponse {
    private ErrorCode errorCode;
    private String message;
}

public enum ErrorCode {
    INTERNAL_SERVER_ERROR,
    TOO_BIG_ID_ERROR,
    TOO_SMALL_ID_ERROR
}

 

만약 우리가 따로 등록해주지 않은 예외가 발생한다면, 결국 마지막에 와서 위 예외처리에 걸리게 될 것이다.

그 어떤 예외가 오든 결국 Exception클래스의 자식클래스이기 때문이다.

 

 

추가로, 이렇게 생각하는 분들도 계실것이다.

지금까지 설명한 예외처리가 모두 특정 @Controller 혹은 @RestController로 지정된 클래스에서 동작하니깐,

새로운 클래스를 만들때마다 결국 예외처리를 작성해야 하는 번거로움은 똑같다고.

 

그렇다. 새로운 클래스에도 마찬가지고 예외처리를 작성해 주어야 적용된다.

하지만 이런 불편함도 역시 또 다른 어노테이션으로 해결할 수 있다.

 

그 이름은 @RestControllerAdvice

모든 클래스에서의 예외를 우리가 작성한 대로 처리해준다.

 

사용법은 간단하다.

클래스 이름 위에 어노테이션을 작성해주고,

클래스 안쪽에는 위에서와 똑같이 예외처리를 작성해주면 된다.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalAccessException.class)
    public ResponseEntity<ErrorResponse> handleIllegalAccessException(){코드}
    
    @ExceptionHandler(WebSampleException.class)
    public ResponseEntity<ErrorResponse> handleWebSampleException(){코드}
}

 

 

 

여기까지 스프링에서 예외처리하는 방법에 대해 알아보았다.

복사했습니다!