fix(spa): add SPA forward controller(/admin SPA deeplink)+NoResourceFound->404 — serve admin route, clean 404 for missing static
This commit is contained in:
parent
4ce97d8807
commit
335a888b48
@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||||
|
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 전역 예외 처리.
|
* 전역 예외 처리.
|
||||||
@ -39,6 +40,14 @@ public class GlobalExceptionHandler {
|
|||||||
return ApiResponse.fail(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)
|
@ExceptionHandler(Exception.class)
|
||||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
public ApiResponse<Void> handleGeneral(Exception e) {
|
public ApiResponse<Void> handleGeneral(Exception e) {
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.zioinfo.mall.config;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPA(React Router) 폴백 — 클라이언트 라우트 딥링크/새로고침 지원.
|
||||||
|
*
|
||||||
|
* <p>스토어프론트(/, /product/**, /cart, /checkout ...) 와 관리자(/admin/**) 는
|
||||||
|
* 모두 단일 index.html 위에서 React Router 가 처리한다. 서버에 해당 정적 파일이
|
||||||
|
* 없으므로, API/정적자원이 아닌 모든 GET 요청을 index.html 로 forward 한다.
|
||||||
|
*
|
||||||
|
* <p>제외 대상: /api/**, /actuator/**, /ws/**, /swagger-ui/**, /v3/**,
|
||||||
|
* 그리고 점(.)이 포함된 실제 정적 리소스(*.js, *.css, *.jpg, *.png ...)는
|
||||||
|
* Spring 기본 ResourceHandler 가 먼저 처리하므로 여기로 오지 않는다.
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class SpaForwardController {
|
||||||
|
|
||||||
|
// 점(.)이 없는 단일/다중 세그먼트 경로 → SPA 라우트로 간주하고 index.html 로 forward.
|
||||||
|
// 정규식: 한 개 이상의 경로 세그먼트, 각 세그먼트에 '.' 미포함.
|
||||||
|
@RequestMapping(value = {
|
||||||
|
"/admin", "/admin/**",
|
||||||
|
"/{path:^(?!api|actuator|ws|swagger-ui|v3|assets|img|images|static)[^.]*}",
|
||||||
|
"/{path:^(?!api|actuator|ws|swagger-ui|v3|assets|img|images|static)[^.]*}/**"
|
||||||
|
})
|
||||||
|
public String forwardSpa() {
|
||||||
|
return "forward:/index.html";
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user