본문 바로가기

Project

[Side Project] 에러처리

✅ ErrorCode ENUM 처리

@Getter
public enum ErrorCode {
    // 400 Bad Request
    BAD_REQUEST(HttpStatus.BAD_REQUEST, "BAD_REQUEST", "클라이언트 요청의 형식이나 내용이 잘못된 경우"),
    INVALID_PARAMETER(HttpStatus.BAD_REQUEST, "INVALID_PARAMETER", "요청한 파라미터의 형식이나 내용에 오류가 있는 경우"),
    INVALID_RESOURCE(HttpStatus.BAD_REQUEST, "INVALID_RESOURCE", "요청한 리소스의 내용에 오류가 있는 경우"),
    MISSING_PARAMETER(HttpStatus.BAD_REQUEST, "MISSING_PARAMETER", "필수 파라미터를 지정하지 않은 경우"),
    LIMIT_EXCEEDED_400(HttpStatus.BAD_REQUEST, "LIMIT_EXCEEDED", "제한 설정을 초과한 경우"),
    OUT_OF_RANGE_400(HttpStatus.BAD_REQUEST, "OUT_OF_RANGE", "요청 값이 범위를 벗어나는 경우"),

    // 401 Unauthorized
    UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "UNAUTHORIZED", "요구되는 인증 정보가 누락되었거나 잘못됨"),
    INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "INVALID_TOKEN", "Access Token이 잘못된 경우"),

    // 403 Forbidden
    FORBIDDEN(HttpStatus.FORBIDDEN, "FORBIDDEN", "요청은 이해했지만 권한이 없어 거부됨"),
    ACCESS_DENIED(HttpStatus.FORBIDDEN, "ACCESS_DENIED", "요청한 리소스에 대한 접근이 제한됨"),
    LIMIT_EXCEEDED_403(HttpStatus.FORBIDDEN, "LIMIT_EXCEEDED", "리소스 제한을 초과한 경우"),
    OUT_OF_RANGE_403(HttpStatus.FORBIDDEN, "OUT_OF_RANGE", "요청 범위를 벗어난 경우"),
    INSUFFICIENT_SCOPE(HttpStatus.FORBIDDEN, "INSUFFICIENT_SCOPE", "Access Token에 필요한 Scope 권한이 없음"),

    // 404 Not Found
    NOT_FOUND(HttpStatus.NOT_FOUND, "NOT_FOUND", "요청한 리소스를 찾을 수 없음"),
    RESOURCE_NOT_EXIST(HttpStatus.NOT_FOUND, "RESOURCE_NOT_EXIST", "특정 리소스를 찾을 수 없음"),

    // 409 Conflict
    CONFLICT(HttpStatus.CONFLICT, "CONFLICT", "요청한 리소스와 충돌 발생"),
    ALREADY_EXIST(HttpStatus.CONFLICT, "ALREADY_EXIST", "요청한 리소스가 이미 존재함"),
    RESOURCE_ALREADY_EXIST(HttpStatus.CONFLICT, "RESOURCE_ALREADY_EXIST", "특정 리소스가 이미 존재함"),

    // 410 Gone
    DELETED(HttpStatus.GONE, "DELETED", "요청한 리소스가 삭제되어 존재하지 않음"),

    // 500 Internal Server Error
    INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR", "서버 내부 오류 발생"),

    // 503 Service Unavailable
    SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", "일시적인 서버 오류");

    ErrorCode(HttpStatus httpStatus, String errorCode, String message) {
        this.httpStatus = httpStatus;
        this.errorCode = errorCode;
        this.message = message;
    }

    private final HttpStatus httpStatus;
    private final String errorCode;
    private final String message;

}

 

 

✏️ 공부내용

일단 httpStatus와 errorCode로 세분화를 해서 enum을 구성했고 단순히 httpStatus와 message보다는 구체적으로 어떤 오류인지 errorCode를 정의해서 사용함으로써 오류를 직관적으로 알수있다.

 

 

@Getter
@Builder
@RequiredArgsConstructor
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();
    }
    //BindingResult에 Error정보들이 들어간다.
    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) {
            //첫번째 에러 메세지가 아니면 ", " 단위로 연결을 해준다
            if(!isFirst){
                sb.append(", ");
            }else{
                isFirst = false;
            }
            sb.append("[");
            //에러의 필드명
            sb.append(fieldError.getField());
            sb.append("]");
            //에러의 기본 메세지
            sb.append(fieldError.getDefaultMessage());
        }
        return sb.toString();
    }

}

✏️ ErrorCode 공부내용

of 메서드는 오버로딩을 사용해서 erroMessage와 bindingResult에서의 메세지를 사용할수있게 하였다.

bindingResult는 BindingResult로 매개변수를 바인딩 할 시 오류가 발생해도 에러를 발생시키지 않고 에러코드를 BindingResult 객체가 저장해서 그걸로 인해 에러를 전달할수 있다. ( 즉 controller에서 오류발생시 사용 ) 

 

@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);
    }

}

✏️ GlobalExceptionHandler 공부내용

Exception의 종류에 따라서 method를 만들어서 사용하면될거같다.

ErrorCode는 httpStatus코드를 넣어놨다 저 세분해서 넣어도될꺼같다