TA 역할 Spring 경계 smoke #3

Open
forge-bot wants to merge 9 commits from forge/role-ta-live-1522-001-attempt-1-run-6e84b72613b1 into main
Showing only changes of commit db3560a819 - Show all commits

View file

@ -0,0 +1,65 @@
package com.runtimematrix.config;
import com.runtimematrix.controller.dto.ErrorResponse;
import com.runtimematrix.domain.exception.BusinessException;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.Instant;
import java.util.UUID;
import java.util.stream.Collectors;
/** 전역 예외 처리기 - RFC 7807 Problem Details 형식으로 변환 */
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex, HttpServletRequest request) {
String traceId = getOrGenerateTraceId();
MDC.put("traceId", traceId);
log.warn("[{}] {} - {}", traceId, ex.getErrorCode(), ex.getMessage());
return ResponseEntity.status(ex.getHttpStatus())
.body(ErrorResponse.of(ex.getType(), ex.getErrorCode(), ex.getMessage(),
ex.getHttpStatus(), request.getRequestURI(), traceId));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex, HttpServletRequest request) {
String traceId = getOrGenerateTraceId();
String detail = ex.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage).collect(Collectors.joining("; "));
log.warn("[{}] Validation failed: {}", traceId, detail);
return ResponseEntity.badRequest()
.body(ErrorResponse.of("https://api.runtimematrix.com/errors/validation-failed",
"validation-failed", "입력 검증에 실패했습니다: " + detail,
HttpStatus.BAD_REQUEST.value(), request.getRequestURI(), traceId));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex, HttpServletRequest request) {
String traceId = getOrGenerateTraceId();
log.error("[{}] Unexpected error", traceId, ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ErrorResponse.of("https://api.runtimematrix.com/errors/internal-server-error",
"internal-server-error", "예상치 못한 오류가 발생했습니다.",
HttpStatus.INTERNAL_SERVER_ERROR.value(), request.getRequestURI(), traceId));
}
private String getOrGenerateTraceId() {
String existing = MDC.get("traceId");
return existing != null ? existing : UUID.randomUUID().toString().substring(0, 12);
}
}