SpringBoot + React Serving

cheonbi·2025년 5월 20일
0

Java

목록 보기
3/3

SpringBoot 서버만 실행시켰을 때 React로 작성된 웹 앱이 같이 동작되게 할려면?

보통 SpringBoot 내에서 정적리소스를 Serving한다고 하면

브라우저 요청 → DispatcherServlet → Controller → ViewResolver(Thymeleaf) → HTML 렌더링 → 응답

위 과정을 거치게 된다.

1. DispatcherServlet이 요청을 받음
Spring Boot 내장 톰캣 서버는 클라이언트의 HTTP 요청을 DispatcherServlet으로 전달
이 서블릿은 Spring MVC의 핵심이며 요청을 적절한 컨트롤러로 라우팅

2. 컨트롤러에서 데이터와 뷰 이름 반환

@GetMapping("/home")
public String home(Model model) {
    model.addAttribute("msg", "Hello!");
    return "home";  // "home.html" 템플릿을 의미
}
  • Model에 데이터를 담고
  • "home"이라는 이름의 뷰(View)를 반환합니다.

3. ViewResolver가 템플릿 위치와 확장자 지정
ThymeleafViewResolver가 아래 설정을 기반으로 뷰 파일 경로를 찾음

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html

"home" → classpath:/templates/home.html

4. Thymeleaf Template Engine이 HTML 처리
템플릿 안의 th:* 속성을 파싱해, Model에 있는 값으로 동적으로 치환합니다.

<h1 th:text="${msg}">기본 텍스트</h1>

msg = "Hello!" 이므로 실제 HTML은

<h1>Hello!</h1>

5. 최종 HTML을 응답
렌더링된 HTML이 DispatcherServlet을 통해 다시 클라이언트로 전송

구성 요소역할
DispatcherServlet요청을 Controller로 전달하고 응답을 다시 반환
Controller데이터 처리 후 View 이름 반환
ModelView에 전달할 데이터
ThymeleafViewResolverView 이름 → 실제 템플릿 파일 매핑
Thymeleaf Template EngineHTML 템플릿 렌더링
templates/ 디렉토리템플릿 파일이 위치하는 곳

만약 SPA(React,Vue)로 구성된 정적파일을 서빙한다고 하면?
pring Boot는 정적 파일을 서빙하는 서버 역할만 하게 되며, 다음과 같은 흐름으로 이루어 진다.

브라우저 요청 → Spring Boot 정적 리소스 핸들러 → index.html → React Router가 이후 라우팅 처리

1. React 빌드
Vite로 빌드를 해서 Build 설정에 따라 빌드 결과물(정적파일들)이 나오게 된다.

  build: {
    // remove previous build files
    emptyOutDir: true,
    // static assets directory
    assetsDir: "assets",

    minify: "terser",
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    cssCodeSplit: true,
    target: "es2019",

    rollupOptions: {
      output: {
        entryFileNames: "[name].js",
        assetFileNames: "[name].[ext]",
        chunkFileNames: "[name].js",
        manualChunks: {
          react: ["react", "react-dom"],
          reactRouter: ["react-router-dom"],
          chartJs: ["chart.js"],
        },
      },
    },
  },

2. Spring Boot와 통합 구조
React build 결과를

Spring Boot의 resources/static 혹은 resources/public 아래로 복사

src/
└── main/
    └── resources/
        └── static/
            ├── index.html
            └── static/
                ├── js/
                └── css/

3. React에서 라우팅 처리
React에서 react-router-dom을 쓰고 있다면, 사용자가 /about, /dashboard 같은 SPA 경로로 접근
그러므로 Spring Boot가 모든 경로를 index.html로 포워딩하도록 설정이 필요

Controller Forward

@Controller
public class SpaController {

    @GetMapping(value = { "/", "/{path:^(?!api).*}" })
    public String forward() {
        return "forward:/index.html";
    }
}
  • /api/** 요청은 백엔드 API로 보내고
  • 나머지 모든 요청은 index.html로 전달하여 React가 라우팅 처리

일반적인 구조

ViewTemplate를 사용하는 경우

Routing

요청: /k6mt-surfer/dashboard
     ↓
addResourceHandlers → 파일 존재 여부 확인
     ↓
없으면 fallback → index.html 반환
     ↓
React 앱이 내부 라우팅으로 처리 (SPA)

ViewTemplate Engine(정적리소스)를 사용할 때, 관련 URL이 한가지 정적리소스로 forwarding

1. @Configuration + WebMvcConfigurer

  • Spring Boot가 웹 애플리케이션일 경우
  • WebMvcAutoConfiguration 이후에
  • 커스텀 WebMvc 설정을 추가로 수행하게 됨

2. addViewControllers → 기본 진입점 리다이렉트

registry.addViewController("/k6mt-surfer").setViewName("forward:/k6mt-surfer/index.html");
registry.addViewController("/k6mt-surfer/").setViewName("forward:/k6mt-surfer/index.html");
  • 사용자가 /k6mt-surfer 또는 /k6mt-surfer/에 접근하면
  • 자동으로 /k6mt-surfer/index.html로 포워딩
  • 이는 SPA 앱의 기본 진입점 역할

3. addResourceHandlers → 정적 리소스 매핑

registry.addResourceHandler("/k6mt-surfer/**")
    .addResourceLocations("classpath:/META-INF/resources/k6mt-surfer/")

classpath:/META-INF/resources/k6mt-surfer/ 내부에 있는
정적 파일(html/js/css 등)을 /k6mt-surfer/** 경로에 매핑

4. .resourceChain().addResolver() → SPA 라우팅 처리

@Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
  Resource requested = location.createRelative(resourcePath);
  return (requested.exists() && requested.isReadable())
      ? requested
      : location.createRelative("index.html"); // fallback
}
  • 요청된 리소스가 실제 파일이면 해당 리소스를 제공하고
  • 아니라면 (SPA 내부 라우팅 URL인 경우),
    → index.html을 대신 반환해서 React 등의 SPA 앱이 라우팅 처리할 수 있게 함
요청: /k6mt-surfer/dashboard
실제 파일 존재 ❌ → index.html 반환 → React Router가 페이지 렌더링

SPA 앱들은 /#/, /dashboard, /settings 등의 경로를 내부적으로 처리하지만
서버는 이 경로를 알지 못함 → 정적 파일이 아니면 404 발생

그래서 다음이 필요함 ⏩

  • 정적 리소스 매핑 (addResourceHandler)
  • fallback 처리 (PathResourceResolver)
  • 진입점 포워딩 (addViewControllers)
profile
༼ つ ◕_◕ ༽つ

0개의 댓글