Error Response JSON[프론트]ErrorException 추가 [간단 사용법]BusinessException 추가 [사용 예시]소스 설명ErrorCode.class (에러 코드 정의)ErrorResponse.class (에러 응답)FieldError.class (@Valid 검증 예외처리 응답에 쓰인다.)exception 패키지 - BusinessException exception 패키지 - InvalidInputException GlobalException.class(주석 참고!)
Error Response JSON[프론트]
{ "success": false, "httpMethod": “POST”, "statusCode": 400, "code" : "u001" "data": { { "message": " Invalid Input Value", "status": 400, // "errors":[], 비어있을 경우 null 이 아닌 빈 배열을 응답한다. "errors": [ { "field": "name.last", "value": "", "reason": "must not be empty" }, { "field": "name.first", "value": "", "reason": "must not be empty" } ], } } }
ErrorException 추가 [간단 사용법]
- ErrorCode 정의 →
enum
값을 만들어준다.
- GlobalException 메서드 추가
- ErrorResponse response 객체 생성
- ResponseEntity에 response, HttpStatus값을 넣어 반환해주면 된다.
BusinessException 추가 [사용 예시]
ex)Entity
를 찾지 못하는 경우의 Exception 생성 예시
- ErrorCode를 먼저 정의 해줍니다.
@Getter public enum ErrorCode { ... //404 ENTITY_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 엔티티를 찾을 수 없습니다."); ... }
EntityNotFoundException
을exception 패키지 하위
에 상속 받는 클래스로 생성해준다.
// BusinessException을 상속받으므로 Constructor를 만들어 주기만 하면 된다. public class EntityNotFoundException extends BusinessException{ public EntityNotFoundException(String message, ErrorCode errorCode) { super(message, errorCode); } public EntityNotFoundException(ErrorCode errorCode) { super(errorCode); } //ErrorCode에서 만든 예외 enum값을 넘겨준다. public EntityNotFoundException() { super(ErrorCode.ENTITY_NOT_FOUND); } }
- 이렇게 정의 하면
GlobalException.class
에서 한번에 처리를 할 수 있다. (아래 따로 추가 필요 X)
// 모든 BusinessException은 여기서 예외처리된다. @ExceptionHandler(BusinessException.class) protected ResponseEntity<ErrorResponse> handleEntityNotFoundException(final BusinessException e) { log.error("handleBusinessException", e); final ErrorCode errorCode = e.getErrorCode(); final ErrorResponse response = ErrorResponse.of(errorCode); return new ResponseEntity<>(response, errorCode.getStatus()); }
소스 설명

ErrorCode.class (에러 코드 정의)
0001 → 잘못된 입력 방식이 아닙니다. ?
@Getter public enum ErrorCode { //500 INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "정의되지 않은 서버 에러입니다."), //400 INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "잘못된 입력 방식입니다."), INVALID_TYPE_VALUE(HttpStatus.BAD_REQUEST, "유효한 타입이 아닙니다"), //405 METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "지원하지 않은 HTTP 메서드입니다."), //403 HANDLE_ACCESS_DENIED(HttpStatus.FORBIDDEN, "인증 권한을 보유하지 않습니다"); private final HttpStatus status; private final String message; ErrorCode(HttpStatus status, String message) { this.status = status; this.message = message; } }
- enum 타입으로 정의하여 예외처리가 새로 필요하면 먼저 여기에 정의하시면 됩니다.
- 이름정의(HttpStatus 값, 예외 메세지)
- ErrorCode는
snake case
로 정해주세요.
ErrorResponse.class (에러 응답)
@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ErrorResponse { private String message; private int status; private List<FieldError> errors; private LocalDateTime severTime; private ErrorResponse(final ErrorCode code, final List<FieldError> errors) { this.message = code.getMessage(); this.status = code.getStatus().value(); this.errors = errors; this.severTime = LocalDateTime.now(); } private ErrorResponse(final ErrorCode code) { this.message = code.getMessage(); this.status = code.getStatus().value(); this.errors = new ArrayList<>(); this.severTime = LocalDateTime.now(); } ... }
- 에러 응답 클래스
- message : 에러 메세지
- status : HttpStatus Code 반환
- errors → FieldError을 List로 가진다.
- 요청 값에 대한
field
,value
,reason
@Valid
에 대한 검증BindingResult
에 대한 값이 없는 경우NULL
이 아닌 빈 배열을 반환[]
- BindingResult : 검증 오류가 발생할 경우 오류 내용을 보관하는 spring 프레임워크에서 제공하는 객체
- 이게 있으면 @ModelAttribute에
데이터 바인딩
시 오류가 발생해도 오류 정보를 FieldError 객체에 담아 오류 정보를 보내준다. - 참고 : https://jaimemin.tistory.com/1874
- serverTime : 에러 발생 시간
FieldError.class (@Valid 검증 예외처리 응답에 쓰인다.)
@Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) public static class FieldError { private String field; private String value; private String reason; private FieldError(final String field, final String value, final String reason) { this.field = field; this.value = value; this.reason = reason; } public static List<FieldError> of(final String field, final String value, final String reason) { List<FieldError> fieldErrors = new ArrayList<>(); fieldErrors.add(new FieldError(field, value, reason)); return fieldErrors; } private static List<FieldError> of(final 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()); } }
exception 패키지 - BusinessException
public class BusinessException extends RuntimeException { private final ErrorCode errorCode; public BusinessException(String message, ErrorCode errorCode) { super(message); this.errorCode = errorCode; } public BusinessException(ErrorCode errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; } public ErrorCode getErrorCode() { return errorCode; } }
- 비지니스 예외시 상속받아 사용하기 위해 만들었습니다
- 실상
RuntimeException
입니다.
exception 패키지 - InvalidInputException
public class InvalidInputException extends BusinessException{ public InvalidInputException(String message, ErrorCode errorCode) { super(message, errorCode); } public InvalidInputException(ErrorCode errorCode) { super(errorCode); } public InvalidInputException() { super(ErrorCode.INVALID_INPUT_VALUE); } }
유효하지 않은 입력
에 대한 비지니스 예외처리를 추가한 코드 입니다.
- 비지니스 예외 추가시 이처럼
exception 패키지 하위
에 만들어서 사용하시면 됩니다.
GlobalException.class(주석 참고!)
@RestControllerAdvice public class GlobalExceptionHandler { private final Logger log = LoggerFactory.getLogger(getClass()); /* - avax.validation.Valid or @Validated 으로 binding error 발생시 발생한다. - HttpMessageConverter 에서 등록한 HttpMessageConverter binding 못할경우 발생 - 주로 @RequestBody, @RequestPart 어노테이션에서 발생 */ @ExceptionHandler(MethodArgumentNotValidException.class) protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { log.error("handleMethodArgumentNotValidException", e); final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult()); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } /* - @ModelAttribut 으로 binding error 발생시 BindException 발생한다. */ @ExceptionHandler(BindException.class) protected ResponseEntity<ErrorResponse> handleBindException(BindException e) { log.error("handleBindException", e); final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult()); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } /* - enum type 일치하지 않아 binding 못할 경우 발생 - 주로 @RequestParam enum으로 binding 못했을 경우 발생 */ @ExceptionHandler(MethodArgumentTypeMismatchException.class) protected ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { log.error("handleMethodArgumentTypeMismatchException", e); final ErrorResponse response = ErrorResponse.of(e); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } /* - 지원하지 않은 HTTP method 호출 할 경우 발생 */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) protected ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { log.error("handleHttpRequestMethodNotSupportedException", e); final ErrorResponse response = ErrorResponse.of(ErrorCode.METHOD_NOT_ALLOWED); return new ResponseEntity<>(response, HttpStatus.METHOD_NOT_ALLOWED); } /* - Authentication 객체가 필요한 권한을 보유하지 않은 경우 발생합 - Security에서 던지는 예외 */ @ExceptionHandler(AccessDeniedException.class) protected ResponseEntity<ErrorResponse> handleAccessDeniedException(AccessDeniedException e) { log.error("handleAccessDeniedException", e); final ErrorResponse response = ErrorResponse.of(ErrorCode.HANDLE_ACCESS_DENIED); return new ResponseEntity<>(response, ErrorCode.HANDLE_ACCESS_DENIED.getStatus()); } /* - input 관련 잘못된 데이터일때 예외 처리 발생 */ @ExceptionHandler({ IllegalStateException.class, IllegalArgumentException.class, TypeMismatchDataAccessException.class, HttpMessageNotReadableException.class, MissingServletRequestParameterException.class, MultipartException.class }) protected ResponseEntity<ErrorResponse> handleBadRequestException(InvalidInputException e){ log.error("handleBadRequestException", e); final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE); return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } /* - 비지니스 요규사항에 따른 Exception - 엔티티 설계후 필요한 비지니스exception을 정의해서 사용해야 한다. - 모든 비지니스 예외처리를 여기서 해결한다. */ @ExceptionHandler(BusinessException.class) protected ResponseEntity<ErrorResponse> handleEntityNotFoundException(final BusinessException e) { log.error("handleBusinessException", e); final ErrorCode errorCode = e.getErrorCode(); final ErrorResponse response = ErrorResponse.of(errorCode); return new ResponseEntity<>(response, errorCode.getStatus()); } /* - 그 밖에 발생하는 모든 예외 처리, Null Point Exception, 등등 - 개발자가 직접 핸들링해서 다른 예외로 던지지 않으면 모두 이곳으로 모인다. */ @ExceptionHandler(Exception.class) protected ResponseEntity<ErrorResponse> handleException(Exception e) { log.error("handleException", e); final ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR); return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); } }