Error 처리

Gyeongjae Ham·2023년 8월 16일
0
post-thumbnail

에러 처리

  • 클라이언트에서 예외처리를 동일하게 관리할 수 있도록 통일된 ErrorResponse를 반환합니다
  • @RestControllerAdvice: @RestController에서 전역적으로 발생하는 예외를 한 군데에서 처리합니다
  • 비즈니스 로직을 수행하다가 조건이 맞지 않을 경우 BussinessException 발생시킵니다
  • ErrorCode Enum을 이용해서 에러 메시지, 에러코드 및 반환할 Http Status를 관리합니다

네이버 ErrorResponse 예시


ErrorCode

  • 반환할 Http Status 값, 에러코드, 에러메시지를 관리하는 Enum 클래스
    [com.app.global.error.ErrorCode.java]
@Getter
public Enum ErrorCode {
	private String errorCode;
    private HttpStatus httpStatus;
    private String message;
    
    ErrorCode(HttpStatus httpStatus, String errorCode, String message) {
	    this.httpStatus = httpStatus;
    	this.errorCode = errorCode;
    	this.message = message;
	}
}

BusinessException

  • 비즈니스 로직 수행 중 예외를 발생시켜야하는 경우 사용할 Custom Exception 정의
  • spring에서는 transaction을 사용하다가 unchecked exception이 발생하면 rollback이 되므로, RuntimeException을 상속받아서 custom exception을 정의합니다
    [com.app.global.error.exception.BusinessException.java]
@Getter
public class BusinessException extends RuntimeException {
	private ErrorCode errorCode;
    
    public BusinessException(ErrorCode errorCode) {
    	super(errorCode.getMessage());
        this.errorCode = errorCode;

ErrorResponse

[com.app.global.error.ErrorResponse.java]

  • 클라이언트쪽으로 반환할 ErrorResponse
  • 에러코드와 에러메세지 반환
@Getter
@Builder
public class ErrorResponse {
	
    private String errorCode;
    private String errorMessage;
    
    public static ErrorResponse of(String errorCode, String errorMessage) {
    	return ErrorResponse.builder()
        	.errorCode(errorCode)
            .errorMessage(errorMessage)
            .build();
    }
    
    public static ErrorResponse of(String errorCode, BindingResult bindingResult) {
    	return ErrorResponse.builder()
        	.errorCode(errorCode)
            .errorMessage(createErrorMessage(bindingResult))
            .build();
    }
    
    private static String createErrorMessage(BindingResult bindingResult) {
    	StringBuilder sb = new StringBuilder();
        boolean isFirst = true;
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        
        for (FieldError fieldError : fieldErrors) {
        	is (!isFirst) {
            	sb.append(",");
            } else {
            	isFirst = false;
            }
            
            sb.append("[");
            sb.append(fieldError.getField());
            sb.append("]");
            sb.append(fieldError.getDefaultMessage());
    	}
    	return sb.toString();
    }
}

GlobalExceptionHandler

[com.app.global.error.GlobalExceptionHandler.java]

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * javax.validation.Valid 또는 @Validated binding error가 발생할 경우
     */
    @ExceptionHandler(BindException.class)
    protected ResponseEntity<ErrorResponse> handleBindException(BindException e) {
        log.error("handleBindException", e);
        ErrorResponse errorResponse = ErrorResponse.of(HttpStatus.BAD_REQUEST.toString(), e.getBindingResult());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(errorResponse);
    }

    /**
     * 주로 @RequestParam enum으로 binding 못했을 경우 발생
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    protected ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        log.error("handleMethodArgumentTypeMismatchException", e);
        ErrorResponse errorResponse = ErrorResponse.of(HttpStatus.BAD_REQUEST.toString(), e.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(errorResponse);
    }

    /**
     * 지원하지 않은 HTTP method 호출 할 경우 발생
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    protected ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("handleHttpRequestMethodNotSupportedException", e);
        ErrorResponse errorResponse = ErrorResponse.of(HttpStatus.METHOD_NOT_ALLOWED.toString(), e.getMessage());
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(errorResponse);
    }

    /**
     * 비즈니스 로직 실행 중 오류 발생
     */
    @ExceptionHandler(value = { BusinessException.class })
    protected ResponseEntity<ErrorResponse> handleConflict(BusinessException e) {
        log.error("BusinessException", e);
        ErrorResponse errorResponse = ErrorResponse.of(e.getErrorCode().getErrorCode(), e.getMessage());
        return ResponseEntity.status(e.getErrorCode().getHttpStatus())
                .body(errorResponse);
    }

    /**
     * 나머지 예외 발생
     */
    @ExceptionHandler(Exception.class)
    protected ResponseEntity<ErrorResponse> handleException(Exception e) {
        log.error("Exception", e);
        ErrorResponse errorResponse = ErrorResponse.of(HttpStatus.INTERNAL_SERVER_ERROR.toString(), e.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
    }

}

spring-boot-starter-validation

  • Java Bean 유효성 검사를 사용하기 위한 스타터
  • 입력값을 검증할 때 효과적으로 사용할 수 있습니다

입력값 검증을 위한 예시 어노테이션

어노테이션설명
@NotEmptyNULL 체크 및 문자열의 경우 길이 0인지 검사
@NotBlankNULL 체크 및 문자열의 경우 길이 0 및 문자열 ("") 검사
@Length(min=, max=)최소, 최대 길이 검사
@Email이메일 형식인지 검사
@Max(숫자)지정한 값보다 작은지 검사
@Min(숫자)지정한 값보다 큰지 검사
@Null값이 NULL인지 검사
@NotNull값이 NULL이 아닌지 검사
profile
Always be happy 😀

0개의 댓글