Spring Boot で Vue3/React のフロントビルドを同梱して自動配信する構成
Spring Boot Vue3 Vite Maven
はじめに
Vue 3 + Spring Boot の分離構成で開発していると、本番デプロイ時に悩む問題があります。
- フロントのビルド成果物はどこに置く?
- Spring BootはどうやってVueの静的ファイルを配信する?
- Mavenでビルドするとき、フロントのnpmビルドも一緒に実行できないか?
この記事は「mvn package 一発でフロントのビルドも含めた実行可能jarを作る構成」の解説です。
最終的な構成
dvd-rental-customer-app/
backend/
pom.xml ← Maven設定(frontendビルドも含む)
src/main/resources/
static/ ← フロントのビルド成果物の配置先
(ビルド後に自動配置される)
frontend/
package.json
vite.config.ts
src/
App.vue
...
ステップ1:Viteのビルド出力先を変更
デフォルトでは frontend/dist/ に出力されますが、Spring Bootが配信できる場所に変更します。
// frontend/vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
build: {
outDir: resolve(__dirname, '../backend/src/main/resources/static'),
emptyOutDir: true, // ビルド前に static/ をクリア
},
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8082',
changeOrigin: true,
}
}
}
})
npm run build すると backend/src/main/resources/static/ に成果物が入ります。
ステップ2:frontend-maven-plugin の設定
<!-- backend/pom.xml -->
<build>
<plugins>
<!-- Nodeのインストールとnpmビルドを自動実行 -->
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.15.0</version>
<configuration>
<!-- フロントのディレクトリ(pom.xmlからの相対パス) -->
<workingDirectory>../frontend</workingDirectory>
<!-- Node/npmのインストール先 -->
<installDirectory>target</installDirectory>
</configuration>
<executions>
<!-- Node.js と npm のインストール -->
<execution>
<id>install-node-and-npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v22.0.0</nodeVersion>
<npmVersion>10.5.2</npmVersion>
</configuration>
</execution>
<!-- npm install -->
<execution>
<id>npm-install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<!-- npm run build -->
<execution>
<id>npm-build</id>
<goals>
<goal>npm</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
ステップ3:Spring Boot の静的ファイル配信設定
Spring Bootはデフォルトで classpath:/static/ を静的ファイルのルートとして使います。
特別な設定は不要ですが、念のため明示する場合:
# application.yml
spring:
web:
resources:
static-locations: classpath:/static/
ステップ4:Vue Routerとの組み合わせ(SPA対応)
Vue RouterでHistory APIを使っている場合、ブラウザで直接 /films/123 にアクセスすると
Spring Bootが /films/123 というパスを探してしまい 404 になります。
// SPAのルーティングをSpring Boot側で受け取る設定
@Controller
public class SpaForwardController {
@GetMapping(value = {"/", "/{path:[^\\.]*}", "/{path:^(?!api|actuator).*$}/**"})
public String forward() {
return "forward:/index.html";
}
}
/api/**→ REST APIエンドポイントで処理- それ以外 →
index.htmlを返す(Vue Routerが受け取って処理)
ビルド時の動作
# mvn package を実行すると...
mvn -f backend/pom.xml package
# 1. frontend-maven-plugin が Node.js をダウンロード(初回のみ)
# 2. npm install を実行
# 3. npm run build を実行 → backend/src/main/resources/static/ に出力
# 4. Spring Boot の jar にすべて同梱
# 5. 実行可能 jar が生成される
生成されたjarを実行するだけでフロント+バックの両方が動きます。
java -jar backend/target/dvd-rental-customer-backend-0.1.0-SNAPSHOT.jar
開発時のフロー
本番ビルドと開発フローを分けることが重要です。
# 開発時(2プロセス並行)
# ターミナル1: Viteの開発サーバー(HMR有効、ポート5173)
cd frontend && npm run dev
# ターミナル2: Spring Boot(ポート8082)
cd backend && ../mvnw spring-boot:run -Dspring-boot.run.profiles=local
開発時はViteのHMR(Hot Module Replacement)が使えるため、ファイル変更が即座にブラウザに反映されます。
Docker との組み合わせ
Dockerfileでも同様の手順でビルドできます。
# マルチステージビルド
FROM node:22-alpine AS frontend-build
WORKDIR /frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ .
RUN npm run build
FROM eclipse-temurin:21-jdk AS backend-build
WORKDIR /app
COPY backend/ .
# フロントのビルド成果物を static/ にコピー
COPY --from=frontend-build /frontend/dist ./src/main/resources/static/
RUN ./mvnw package -DskipTests -Dfrontend.skip=true
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=backend-build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
まとめ
frontend-maven-pluginを使うとmvn package一発でフロントのビルドも含めた jar が作れる- Viteのビルド出力先を
backend/src/main/resources/static/に向けることで自動同梱される - Vue RouterのHistory APIを使う場合、Spring Boot側でSPAフォワーディングが必要
- 開発時はViteのHMRを活かして2プロセス並行で動かす
この構成にすることで、デプロイ時に「フロントを別途デプロイする」手間がなくなり、jarを1つコピーするだけで済みます。
このシリーズの記事マップ
→ dvdrental 管理アプリと対になるエンドユーザー向けDVDレンタルアプリを作っている話 — Vue 3 + Spring Boot の全体構成と記事マップ