@ControllerAdvice 애노테이션과 ResponseEntityExceptionHandler 추상 클래스 상속을 통해 예외처리를 할 수 있다.
@ControllerAdvice 애노테이션을 사용하여 Exception 처리를 한 곳으로 모으는 경우, ResponseEntityExceptionHandler를 상속 받도록 하여 Spring MVC에서 기본으로 제공되는 Exception들의 처리를 간단하게 등록할 수 있다.
ResponseEntityExceptionHandler 추상 클래스에는 각 Exception 처리를 위한 메서드들을 모두 protected로 선언되어 있어서 필요에 따라 Override를 해야한다.
📌 ExceptionResponse
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {
private LocalDateTime timestamp;
private String message;
private String details;
}
오류를 담을 객체다.
📌 UserNotFoundException
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
사용자 정의 예외 클래스다.
📌 CustomizedResponseEntityExceptionHandler
@RestControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
// 오류 정보를 담을 객체를 만드는 메서드
private ExceptionResponse createExceptionResponse(Exception ex, WebRequest request) {
return new ExceptionResponse(LocalDateTime.now(), ex.getMessage(), request.getDescription(false));
}
@ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
ExceptionResponse exceptionsResponse = createExceptionResponse(ex, request);
return new ResponseEntity<>(exceptionsResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundException(Exception ex, WebRequest request) {
ExceptionResponse exceptionsResponse = createExceptionResponse(ex, request);
return new ResponseEntity<>(exceptionsResponse, HttpStatus.NOT_FOUND);
}
}
스프링 MVC Excpetion Handler 기능을 한다.
@ControllerAdivce를 사용하면 Global Controller Exception Handling을 할 수 있다.
ResponseEntityExceptionHandler 추상 클래스를 상속 받고 있다. ResponseEntityExceptionHandler 추상 클래스는 Spring MVC에서 발생할 수 있는 예외에 대해 미리 Handling 해놓은 클래스다.
즉, @ControllerAdvice 애노테이션으로 모든 컨트롤러에서 발생하는 Exception을 캐지하고 캐치한 Exception들(사용자 정의 Exception 클래스 제외)을 기본적으로 ResponseEntityExceptionHandler에서 정의된 메서드로 처리할 수 있다.
WebRequest는 HttpServletRequest 클래스와 비슷하지만 HttpServletRequest와 달리 서블릿에 종속되지 않는다. HttpServletRequest보다 WebRequest를 사용할 것을 권장한다. WebRequest를 통해 예외가 어느 URI에서 발생했는지 알 수 있다. WebReqeust#getDescription(boolean includeClientInfo) 를 통해 예외가 발생한 URI 주소를 가져올 수 있다. 파라미터를 true로 할 경우 클라이언트에 대한 정보도 가져온다.
🧷 오류 메시지 캐싱
📌 ExampleException
public class ExampleException extends RuntimeException{
public ExampleException() {
super();
}
public ExampleException(String message) {
super(message);
}
public ExampleException(String message, Throwable cause) {
super(message, cause);
}
public ExampleException(Throwable cause) {
super(cause);
}
protected ExampleException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
사용자 정의 오류 클래스
📌 ExceptionResponse
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {
private LocalDateTime timestamp;
private String message;
private String details;
}
오류를 담을 객체
📌 CustomizedResponseEntityExceptionHandler
@RestControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
// 오류 정보를 담을 객체를 만드는 메서드
private ExceptionResponse createExampleException(Exception ex, WebRequest request) {
return new ExceptionResponse(LocalDateTime.now(), resolveMessage(ex, ex.getMessage()), request.getDescription(false));
}
/**
* 오류 메시지 캐싱
*/
private final Map<Class<? extends Exception>, String> messageMappings
= Collections.unmodifiableMap(new LinkedHashMap<>(){
{
put(ExampleException.class, "심플예제 입니다.");
}
});
private String resolveMessage(Exception ex, final String defaultMessage) {
return messageMappings.entrySet()
.stream()
.filter(entry -> entry.getKey().isAssignableFrom(ex.getClass()))
.findFirst()
.map(Map.Entry::getValue)
.orElse(defaultMessage);
}
@ExceptionHandler(ExampleException.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
ExceptionResponse exceptionsResponse = createExceptionResponse(ex, request);
return new ResponseEntity<>(exceptionsResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
messageMappings : key를 Exception Class로 하고, value를 String 타입인 에러 메시지로 했다. 순서가 있는 순서가 있는 LinkedHashMap을 만들고 이 map을 unmodifiableMap으로 불변 객체로 만들어 캐싱한다.
resovleMeessage : 파라미터로 Exception과 defaulMessage를 받는다. messageMappings에서 일치하는 Exception이 있는지 확인하고 존재하면 value 값을 가져와서 사용하고 없다면 인자로 받은 defaultMessage를 사용한다.
👀 참고 자료
https://programmingrecoding.tistory.com/20
'[ Spring ] > REST API' 카테고리의 다른 글
[Spring] Rest API Documentation을 위한 Swagger 사용 (0) | 2022.05.10 |
---|---|
[Spring] API 구현을 위한 Hateoas 적용 (0) | 2022.05.10 |
[Spring] Rest Api Version 관리 (0) | 2022.05.10 |
[Spring] Response 데이터 제어를 위한 Filtering (0) | 2022.05.09 |
[Spring] URI 조립을 위한 ServletUriComponentsBuilder (0) | 2022.05.08 |