
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도 추가해보았다.
두 방식 모두 출력 결과가 동일하며, ResponseEntity를 사용할 때 작성했던 header까지 추가된 것을 볼 수 있다.
(윗부분이 header고 엔터키를 구분으로 아래 Json 부분이 body다.)
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(){코드}
}
여기까지 스프링에서 예외처리하는 방법에 대해 알아보았다.
'스프링' 카테고리의 다른 글
[스프링] 프로젝트 실행 시 LoggingFailureAnalysisReporter 에러 해결 (0) | 2022.08.29 |
---|---|
[Spring Boot] Spring Data JPA - domain과 Entity 개념 / Entity에서 Dto 변환 (0) | 2022.08.24 |
[스프링] JUnit 테스트 시 반복되는 NullPointerException 오류 해결 (0) | 2022.08.13 |
[스프링] Http 요청을 매핑하여 데이터 받기 (0) | 2022.07.31 |
[자바] 스프링의 환경 설정 : 프로파일(Profile) 사용하기 (0) | 2022.07.20 |