우주먼지
article thumbnail

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
profile

우주먼지

@o귤o

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그