Spring Boot バックエンド + Vue 3 フロントエンドの分離構成を選んだ理由と、実際の繋ぎ方
はじめに
顧客向けDVDレンタルアプリを作るとき、最初に決めた構成があります。
- バックエンド: Spring Boot(REST API)→ ポート8082
- フロントエンド: Vue 3 + Vite → ポート5173(開発時)
「なぜThymeleafで作らないのか」という疑問が来そうなので、先に答えておきます。
管理画面はThymeleafで作りました。それは管理画面として正しい選択でした。
しかし顧客向けアプリは、Thymeleafでは実現しにくいUXがありました。
この記事は「どちらが正解か」ではなく「どう判断したか」の話です。
Thymeleaf で十分なケースとそうでないケース
Thymeleaf が向いているケース
管理画面のような「フォーム送信→結果表示」の繰り返しは、Thymeleafが非常に得意です。
- ページ遷移ごとにサーバーからHTMLを返す
- Spring Securityとの統合が密で、CSRF保護が自動で入る
- サーバーサイドでのバリデーションとエラー表示が簡単
管理画面はこれで十分でした。
Vue 3 を選んだ理由
顧客向けアプリで実現したかったUXがありました。
- フィルタ・検索の即時反映 — 「検索ボタンを押さなくても結果が変わる」体験
- カートドロワーのスムーズな開閉 — ページ遷移なしのインタラクション
- モバイルとデスクトップで異なるレイアウト — ビューポートに応じた動的な表示切り替え
- 将来的なネイティブアプリとのAPI共有 — 同じREST APIをiOS/Androidアプリからも叩ける
これらはThymeleafで「不可能」ではありませんが、JavaScriptを大量に書くことになり、
どうせJSを書くならVue 3でコンポーネント化した方がメンテナブルです。
実際の構成
dvd-rental-customer-app/
backend/ ← Spring Boot(ポート8082)
src/main/java/
api/ ← REST APIコントローラ
service/ ← ビジネスロジック
frontend/ ← Vue 3 + Vite(開発時ポート5173)
src/
api/ ← バックエンドへのHTTPクライアント
components/ ← Vueコンポーネント
開発時のCORSとProxy設定
分離構成の最初の壁がCORSです。
フロント(ポート5173)からバックエンド(ポート8082)を呼ぶと、ブラウザがCORSエラーを出します。
解決策1:Viteのdevプロキシ(開発時)
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8082',
changeOrigin: true,
}
}
}
})
開発時はViteが /api へのリクエストをバックエンドに転送するので、CORSが発生しません。
解決策2:Spring BootのCORS設定(本番時)
本番環境ではフロントとバックが別ドメイン or 別ポートになる可能性があります。
その場合はSpring Boot側でCORSを許可します。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://your-frontend-domain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true);
}
}
本番ビルドの統合
本番環境では npm run build でVueをビルドし、その成果物をSpring Bootの static リソースとして配信する構成にしました。
<!-- pom.xml(抜粋) -->
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<executions>
<execution>
<id>npm build</id>
<goals><goal>npm</goal></goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
mvn package 一発でフロントのビルドも含めた jar が生成されます。
分離構成で実際に困ったこと
① 開発時に2プロセス起動が必要
Viteを起動してから、Spring Bootも起動する。
慣れれば10秒で済みますが、最初はセットを忘れて「なんで動かないんだ」となります。
② フロントのビルドを忘れてデプロイする
mvn package を実行してもフロントが古いビルドのまま、ということが起きます。
CI/CDで npm run build を必ず先に実行する順序を明示することが重要です。
③ 型の不一致
バックエンドのレスポンス型とフロントのTypeScript型が乖離しやすいです。
OpenAPIでスキーマ定義を共有するか、定期的に合わせる習慣が必要です。
まとめ
| 観点 | Thymeleaf | Vue 3 + REST API |
|---|---|---|
| 初期開発速度 | 速い | やや遅い(環境構築あり) |
| インタラクティブなUI | 難しい | 得意 |
| API共有(モバイル等) | 不可 | 可能 |
| Spring Securityとの統合 | 簡単 | 自前実装が必要 |
| デプロイの複雑さ | シンプル | やや複雑 |
管理画面はThymeleaf、顧客向けはVue 3。
この使い分けが今回のプロジェクトでは最適解でした。
「どちらが優れているか」ではなく「何を作りたいかで選ぶ」ことが大事です。
このシリーズの記事マップ
→ dvdrental 管理アプリと対になるエンドユーザー向けDVDレンタルアプリを作っている話 — Vue 3 + Spring Boot の全体構成と記事マップ