1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SpringBootプロジェクトとReactの同時実行のための設定

Last updated at Posted at 2024-10-16

SpringBoot内でReactを同時に実行するための環境を設定したやり方を解説する。

背景

RestAPIの成果をどう見せるか

プログラミングスクールでSpringBootによるWebアプリ開発を学び、それを活用したポートフォリオを作成しようと考えた。ただ、RestAPIが扱うのは情報のやり取りのみであり、それによって何が可能なのかはよく分かりづらい。面倒でもフロントエンドがあった方が成果がよくわかる。

データの入力

フロントエンドが存在しない場合にそのWebアプリで使用しているデータベースを操作するには直接MySQLから操作するか、PostmanのようなAPI開発ツールを使用するかという選択肢になりそうだが、入力ミスが多発しそうなのは明白であり、フロントエンド構築が求められる。

フロントエンドフレームワークの選択

SpringBootにフロントエンドを統合するためにはsrc/main/resources/staticフォルダにHTMLファイルを格納する必要がある。ただし、HTMLでは動的なレンダリングは難しいため、それを可能とするフレームワークを使用する必要がある、つまりコンパイルが必要。
フレームワークとしてはReactを選択した(あまり勉強しないで簡単に構築するためにはVue.jsがよかったかもしれないと少し反省)。

設定方法

Reactの導入

SpringBootプロジェクト内にReactプロジェクトを配置する。インストール等はここでは省略。

project/
├── .git/
├── .gradle/
├── .idea/
├── build/
├── gradle/
├── output/
├── react-app/ ※Reactプロジェクト
└── src/

これ以降は、SpringBootの実行に合わせてフロントエンドも準備されるようにbuild.gradleを設定していく。

build.gradleのタスクの理解

特に設定しないでApplicationを実行したときのタスクは以下の順で実施される。

  1. compileJava
    compileJavaタスクは、プロジェクト内のJavaソースファイルをコンパイルし、バイトコードを生成する。このタスクは、ソースコードの変更があった場合に実行され、コンパイルされたクラスファイルは後続のビルドプロセスで使用される。

  2. processResources
    processResourcesタスクは、プロジェクト内のリソースファイル(例えば、プロパティファイルやXMLファイル)を処理し、必要に応じてコピーや変換を行う。このタスクは、リソースが適切に配置され、最終的なアプリケーションパッケージに含まれることを保証する。

  3. classes
    classesタスクは、compileJavaとprocessResourcesの成果物をまとめて、クラスパスに必要なファイルを生成する。このタスクは、アプリケーションを実行するために必要なクラスファイルやリソースが整っていることを確認し、ビルドプロセスの一部として自動的に実行される。

以上より、processResources以前にsrc/main/resources/staticにReactのコンパイル成果物が格納されていないといけないことになる。

Reactアプリをビルドするタスクの追加

build.gradleに以下のタスクを追加する。これによりreact-app/build内にReactコンパイル成果物を格納できるようになる。実際にreact-appでビルドする際のコマンドは'npm run build'だが、ここでは"npm.cmd"が必要であることに注意が必要。

build.gradle
// Reactアプリをビルドするタスク
task buildReact(type: Exec) {
	workingDir "$projectDir/react-app"
	commandLine 'npm.cmd', 'run', 'build'
}
project/
├── .git/
├── .gradle/
├── .idea/
├── build/
├── gradle/
├── output/
├── react-app/
│   └── build/  ※reactコンパイル成果物の格納フォルダ
└── src/
    └── main/
        └── resources/
            └── static/

Reactコンパイル成果物をコピーするタスクの追加

先ほどのコンパイル成果物をsrc/main/resources/staticにコピーするタスクをbuild.gradleに追加。このときdependsOnを用いて「buildReact→copyToStatic→processResources」の順にタスクが実行されるようにする。

build.gradle
// Reactアプリをビルドするタスク
task buildReact(type: Exec) {
	workingDir "$projectDir/react-app"
	commandLine 'npm.cmd', 'run', 'build'
}

// Reactのビルド結果をSpring Bootのstaticフォルダにコピーするタスク
task copyToStatic(type: Copy) {
	dependsOn buildReact
	from "$projectDir/react-app/build"
	into "$projectDir/src/main/resources/static"
}

// パッケージング以前にReact関連タスクを実行する
processResources.dependsOn copyToStatic
project/
├── .git/
├── .gradle/
├── .idea/
├── build/
├── gradle/
├── output/
├── react-app/
│   └── build/  ※reactコンパイル成果物の格納フォルダ
└── src/
    └── main/
        └── resources/
            └── static/  ※reactコンパイル成果物のコピー先

staticを削除するタスクを追加

Reactのコンパイル成果物の詳細は以下の通り。

├── react-app/
│   └── build/  ※reactコンパイル成果物の格納フォルダ
│       ├── asset-manifest.json
│       ├── favicon.ico
│       ├── index.html
│       ├── logo192.png
│       ├── logo512.png
│       ├── manifest.json
│       ├── robots.txt
│       └── static/
│           ├── css/
│           │   ├── main.3572f698.css
│           │   └── main.3572f698.css.map
│           └── js/
│               ├── 453.2a82e7ac.chunk.js
│               ├── 453.2a82e7ac.chunk.js.map
│               ├── main.f4025a23.js
│               ├── main.f4025a23.js.LICENSE.txt
│               └── main.f4025a23.js.map

このうちcss/やjs/内のファイルはindex.htmlに紐づいて表示内容のベースとなるものである。ファイル名のハッシュ部分はキャッシュバスティングのためにファイルが変更された際に、ブラウザが古いファイルを参照しないようにする役割を果たす。
先ほど追加したcopyToStaticでは元々staticフォルダが存在する場合は上書き保存となるが、これによりcss/やjs/内のファイルは毎回新規でコピーされることになるため、放っておくとフォルダ内に大量のファイルが存在することとなる。したがって、コピー前にstaticを消去するタスクを追加する。また、依存関係の変更を忘れてはいけない。

build.gradle
// Reactアプリをビルドするタスク
task buildReact(type: Exec) {
	workingDir "$projectDir/react-app"
	commandLine 'npm.cmd', 'run', 'build'
}

// static フォルダを削除するタスク
task deleteStatic(type: Delete) {
	dependsOn buildReact // Reactのビルドが終わってからコピーを実行
	delete "$projectDir/src/main/resources/static"
}

// Reactのビルド結果をSpring Bootのstaticフォルダにコピーするタスク
task copyToStatic(type: Copy) {
	dependsOn deleteStatic // staticフォルダを削除してからコピーを実行
	from "$projectDir/react-app/build"
	into "$projectDir/src/main/resources/static"
}

// パッケージング以前にReact関連タスクを実行する
processResources.dependsOn copyToStatic

テスト時の対応

この段階での以下の各ケースでの実行タスクは以下の通り。

  • Application実行
> Task :compileJava UP-TO-DATE
> Task :buildReact
> Task :deleteStatic
> Task :copyToStatic
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
  • Test実行
> Task :compileJava UP-TO-DATE
> Task :buildReact
> Task :deleteStatic
> Task :copyToStatic
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE

Testにおいても先ほど設定したReact関連のタスクが実行されているが、テストにはフロントエンドは含まれていないため、Test実行時にはこれらを実行しないように設定が必要。そこで、build.gradleに以下を追加。

build.gradle
gradle.taskGraph.whenReady { taskGraph ->
	if (taskGraph.hasTask(':test')) {
		// テスト実行時にReact関連タスクを無効にする
		tasks.buildReact.enabled = false
		tasks.deleteStatic.enabled = false
		tasks.copyToStatic.enabled = false
	}
}

再度testを実行すると、タスクリストは下記の通り、React関連の3つのタスクがスキップされるようになった。これでひとまず完成。

> Task :compileJava UP-TO-DATE
> Task :buildReact SKIPPED
> Task :deleteStatic SKIPPED
> Task :copyToStatic SKIPPED
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE

あとがき

今後AWS上にデプロイする際のことを少し考えたが、おそらくここの内容には変更はなさそう。ただ、そもそもの環境構築でひと手間かかりそうな予感がする。
続報あればまた記事投稿します。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?