guardia-mall/backend/src/main/java/com/zioinfo/mall/common/GlobalExceptionHandler.java

58 lines
2.3 KiB
Java

package com.zioinfo.mall.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
/**
* 전역 예외 처리.
*
* <p>보안 불변 규칙: 스택트레이스를 응답에 절대 노출하지 않는다.
* 에러 코드 + 요약 메시지만 반환한다.
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MaxUploadSizeExceededException.class)
@ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
public ApiResponse<Void> handleMaxSize(MaxUploadSizeExceededException e) {
log.warn("업로드 크기 초과: {}", e.getMessage());
return ApiResponse.fail("ERR-MALL-413: 파일 크기 초과 (최대 20MB)");
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<Void> handleIllegalArg(IllegalArgumentException e) {
log.warn("잘못된 요청: {}", e.getMessage());
return ApiResponse.fail(e.getMessage());
}
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<Void> handleRuntime(RuntimeException e) {
// 스택트레이스 미노출 — 에러 코드/요약만 반환
log.warn("업무 오류: {}", e.getMessage());
return ApiResponse.fail(e.getMessage());
}
/** 정적 리소스 미존재 — 500 으로 감싸지 않고 깔끔히 404 반환 (SPA 폴백 이후의 실제 미스). */
@ExceptionHandler(NoResourceFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ApiResponse<Void> handleNoResource(NoResourceFoundException e) {
log.warn("리소스 없음: {}", e.getResourcePath());
return ApiResponse.fail("ERR-MALL-404: 리소스를 찾을 수 없습니다");
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ApiResponse<Void> handleGeneral(Exception e) {
log.error("시스템 오류", e);
return ApiResponse.fail("ERR-SYS-001");
}
}