1. 💡 Throw Exception
1.1. 예외의 구분
- 어플리케이션의 발생예외는 크게 체크예외, 언체크예외로 구분
- 체크예외 : 발생한 예외를 잡고(catch) 체크 후, 예외 복구&회피 등 구체적인 처리 필요
- ex: ClassNotFoundException
- 언체크예외 : 예외를 잡고(catch) 따로 처리가 필요하지 않음
- ex: NullPointerException, ArrayIndexOutOfBoundsException
- 휴먼에러로 인해 발생하는 오류는 전부 RuntimeException을 상속한 예외들임
- 자바는 많은 RuntimeException을 지원하지만 직접 예외를 만들어야 하는 경우도 있음
1.2. 의도적으로 예외 throw & catch 하기
- Custom Exception(enum) 생성
- 특정 계층에서 사용할 클래스 생성 후 생성자에 CustomException을 파라미터로 사용
- ExceptionAdvice에 @ExceptionHandler를 사용하는 메소드 추가 작성
- 예외가 발생할만한 서비스에 throw new Custom_Exception(Custom_enum.Method) 작성하여 throw해주기
- 던진 예외를 예외처리를 담당하는 클래스에 던진 예외를 잡기위한 @ExceptionHandler 메소드 생성
2. 💡 구현
GlobalExceptionAdvice - 처리된 에러를 응답으로 전달
BusinessLogicException - 적절한 Custom 상태코드 반환
ExceptionCode (Enum) - Custom Status
ErrorResponse - 직접적인 에러 처리 클래스, 용도벌 of 메소드 사용으로 특정 Exception별 에러 처리
<java />
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleMethodArgumentNotValidException(
MethodArgumentNotValidException e) {
final ErrorResponse response = ErrorResponse.of(e.getBindingResult());
return response;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleConstraintViolationException(
ConstraintViolationException e) {
final ErrorResponse response = ErrorResponse.of(e.getConstraintViolations());
return response;
}
@ExceptionHandler
public ResponseEntity handleBusinessLogicException(BusinessLogicException e) {
final ErrorResponse response = ErrorResponse.of(e.getExceptionCode());
return new ResponseEntity<>(response, HttpStatus.valueOf(e.getExceptionCode()
.getStatus()));
}
@ExceptionHandler
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public ErrorResponse handleHttpRequestMethodNotSupportedException(
HttpRequestMethodNotSupportedException e) {
final ErrorResponse response = ErrorResponse.of(HttpStatus.METHOD_NOT_ALLOWED);
return response;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleHttpMessageNotReadableException(
HttpMessageNotReadableException e) {
final ErrorResponse response = ErrorResponse.of(HttpStatus.BAD_REQUEST,
"Required request body is missing");
return response;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleMissingServletRequestParameterException(
MissingServletRequestParameterException e) {
final ErrorResponse response = ErrorResponse.of(HttpStatus.BAD_REQUEST,
e.getMessage());
return response;
}
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleException(Exception e) {
log.error("# handle Exception", e);
/*
* TODO 애플리케이션의 에러는 에러 로그를 로그에 기록하고, 관리자에게 이메일이나 카카오 톡,
* 슬랙 등으로 알려주는 로직이 있는게 좋음.
*/
final ErrorResponse response = ErrorResponse.of(HttpStatus.INTERNAL_SERVER_ERROR);
return response;
}
}
<java />
public class BusinessLogicException extends RuntimeException {
@Getter
private ExceptionCode exceptionCode;
public BusinessLogicException(ExceptionCode exceptionCode) {
super(exceptionCode.getMessage());
this.exceptionCode = exceptionCode;
}
}
<java />
public enum ExceptionCode {
MEMBER_NOT_FOUND(404, "Member not found"),
MEMBER_EXISTS(409, "Member exists"),
COFFEE_NOT_FOUND(404, "Coffee not found"),
COFFEE_CODE_EXISTS(409, "Coffee Code exists"),
ORDER_NOT_FOUND(404, "Order not found"),
CANNOT_CHANGE_ORDER(403, "Order can not change"),
NOT_IMPLEMENTATION(501, "Not Implementation"),
INVALID_MEMBER_STATUS(400, "Invalid member status");
@Getter
private int status;
@Getter
private String message;
ExceptionCode(int status, String message) {
this.status = status;
this.message = message;
}
}
<java />
@Getter
public class ErrorResponse {
private int status;
private String message;
private List<FieldError> fieldErrors;
private List<ConstraintViolationError> violationErrors;
private ErrorResponse(int status, String message) {
this.status = status;
this.message = message;
}
private ErrorResponse(final List<FieldError> fieldErrors,
final List<ConstraintViolationError> violationErrors) {
this.fieldErrors = fieldErrors;
this.violationErrors = violationErrors;
}
public static ErrorResponse of(BindingResult bindingResult) {
return new ErrorResponse(FieldError.of(bindingResult), null);
}
public static ErrorResponse of(Set<ConstraintViolation<?>> violations) {
return new ErrorResponse(null, ConstraintViolationError.of(violations));
}
public static ErrorResponse of(ExceptionCode exceptionCode) {
return new ErrorResponse(exceptionCode.getStatus(), exceptionCode.getMessage());
}
public static ErrorResponse of(HttpStatus httpStatus) {
return new ErrorResponse(httpStatus.value(), httpStatus.getReasonPhrase());
}
public static ErrorResponse of(HttpStatus httpStatus, String message) {
return new ErrorResponse(httpStatus.value(), message);
}
@Getter
public static class FieldError {
private String field;
private Object rejectedValue;
private String reason;
private FieldError(String field, Object rejectedValue, String reason) {
this.field = field;
this.rejectedValue = rejectedValue;
this.reason = reason;
}
public static List<FieldError> of(BindingResult bindingResult) {
final List<org.springframework.validation.FieldError> fieldErrors = bindingResult.getFieldErrors();
return fieldErrors.stream()
.map(error -> new FieldError(
error.getField(),
error.getRejectedValue() == null ? "" : error.getRejectedValue().toString(),
error.getDefaultMessage()))
.collect(Collectors.toList());
}
}
@Getter
public static class ConstraintViolationError {
private String propertyPath;
private Object rejectedValue;
private String reason;
private ConstraintViolationError(String propertyPath, Object rejectedValue, String reason) {
this.propertyPath = propertyPath;
this.rejectedValue = rejectedValue;
this.reason = reason;
}
public static List<ConstraintViolationError> of(Set<ConstraintViolation<?>> constraintViolations) {
return constraintViolations.stream()
.map(constraintViolation -> new ConstraintViolationError(
constraintViolation.getPropertyPath().toString(),
constraintViolation.getInvalidValue().toString(),
constraintViolation.getMessage()))
.collect(Collectors.toList());
}
}
}
'Framework > Spring' 카테고리의 다른 글
Spring Data JDBC 기반 도메인 엔티티 & 테이블 설계 (0) | 2022.10.27 |
---|---|
Spring JDBC (0) | 2022.10.27 |
Exception Handler -> RestControllerAdvice (0) | 2022.10.25 |
Web MVC Service Layer (0) | 2022.10.24 |
정규표현식 (0) | 2022.10.21 |