HapInS Developers Blog

HapInSが提供するエンジニアリングの情報サイト

Spring Boot + React + Azureで日誌アプリを作成してみた

はじめに

SES案件では「Spring Boot経験」「React経験」「Azure経験」が求められる案件をよく見かける。
私自身、Javaの経験は長いものの、実務ではSpring Bootに触れる機会がなく、応募できる案件が限られていることを感じていた。
そこで今回は、Spring Boot・React・Azureの学習を目的として、アプリケーションを1から作成することにした。 単に環境構築を行うだけでなく、データベース設計、REST APIの作成、Azureへのデプロイまで一通り経験することを目標とした。

使用技術

フロントエンド

  • React 19.2.6
  • TypeScript
  • Vite
  • Bootstrap 5.3.8

バックエンド

  • Spring Boot 3.5
  • Java 21

データベース

  • PostgreSQL 18.4

マイグレーション

  • Flyway

サーバー

  • Azure App Service(バックエンド)
  • Azure Static Web Apps(フロントエンド)
  • Azure Database for PostgreSQL(データベース)

CI/CD

  • GitHub Actions

IDE (統合開発環境)

  • VSCode(フロントエンド)
  • Spring Tools Suite 5(バックエンド)

使用技術備考

フロントエンドおよびデータベースは、学習を目的としていたため基本的に最新バージョンを採用した。
画面デザインについては、CSSの作成に時間をかけすぎないよう Bootstrap を導入した。最低限の実装で見た目を整えられるため、学習用アプリとの相性が良い。
テーブル定義の管理には Flyway を使用した。SQLファイルを管理することで、アプリケーション起動時に自動でテーブルを作成・更新できる。
ソースコードは GitHub で管理した。当初はバージョン管理のみを目的としていたが、後に Azure と連携することで GitHub Actions を利用した自動デプロイを構築できた。
結果として、コードをプッシュするだけでリリースできる環境を整えることができた。

バックエンドについて

今回は Spring Boot 3.5 と Java 21 を使用した。

Spring Boot の最新メジャーバージョンは4系だが、リリースから間もなく利用事例や関連情報が少なかったため、現在も広く利用されている3系を選択した。
また、当初は Spring Boot 4 の利用も検討したが、Flyway との組み合わせに関する情報が少なく、トラブル発生時の調査コストも考慮して Spring Boot 3.5 を採用した。
Spring Boot 3.5 は Java 17~25 に対応している。この中で長期サポート(LTS)版は Java 17、21、25 の3つである。
Java 25 もLTS版ではあるが、リリースから日が浅く、実務や学習記事での採用例が多い Java 21 を選択した。
学習用アプリということもあり、できるだけ現在一般的に利用されている技術の組み合わせで開発を進めることにした。

構成図

React を Azure Static Web Apps、Spring Boot を Azure App Service に配置し、データは Azure Database for PostgreSQL に保存している。
GitHub Actions を利用して GitHub への Push 時に自動デプロイを行う構成とした。

作成機能

今回は、学習内容を管理するという目的も含め、日誌アプリを作成した。 まず日付をベースに日誌情報の大枠を作成し、そこに詳細内容を追加していくデザインとした。

今回実装した機能

  • 日誌一覧表示
  • 日誌登録、編集、削除
  • 日誌詳細一覧表示
  • 日誌詳細登録、編集、削除
  • 日誌詳細表示モーダル

TOP画面

当月のカレンダーと日付に対して登録してある大枠データを表示

日誌情報登録画面

日誌詳細情報一覧画面

選択した日の具体的な内容を登録、一覧表示

日誌詳細情報登録画面

日誌詳細情報詳細モーダル画面

一覧では内容が見づらいので、詳細をモーダルで表示できるようにした

苦労した点

Spring BootのAPI設計

APIを作る際、画面ごとにAPIを作るか、テーブル単位でAPIを作るかで少し悩んだ。
最初は「この画面で必要なデータを返すAPI」「この画面で登録するAPI」というように、画面単位で実装していた。しかし実装を進めるうちに登録・更新・削除処理はテーブル単位で実装した方が分かりやすいと感じた。
画面単位のAPIはフロントエンド側の実装がしやすい一方で、似たようなAPIが増えてしまう可能性がある。逆にテーブル単位のAPIは責務が分かりやすいが、画面によっては複数のAPIを組み合わせる必要がある。
今回は学習用アプリであり、全体の構造もシンプルだったため、APIの責務が分かりやすいテーブル単位で作成する方針に変更した。
どちらの設計にもメリット・デメリットがあるため、今後はRESTの考え方や実務での設計例も学びながら、より良いAPI設計ができるようになりたい。

Flywayのマイグレーション管理

Flyway導入時に、最初に V1__init.sql を作成して Spring Boot を起動したところ、想定通りテーブルが作成された。
その後、カラムを変更したくなり、実行済みの V1__init.sql を直接編集して再度 Spring Boot を起動したところ、エラーが発生した。
原因は、Flyway がマイグレーションの実行履歴を flyway_schema_history テーブルで管理しているためだった。
一度実行されたマイグレーションファイルは履歴として記録されるため、同じバージョンのSQLファイルを後から変更すると、Flyway側の履歴と現在のSQLファイルの内容が一致しなくなる。
今回は初期開発中でデータを残す必要がなかったため、以下のSQLでスキーマごと削除し、再作成することで解決した。

DROP SCHEMA public CASCADE;
CREATE SCHEMA public;

これにより、flyway_schema_history を含む既存テーブルが削除され、Spring Boot 起動時に Flyway によってテーブルが再作成されるようになった。
本来は、実行済みのマイグレーションファイルを直接編集するのではなく、V2__add_xxx_column.sql のように新しいバージョンのマイグレーションファイルを追加して変更する必要がある。

Azureデプロイ

AWSについては多少触れたことがあったが、Azureは今回が初めてだった。
サービス数も多く、どのサービスがどの役割を持っているのか分からなかったため、調べながら構築を進めた。

フロントエンドからREST APIが呼び出せない

Azureへのデプロイ自体は成功したものの、フロントエンドからバックエンドのAPIを呼び出せない問題が発生した。

1. APIのURIが取得できていなかった

開発中は localhost を使用していたが、Azureデプロイ後はバックエンドのURLへ変更する必要がある。
そのため、React側で .env.production を作成し、Azure上のAPIのURLを設定した。
しかし、画面を表示するとAPIのURLが undefined になっていた。
原因を調べたところ、Viteでは環境変数名を VITE_ から始める必要があることが分かった。

修正前

API_BASE_URL=https://xxxxx.azurewebsites.net
export const API_BASE_URL = import.meta.env.API_BASE_URL;

修正後

VITE_API_BASE_URL=https://xxxxx.azurewebsites.net
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

環境変数名を修正することで、正しくAPIのURLを取得できるようになった。

2. CORSエラー

APIのURLは取得できるようになったが、今度はブラウザのコンソールに以下のエラーが表示された。

Access to XMLHttpRequest has been blocked by CORS policy

これは異なるドメイン間で通信を行う際に発生するクロスオリジンエラーである。
今回は、Reactを Azure Static Web Apps、Spring Bootを Azure App Service に配置していたため、Spring Boot側でアクセスを許可する設定が必要だった。
Springでは WebMvcConfigurer を利用してCORS設定を追加できる。

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("(許可するURI)")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*");
    }
}

上記の設定を追加することで、ReactからSpring BootのAPIを呼び出せるようになった。

今回のAzureデプロイでは、単にアプリを公開するだけでなく、環境変数の扱いやCORS設定など、ローカル開発では意識しなかった部分についても学ぶことができた。

まとめ

今回触れた技術は初めてのものが多かったが、 アプリ作成からAzureへの公開まで一通り経験することができた。
特にクラウド環境の構築やデプロイは、 実務では既に用意された環境を利用することが多かったため、 今回自分で構築したことでサービスの役割や全体の流れを理解することができた。
Spring Boot、Reactの環境構築、Azureサービス立ち上げ方法については、後日時間があればまとめようと思う。