common: exception handling policy - takeoff-26/logistics-service GitHub Wiki
exception handling policy
MSA์์๋ ์ ์ญ์ ์ธ exception์ ๋ํ ๊ด๋ฆฌ๊ฐ ํ์ํ๋ค.
exception์ ๋ํด ๊ณตํต๋ ์ฒ๋ฆฌ, ๋ฐํ๋๋ ์๋ฌ ์ฝ๋๋ ์๋ต ํ์์ ํต์ผํด์ผ ํ๋ฉฐ ๊ฐ๊ฐ ๋น์ฆ๋์ค์ ๋ง์ถฐ ํด๋น ์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํด ๋ฐํํ๊ฒ ๋๋ ๋ฉ์์ง๋ ๋ค๋ฅผ ๊ฒ์ด๋ค.
์๋ต๊ณผ ์ฝ๋์ ๋ํด์๋ ํต์ผ์ฑ์ ๊ฐ์ง๋ ์ฌ์ฉ๋๊ณ ๋ฐํ๋๋ ๋ฉ์์ง์ ๋ํ ๊ฐ์ ์ผ๊ด์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ๋ฒํํ๊ธฐ ์ํด ์์ฑํ๋ค.
๊ณตํต์ผ๋ก ์ ์ฉ๋ ๋ถ๋ถ์ด๋ค ๋ณด๋ ์ญ์ Common์ ์์นํ๊ฒ ๋์๊ณ ์ด๋ฒ์๋ exception์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ๋ ์ธ๋ถํํด ์ฒ๋ฆฌํ๊ฒ ๋์๋ค.
์๋ฌ์ฒ๋ฆฌ์ ๋ํ ํ๋ฆ์ ์ด์ ๊ณผ ๋น์ทํ๋ ๊ทธ๋ฆผ์ผ๋ก ํ์ธํ๊ณ ๋ก์ง์ผ๋ก ์งํํ๋๋ก ํ๊ฒ ๋ค.
@Getter
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
public static BusinessException from(ErrorCode errorCode) {
return new BusinessException(errorCode);
}
protected BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
public record ErrorResponse(String code, String message, int status, List<ValidationError> errors) {
static final String ILLEGAL_ERROR_CODE = "ILL_001";
public static ErrorResponse of(ErrorCode errorCode) {
return new ErrorResponse(
errorCode.getCode(),
errorCode.getMessage(),
errorCode.getStatus(),
Collections.emptyList()
);
}
public static ErrorResponse of(String message, int status) {
return new ErrorResponse(
ILLEGAL_ERROR_CODE,
message,
status,
Collections.emptyList()
);
}
public static ErrorResponse of(BindingResult bindingResult) {
return new ErrorResponse(
VALIDATION_ERROR.getCode(),
VALIDATION_ERROR.getMessage(),
VALIDATION_ERROR.getStatus(),
ValidationError.ofFieldErrors(bindingResult.getFieldErrors())
);
}
public static ErrorResponse of(Set<ConstraintViolation<?>> violations) {
return new ErrorResponse(
CONSTRAINT_VIOLATION.getCode(),
CONSTRAINT_VIOLATION.getMessage(),
CONSTRAINT_VIOLATION.getStatus(),
ValidationError.ofConstraintViolations(violations)
);
}
public record ValidationError(
String field,
String rejectedValue,
String reason
) {
private static List<ValidationError> ofFieldErrors(
List<org.springframework.validation.FieldError> fieldErrors) {
return fieldErrors.stream()
.map(error -> new ValidationError(
error.getField(),
error.getRejectedValue() == null ?
"" : String.valueOf(error.getRejectedValue()),
error.getDefaultMessage()))
.toList();
}
private static List<ValidationError> ofConstraintViolations(
Set<ConstraintViolation<?>> constraintViolations) {
return constraintViolations.stream()
.map(violation -> new ValidationError(
violation.getPropertyPath().toString(),
violation.getInvalidValue() == null ?
"" : String.valueOf(violation.getInvalidValue()),
violation.getMessage()))
.toList();
}
}
}
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
ErrorResponse errorResponse = ErrorResponse.of(e.getErrorCode());
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(
MethodArgumentNotValidException e) {
ErrorResponse errorResponse = ErrorResponse.of(e.getBindingResult());
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolationException(
ConstraintViolationException e) {
ErrorResponse errorResponse = ErrorResponse.of(e.getConstraintViolations());
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException(
HttpRequestMethodNotSupportedException e) {
log.error(e.getMessage(), e);
ErrorResponse errorResponse = ErrorResponse.of(METHOD_NOT_ALLOWED);
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error(e.getMessage(), e);
ErrorResponse errorResponse = ErrorResponse.of(INTERNAL_SERVER_ERROR);
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
@ExceptionHandler(IllegalStateException.class)
public ResponseEntity<ErrorResponse> handleIllegalStateException(IllegalStateException e) {
ErrorResponse errorResponse = ErrorResponse.of(e.getMessage(), HttpStatus.CONFLICT.value());
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException e) {
ErrorResponse errorResponse = ErrorResponse.of(e.getMessage(), HttpStatus.BAD_REQUEST.value());
return ResponseEntity.status(errorResponse.status()).body(errorResponse);
}
}
public interface ErrorCode {
String getCode();
String getMessage();
int getStatus();
}
์ ErrorCode๋ฅผ ์์ ๋ฐ์ ๊ฐ ๋น์ฆ๋์ค์์ ๋ณธ์ธ์ ์๋ฌ์ฒ๋ฆฌ์ ๋ง๋ ๋ฉ์์ง๋ฅผ ์ค์ ํ๊ณ ์ฌ์ฉํ๊ฒ ๋๋ค.