에러메시지 및 예외 처리

다음은 에러메시지 및 예외 처리에 대해 설명합니다.


예외 처리

서버에서 Exception이 발생되어 ‘예외 처리’를 하는 경우는 다음과 같습니다.

  • 서버 페이지(Next.js)를 호출할 때 (BO 서버 프로젝트)

  • Restful API를 호출할 때(API 서버 프로젝트)

일반적인 API서버들의 경우 Common에 있는 GlobalControllerAdvice 및 각각 API 프로젝트의 DisplayControllerAdvice(GlobalControllerAdvice를 상속) 등에서 예외를 처리합니다.

  • GlobalControllerAdvice.class (해당 파일은 Common에서 작성되며, 이러한 Exception들을 처리하는 것으로 인식하면 됩니다.)

GlobalControllerAdvice Class의 경우 공통에서 처리해야 될 예외 사항들을 처리합니다.

일반적인 Exception의 경우 Httpstatus값을 500으로 반환하고 있으며, ValidationException과 같이 400번대 오류들의 경우 앞에 9를 붙여서 9400, 9404, 9401, 9403등으로 반환합니다.

예시 소스(일부 발췌):

GlobalControllerAdvice.java
/** * GlobalControllerAdvice */
@Slf4j
public class GlobalControllerAdvice {

    @ExceptionHandler(Exception.class)
    protected ResponseEntity<Object> handleException(Exception e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.error("", e);
        String code = String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value());
        String message = e.getMessage();
        int httpStatus = Integer.parseInt(code);
        ErrorCode errorCode = ErrorCode.builder()
            .code(code)
            .message(message)
            .httpStatus(httpStatus)
            .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(BindException.class)
    protected ResponseEntity<Object> handleBindException(BindException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("", e);
        String code = CommonAppError.BINDING_ERROR.getCode();
        String message = getBindingErrorMessage(e.getBindingResult());
        String httpStatusCode = String.valueOf(HttpStatus.BAD_REQUEST.value());
        int httpStatus = getBadRequestHttpStatusCode(httpStatusCode);
        ErrorCode errorCode = ErrorCode.builder()
            .code(code)
            .message(message)
            .httpStatus(httpStatus)
            .build();
        return handleExceptionInternal(e, errorCode);
    }

    // ...... 그외 등등 소스코드가 길어서 생략함.
}

예외 반환 응답값

GlobalControllerAdvice에서 처리하는 Exception 목록 (Exception들은 대부분 추가 되었으나, 혹시 제외된 ‘예외 처리’가 있다면 추가될 수 있습니다.)

Exception 종류
반환응답값
비고

Exception.class

500

handleException 함수에서 처리하며, ControllerAdvice에서 잡지 못하는 모든 예외 사항은 해당 함수에서 처리함. 해당 Exception의 메시지값과 500 http status값을 반환함.

NullPointerException.class, IOException.class, ArrayIndexOutOfBoundsException.class, EntityNotFoundException.class, StringIndexOutOfBoundsException.class, IndexOutOfBoundsException.class, UnsupportedEncodingException.class

500

handleErrorException 함수에서 처리하며, 종류에 정의된 Exception들의 경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 500 http status값을 반환함.

IllegalArgumentException.class, IllegalStateException.class, ConstraintViolationException.class, JsonParseException.class, com.fasterxml.jackson.core.JsonParseException.class, HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class, MissingServletRequestParameterException.class, MultipartException.class

500

handleIllegalException 함수에서 처리하며, 종류에 정의된 Exception들의 경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 500 http status값을 반환함.

HttpInterfaceResponseException.class

HttpInterface 에서 반환하는 응답값

handleHttpInterfaceException 함수에서 처리하며, HttpInterface 기능에서 Exception이 날경우 해당 함수에서 처리함. HttpInterface를 호출하는 상대쪽에서 반환하는 메시지값과 http status값을 반환함.

HttpException.class

RestApiInterface 에서 반환하는 응답값

handleHttpException 함수에서 처리하며, RestApiInterface 기능에서 Exception이 날경우 해당 함수에서 처리함. RestApiInterface를 호출하는 상대쪽에서 반환하는 메시지값과 http status값을 반환함.

WebClientResponseException.class

WebClient에서 반환하는 응답값

handleWebClientResponseException 함수에서 처리하며, WebClient 기능에서 Exception이 날경우 해당 함수에서 처리함. WebClient를 호출하는 상대쪽에서 반환하는 메시지값과 http status값을 반환함.

WebClientRequestException.class

500

handleWebClientRequestException 함수에서 처리하며, WebClient 요청시 Exception이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 500 http status값을 반환함.

AsyncRequestTimeoutException.class

500

handleAsyncRequestTimeoutException 함수에서 처리하며, 비동기 요청시 Timeout Exception이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 500 http status값을 반환함.

ValidationException.class

원래 400이지만 9400으로 처리함. 9400

handleValidationException 함수에서 처리하며, ValidationException이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9400 http status값을 반환함.

BindException.class

원래 400이지만 9400으로 처리함. 9400

handleBindException 함수에서 처리하며, BindException이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9400 http status값을 반환함.

MethodArgumentNotValidException.class

원래 400이지만 9400으로 처리함. 9400

handleMethodArgumentNotValidException 함수에서 처리하며, MethodArgumentNotValidException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9400 http status값을 반환함.

HttpRequestMethodNotSupportedException.class

원래 405이지만 9405으로 처리함. 9405

handleNotSupportedException 함수에서 처리하며, HttpRequestMethodNotSupportedException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9405 http status값을 반환함.

NoHandlerFoundException.class

원래 404이지만 9404으로 처리함. 9404

handleNoHandlerFoundException 함수에서 처리하며, NoHandlerFoundException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9404 http status값을 반환함.

MaxUploadSizeExceededException.class

원래 413이지만 9413으로 처리함. 9413

handleMaxSizeException 함수에서 처리하며, MaxUploadSizeExceededException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9413 http status값을 반환함.

AuthenticationException.class

원래 401이지만 9401으로 처리함. 9401

handleAuthenticationException 함수에서 처리하며, AuthenticationException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9401 http status값을 반환함.

JwtException.class

원래 401이지만 9401으로 처리함. 9401

handleJwtException 함수에서 처리하며, JwtException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9401 http status값을 반환함.

AccessDeniedException.class

원래 403이지만 9403으로 처리함. 9403

handleAccessDeniedException 함수에서 처리하며, AccessDeniedException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9403 http status값을 반환함.

ExpiredJwtException.class

원래 403이지만 9403으로 처리함. 9403

handleExpiredJwtException 함수에서 처리하며, ExpiredJwtException 이 날경우 해당 함수에서 처리함. 해당 Exception의 메시지값과 9403 http status값을 반환함.

  • DisplayControllerAdvice.class

(EventControllerAdvice 등등 각 API서버들의 ControllerAdvice Class)

(해당 파일은 각 프로젝트에서 작성되며 GlobalControllerAdvice를 상속받아서 AppException.class 만이 추가 되었습니다. 해당 Class 또한 특별하게 수정할 일이 없는 경우, 확인만 진행하면 됩니다.)

예시 소스(발췌):

각각 API의 ControllerAdvice에서는 @ExceptionHandler(AppException.class)만을 담당하고 있습니다.

그리고 가장 중요한 getAppExceptionHttpStatus 함수에서는 반환할 Httpstatus값을 처리하고 있습니다.

프로젝트별 Code값 목록

프로젝트별 ControllerAdvice에서 처리하는 Code값 목록

실제 업무단에서는 1000~8999번대 code만 정의하기 때문에 하단 목록에서 가장 아래 항목인 ‘그외’만 확인하면 됩니다.

Code
HttpStatus
설명 (ex)

0000

200

SUCCESS("0000", "common.message.success", "common.message.success") — 0000인 경우에는 SUCCESS라고 판단하여 200으로 설정

9999

500

FAIL("9999", "common.message.fail", "common.message.fail") — 9999인 경우에는 FAIL라고 판단하여 500으로 설정

9000

500

UNKNOWN("9000", "common.error.unknown", "common.error.unknown") — 9000인 경우에는 UNKNOWN인데 역시나 500으로 설정

9001 ~ 9099

9400

여러 validation/parameter 관련 에러(예: EMPTY_PARAMETER, INVALID_PARAMETER 등). 400번대라고 판단하여 9를 붙여 9400으로 설정

9100 ~ 9199

9401

인증(401) 관련 코드들 — 9를 붙여 9401으로 설정

9300 ~ 9399

9403

권한(403) 관련 코드들 — 9를 붙여 9403으로 설정

9400 ~ 9499

9404

404 관련 코드들 — 9를 붙여 9404으로 설정

그외

9400

그외 모든 코드값들의 경우 현재는 모두 9를 붙여서 9400으로 설정. 실제로 각 업무단에서 1000~8999번대 코드들을 관리하게 됩니다. 해당 코드값들은 모두 http status값을 9400으로 반환합니다.

  • CommonAppError.class

(Common에 있는 것으로 공통 0000, 9000번대 코드값들을 정의함) 해당 Class는 공통에서 관리되기 때문에 이러한 Code값들이 관리가 되고 있는 수준만 확인하면 됩니다.

  • DisplayApiError.class

(DisplayApiError등등 각 API서버들의 Error Class)

UI쪽에서 사용하는 isProcess 필드값이 추가가 되었습니다.

해당값은 기본값이 false이기 때문에 모든 메시지 값을 false로 쓸 경우, default 메서드가 false로 작동하기 때문에 다음과 같이 작성해주시면 됩니다.

isProcess 필드값을 활용하시는 경우에는 다음과 같이 getIsProcess 함수를 Override하는 부분을 추가 해야 되며, 다음과 같이 마지막 항목에 false, true값 등을 모두 명시해 주셔야 합니다.

이렇게 각 서버 프로젝트들에서는 다음과 같이 사용할 Error값들을 1000 ~ 8999번대 코드로 정의해 주시면 됩니다.

※ BO프로젝트 같은 경우에는 타임리프와 같은 페이지 호출이 같이 있기 때문에 서버페이지를 호출할 때 Exception이 발생하면 ControllerAdvice Class에서 다음과 같은 분기 로직이 존재합니다.

  • GlobalErrorController.java

(BO같은 경우에는 error페이지로 보내는 경우가 존재함)

해당 Class파일은 BO 프로젝트에서만 존재합니다.

ControllerAdvice에서 api 호출이 아닌 경우에는 GlobalErrorController로 쪽으로 리디렉션 시켜서 error 페이지를 호출하는 정도입니다.

에러메시지 처리

에러메시지 처리의 경우 FO, BO Message 처리 부분인 해당 내용을 참고하면 됩니다.

Message값을 처리하기 위해서 공통에서 MessageResolver Class를 제공하고 있습니다.

MessageResolver.class

getLocaleMessage함수가 메시지값을 가져오는 주요 함수로서 다음과 같이 작동합니다.

  1. messageKey값이 String Key 인자의 함수인 경우에는 그대로 메지시값 반환

  2. messageKey값이 Emum Class 인자의 함수인 경우에는 RequestContextHolder 객체의 헤더값에서 호출한 서버명을 인식하여, BO인 경우 2번째 Message 인자값인 boMessageKey의 메시지값을 반환, 호출한 서버명이 BO쪽이 아닌 경우 기존 messageKey 그대로 메시지값을 반환

사용법은 다음과 같습니다.

  • ApiError Class 파일 작성

해당 enum class파일에 code값 및 FO message, BO message키값을 정의 합니다.

BO message값이 따로 없을 경우 FO message값을 동일하게 적용합니다.

  • message properties 파일 작성

event_ko.properties, event_en.properties 등등 message properties파일을 정리합니다.

해당 message properties파일에 FO 및 BO에서 사용할 메시지값을 정의함.

  • 비지니스 로직에서 사용

마지막 업데이트