# Exception Handling

본 문서에서는 X2BEE Framework 에서의 예외 처리 방법과 관련된 내용을 기술합니다.

{% stepper %}
{% step %}

### 에러메시지 및 예외 처리

#### 에러 메시지 처리

API 오류 처리 Response 객체에 Error 내용을 담아 Return

{% code title="GlobalControllerAdvice.java" %}

```java
package com.x2bee.common.base.exception;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Optional;

import com.x2bee.common.base.rest.HttpException;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.boot.json.JsonParseException;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.x2bee.common.base.context.ConfigProperties;
import com.x2bee.common.base.filter.RequestLoggingFilter;
import com.x2bee.common.base.rest.Response;
import com.x2bee.common.base.rest.ValidationError;
import com.x2bee.common.base.util.RequestUtils;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.persistence.EntityNotFoundException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GlobalControllerAdvice {
    private String attributeError = "error";

    @ExceptionHandler(HttpInterfaceResponseException.class)
    protected ResponseEntity<Object> handleHttpInterfaceException(HttpInterfaceResponseException e, WebRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        String code = Optional.ofNullable(e.getErrorCode()).orElse(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()));
        String message = e.getErrorMessage();
        HttpStatus httpStatus = HttpStatus.resolve(Integer.valueOf(code));
        if (httpStatus == null) {
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        }
        // HttpInterfaceResponseException의 경우 message값에 json 데이터가 넘어오기 때문에
        // ErrorCode로 반환하지 않고 Object형태로 그대로 반환함.
        return new ResponseEntity<>(message, httpStatus);
    }

    @ExceptionHandler(HttpException.class)
    protected ResponseEntity<Object> handleHttpException(HttpException e, WebRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        String code = Optional.ofNullable(e.getErrorCode()).orElse(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()));
        String message = e.getErrorMessage();
        int httpStatus = Integer.parseInt(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(WebClientResponseException.class)
    protected ResponseEntity<Object> handleWebClientResponseException(WebClientResponseException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.error("", e);
        String code = Optional.ofNullable(String.valueOf(e.getStatusCode().value())).orElse(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(WebClientRequestException.class)
    protected ResponseEntity<Object> handleWebClientRequestException(WebClientRequestException 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(AsyncRequestTimeoutException.class)
    protected ResponseEntity<Object> handleAsyncRequestTimeoutException(AsyncRequestTimeoutException 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(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(value = { NullPointerException.class, IOException.class, ArrayIndexOutOfBoundsException.class, EntityNotFoundException.class, StringIndexOutOfBoundsException.class, IndexOutOfBoundsException.class, UnsupportedEncodingException.class })
    protected ResponseEntity<Object> handleErrorException(Exception e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.error("handleErrorException {}", 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(value = { IllegalArgumentException.class, IllegalStateException.class, ConstraintViolationException.class, JsonParseException.class, com.fasterxml.jackson.core.JsonParseException.class, HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class, MissingServletRequestParameterException.class, MultipartException.class })
    protected ResponseEntity<Object> handleIllegalException(Exception e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.error("handleIllegalException {}", 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(value = { DataAccessException.class, BadSqlGrammarException.class })
    protected ResponseEntity<Object> handleSqlException(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 = MessageResolver.getMessage(CommonAppError.SYSTEM_FAIL);
        int httpStatus = Integer.parseInt(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(ValidationException.class)
    protected ResponseEntity<Object> handleValidationException(ValidationException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn(e.getMessage(), e);
        String code = CommonAppError.VALIDATION_EXCEPTION.getCode();
        String message = e.getMessage();
        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(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);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException 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);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    protected ResponseEntity<Object> handleNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("", e);
        String code = String.valueOf(HttpStatus.METHOD_NOT_ALLOWED.value());
        String message = e.getMessage();
        int httpStatus = getBadRequestHttpStatusCode(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("", e);
        String code = String.valueOf(HttpStatus.NOT_FOUND.value());
        String message = e.getMessage();
        int httpStatus = getBadRequestHttpStatusCode(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

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

    @ExceptionHandler(AuthenticationException.class)
    protected ResponseEntity<Object> handleAuthenticationException(AuthenticationException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("AuthenticationException: {}", e.getMessage());
        log.warn("", e);
        String code = String.valueOf(HttpStatus.UNAUTHORIZED.value());
        String message = e.getMessage();
        int httpStatus = getBadRequestHttpStatusCode(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(JwtException.class)
    protected ResponseEntity<Object> handleJwtException(JwtException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("JwtException: {}", e.getMessage());
        log.warn("", e);
        String code = String.valueOf(HttpStatus.UNAUTHORIZED.value());
        String message = e.getMessage();
        int httpStatus = getBadRequestHttpStatusCode(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(AccessDeniedException.class)
    protected ResponseEntity<Object> handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("AccessDeniedException: {}", e.getMessage());
        log.warn("", e);
        String code = String.valueOf(HttpStatus.FORBIDDEN.value());
        String message = e.getMessage();
        int httpStatus = getBadRequestHttpStatusCode(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    @ExceptionHandler(ExpiredJwtException.class)
    protected ResponseEntity<Object> handleExpiredJwtException(ExpiredJwtException e, HttpServletRequest request) {
        RequestUtils.setAttribute(RequestLoggingFilter.REQUEST_LOG_LEVEL, this.attributeError);
        log.warn("ExpiredJwtException: {}", e.getMessage());
        log.warn("", e);
        String code = String.valueOf(HttpStatus.FORBIDDEN.value());
        String message = e.getMessage();
        int httpStatus = getBadRequestHttpStatusCode(code);
        ErrorCode errorCode = ErrorCode.builder()
                .code(code)
                .message(message)
                .httpStatus(httpStatus)
                .build();
        return handleExceptionInternal(errorCode);
    }

    private String getBindingErrorMessage(BindingResult bindingResult) {
        if (bindingResult.hasFieldErrors()) {
            List<FieldError> errors = bindingResult.getFieldErrors();
            if (CollectionUtils.isNotEmpty(errors)) {
                FieldError error = errors.get(0);
                return error.getDefaultMessage();
            } else {
                return MessageResolver.getMessage(CommonAppError.BINDING_ERROR);
            }
        } else {
            return MessageResolver.getMessage(CommonAppError.BINDING_ERROR);
        }
    }

    private ResponseEntity<Object> handleExceptionInternal(ErrorCode errorCode) {
        ResponseEntity<Object> body = ResponseEntity.status(errorCode.getHttpStatus())
                .body(makeErrorResponse(errorCode));
        return body;
    }

    private Response<Object> makeErrorResponse(ErrorCode errorCode) {
        return Response.builder()
                .code(errorCode.getCode())
                .message(errorCode.getMessage())
                .error(true)
                .build();
    }

    private ResponseEntity<Object> handleExceptionInternal(BindException e, ErrorCode errorCode) {
        return ResponseEntity.status(errorCode.getHttpStatus())
                .body(makeErrorResponse(e, errorCode));
    }

    private Response<Object> makeErrorResponse(BindException e, ErrorCode errorCode) {
        List<ValidationError> validationErrorList = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(ValidationError::of)
                .toList();
        return Response.builder()
                .code(errorCode.getCode())
                .message(errorCode.getMessage())
                .error(true)
                .errors(validationErrorList)
                .build();
    }

    private int getBadRequestStartPrefix() {
        Integer badRequestStartPrefix = null;
        try {
            badRequestStartPrefix = ConfigProperties.getInstance().getIntValue("http-status-bad-request-start-prefix");
        } catch (Exception ex) {
            badRequestStartPrefix = null;
        }
        if (badRequestStartPrefix == null) {
            badRequestStartPrefix = 400;
        }
        return badRequestStartPrefix;
    }

    private int getBadRequestHttpStatusCode(String httpStatusCode) {
        int badRequestStartPrefix = getBadRequestStartPrefix();
        String lastCode = httpStatusCode.substring(httpStatusCode.length() - 2);
        int httpStatus = badRequestStartPrefix + Integer.valueOf(lastCode);
        return httpStatus;
    }
}
```

{% endcode %}

Response 객체에 Error 내용을 담아 Return

{% code title="Response.java" %}

```java
package com.x2bee.common.base.rest;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;

@Getter
@Setter
@NoArgsConstructor
@ToString
@Accessors(chain = true)
public class Response<T> {

    @Schema(description = "result time")
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
    private LocalDateTime timestamp = LocalDateTime.now();

    @Schema(description = "result code")
    private String code = "0000";

    @Schema(description = "result message")
    private String message = "";

    @Schema(description = "process check")
    private Boolean isProcess;

    @Schema(description = "payload")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private T payload;

    @Schema(description = "is error")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Boolean error = null;

    @Schema(description = "validation error list")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<ValidationError> errors = new ArrayList<>();

    @Builder
    public Response(String code, String message, T payload, Boolean error, List<ValidationError> errors, Boolean isProcess) {
        if (code != null) {
            this.code = code;
        }
        this.message = message;
        this.payload = payload;
        this.error = error;
        this.isProcess = isProcess;
        if (errors != null && !errors.isEmpty()) {
            this.errors = errors;
        }
    }

    public Response<Object> setErrorResponse(String code, String message, Boolean isProcess) {
        return Response.builder()
                .code(code)
                .message(message)
                .isProcess(isProcess)
                .error(true)
                .build();
    }
}
```

{% endcode %}

Error Code 정의

{% code title="ApiError.java" %}

```java
package com.x2bee.api.common.base.advice;

import com.x2bee.common.base.exception.AppError;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ApiError implements AppError {
    // success
    SUCCESS("0000", "common.message.success", "common.message.success", false),

    // app error
    EMPTY_PARAMETER("1001", "common.error.emptyParameter", "common.error.emptyParameter", false),
    INVALID_PARAMETER("1002", "common.error.invalidParameter", "common.error.invalidParameter", false),
    DATA_NOT_FOUND("1003", "common.error.dataNotFound", "common.error.dataNotFound", false),
    DATA_INVALID_PARAMETER("1004", "common.error.dataInvalidParameter", "common.error.dataInvalidParameter", false),
    INVALID_FILE("1005", "common.error.invalidFile", "common.error.invalidFile", false),
    DUPLICATE_DATA("1007", "common.error.duplicateData", "common.error.duplicateData", false),
    TROUBLE_NETWORK("1008", "common.error.trouble.network", "common.error.trouble.network", false),
    TROUBLE_EXCEPTION("1009", "common.error.trouble.exception", "common.error.trouble.exception", false),
    UPLOAD_FAIL("1100", "common.error.uploadFail", "common.error.uploadFail", false),

    // ERP
    ERP_ERROR_MESSAGE("2000", "common.erp.errorMessage", "common.erp.errorMessage", false),

    // 결제관련
    PAYMENT_TOSS_NOT_INCLUDE("4001" , "paymentCommon.toss.not.include", "paymentCommon.toss.not.include", false),
    PAYMENT_CUSTOMERKEY_NOT_SAME("4002" , "paymentCommon.customerkey.not.same", "paymentCommon.customerkey.not.same", false),
    PAYMENT_REQUIED_NOT_FIND("4003" , "paymentCommon.requied.not.find", "paymentCommon.requied.not.find", false),
    PAYMENT_COMMON_IN_APRL_ERROR("4004" , "paymentCommon.in.aprl.error", "paymentCommon.in.aprl.error", false),
    PAYMENT_COMMON_CNCL_IN_APRL_SUC("4005" , "paymentCommon.cncl.in.aprl.suc", "paymentCommon.cncl.in.aprl.suc", false),
    PAYMENT_COMMON_CNCL_IN_APRL_ERROR("4006" , "paymentCommon.cncl.in.aprl.error", "paymentCommon.cncl.in.aprl.error", false),
    PAYMENT_COMMON_IN_CNCL_ERROR("4010" , "paymentCommon.in.cncl.error", "paymentCommon.in.cncl.error", false),
    PAYMENT_COMMON_IN_CNCL_SUC("4011" , "paymentCommon.in.cncl.suc", "paymentCommon.in.cncl.suc", false),
    PAYMENT_RESPONSE_EMPTY("4012" , "paymentCommon.response.empty", "paymentCommon.response.empty", false),

    // 권한 없음.
    NOT_AUTHORIZED("7000", "common.error.notAuthorized", "common.error.notAuthorized", false),

    // binding error
    BINDING_ERROR("8000", "common.error.bindingError", "common.error.bindingError", false),
    BINDING_ERROR_NOT_NULL("8001", "common.error.bindingErrorNotNull", "common.error.bindingErrorNotNull", false),

    // unknow error
    UNKNOWN("9000", getMessageUnknown(), getMessageUnknown(), false),

    // ValidatioException error
    VALIDATION_EXCEPTION("9100", getMessageUnknown(), getMessageUnknown(), false);

    private final String code;
    private final String messageKey;
    private final String boMessageKey;
    private final Boolean isProcess;

    @Override
    public boolean getIsProcess() {
        return isProcess;
    }

    private static String messageUnknown = "common.error.unknown";

    public static String getMessageUnknown() {
        return messageUnknown;
    }
}
```

{% endcode %}
{% endstep %}

{% step %}

### 예외 처리 기능 (x2bee-common 패키지 하위 Exception 클래스 구현)

* src/main/java 아래에 패키지 생성 후 Exception 코드 작성

예시 1

{% code title="CommonException.java" %}

```java
package com.x2bee.common.base.exception;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CommonException extends UserDefinedException {
    private static final long serialVersionUID = 1L;

    private final String errorCode;
    private final String errorMessage;

    public CommonException(String message, Throwable cause) {
        super(message, cause);
        this.errorCode = "500";
        this.errorMessage = message;
    }

    public CommonException(String message) {
        super(message);
        this.errorCode = "500";
        this.errorMessage = message;
    }

    public CommonException(Throwable cause) {
        super(cause);
        this.errorCode = "500";
        this.errorMessage = cause.getMessage();
    }

    public CommonException(String errorCode, String errorMessage) {
        super(errorMessage);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
}
```

{% endcode %}

예시 2

{% code title="ValidationException.java" %}

```java
package com.x2bee.common.base.exception;

@SuppressWarnings("serial")
public class ValidationException extends UserDefinedException {
    public ValidationException() {
        super();
    }

    public ValidationException(String message) {
        super(message);
    }

    public ValidationException(Throwable t) {
        super(t);
    }

    public ValidationException(String message, Throwable t) {
        super(message, t);
    }
}
```

{% endcode %}
{% endstep %}
{% endstepper %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tech.x2bee.com/dev-guide/pjt-prepare/framework/exception-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
