ここまでで REST API とデータベースが動いています。最後のステップとして、ブラウザから API を呼び出す画面を作り、データの一覧表示・追加・削除ができるようにします。
fetch の回とフォームの回で学んだパターンをそのまま使います。
完成イメージ
画面には次の要素があります。
- 社員一覧テーブル(名前・部署・削除ボタン)
- 新規追加フォーム(名前・部署の入力欄と追加ボタン)
操作の流れは「フォームで入力 → API を呼び出し → テーブルを再描画」です。
HTML ファイルの配置場所
Spring Boot は src/main/resources/static/ フォルダに置いたファイルをそのまま配信します。ここに index.html を作成します。
src/main/resources/static/index.html
追加の設定は不要です。アプリ起動後に http://localhost:8080/ を開くと、この HTML が表示されます。
前回までは API を確かめるために HTML を別の場所に置き、@CrossOrigin で CORS を回避していました。今回は HTML を Spring Boot の static/ に入れることで、画面と API が同じ localhost:8080 から配信されます。オリジンが同じなので CORS の問題は起きず、@CrossOrigin も不要になります。
HTML のひな形
index.html を次の内容で作成します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>社員管理</title>
<style>
body { font-family: sans-serif; max-width: 600px; margin: 40px auto; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background: #f5f5f5; }
input { padding: 6px; margin-right: 8px; }
button { padding: 6px 12px; }
</style>
</head>
<body>
<h1>社員管理</h1>
<table>
<thead>
<tr><th>ID</th><th>名前</th><th>部署</th><th></th></tr>
</thead>
<tbody id="employee-list"></tbody>
</table>
<h2>新規追加</h2>
<form id="add-form">
<input type="text" id="name-input" placeholder="名前" required>
<input type="text" id="dept-input" placeholder="部署" required>
<button type="submit">追加</button>
</form>
<script src="app.js"></script>
</body>
</html>
ポイントは2つです。
<tbody id="employee-list">— JavaScript から行を追加する場所<form id="add-form">— 送信イベントを JavaScript で処理する
一覧を取得して表示する
src/main/resources/static/app.js を作成します。まず一覧取得の部分です。
const API_URL = "/api/employees";
async function loadEmployees() {
const response = await fetch(API_URL);
const employees = await response.json();
const tbody = document.getElementById("employee-list");
tbody.innerHTML = "";
for (const emp of employees) {
const row = document.createElement("tr");
row.innerHTML =
"<td>" + emp.id + "</td>" +
"<td>" + emp.name + "</td>" +
"<td>" + emp.department + "</td>" +
'<td><button onclick="deleteEmployee(' + emp.id + ')">削除</button></td>';
tbody.appendChild(row);
}
}
loadEmployees();
loadEmployees は fetch の回と同じパターンです。
fetch(API_URL)で GET リクエストを送る- JSON を配列として受け取る
forループで1行ずつ<tr>を作り、<tbody>に追加する
tbody.innerHTML = "" で古い行をクリアしてから再描画しています。
API の URL が /api/employees になっていることに注目してください。HTML と API が同じ localhost:8080 から配信されるため、ドメイン部分を省略できます。前の回で説明した CORS の問題も起きません。
新規追加を実装する
app.js に次のコードを追加します。
document.getElementById("add-form").addEventListener("submit", async function(event) {
event.preventDefault();
const name = document.getElementById("name-input").value;
const department = document.getElementById("dept-input").value;
await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: name, department: department })
});
document.getElementById("name-input").value = "";
document.getElementById("dept-input").value = "";
loadEmployees();
});
フォームの回で学んだパターンがそのまま使えます。
event.preventDefault()でページ遷移を止める- 入力値を取得して JSON に変換する
fetchのmethod: "POST"で送信する- 入力欄をクリアして
loadEmployees()で再描画する
削除を実装する
app.js に次のコードを追加します。
async function deleteEmployee(id) {
await fetch(API_URL + "/" + id, {
method: "DELETE"
});
loadEmployees();
}
削除ボタンのクリックで deleteEmployee(id) が呼ばれます。DELETE リクエストを送った後、loadEmployees() で一覧を更新します。
app.js の全体
ここまでのコードを1つにまとめます。
const API_URL = "/api/employees";
async function loadEmployees() {
const response = await fetch(API_URL);
const employees = await response.json();
const tbody = document.getElementById("employee-list");
tbody.innerHTML = "";
for (const emp of employees) {
const row = document.createElement("tr");
row.innerHTML =
"<td>" + emp.id + "</td>" +
"<td>" + emp.name + "</td>" +
"<td>" + emp.department + "</td>" +
'<td><button onclick="deleteEmployee(' + emp.id + ')">削除</button></td>';
tbody.appendChild(row);
}
}
document.getElementById("add-form").addEventListener("submit", async function(event) {
event.preventDefault();
const name = document.getElementById("name-input").value;
const department = document.getElementById("dept-input").value;
await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: name, department: department })
});
document.getElementById("name-input").value = "";
document.getElementById("dept-input").value = "";
loadEmployees();
});
async function deleteEmployee(id) {
await fetch(API_URL + "/" + id, {
method: "DELETE"
});
loadEmployees();
}
loadEmployees();
動作確認
アプリを起動します。
./mvnw spring-boot:run
ブラウザで http://localhost:8080/ を開くと、初期データ3件が表示されます。
確認する操作は次の3つです。
- 一覧表示 — テーブルに ID・名前・部署が表示される
- 追加 — フォームに名前と部署を入力して「追加」を押すと、テーブルに行が増える
- 削除 — 「削除」ボタンを押すと、該当行が消える
H2 コンソール(http://localhost:8080/h2-console)でも SELECT * FROM EMPLOYEE を実行すると、画面の操作がデータベースに反映されていることを確認できます。
編集(PUT)について
この記事では編集機能を省略しました。実装するには「行をクリックしたらフォームに値を入れ、PUT で送信する」という処理が必要です。考え方は POST と同じで、method: "PUT" に変えて ID をURLに含めるだけです。余力があれば挑戦してみてください。
カリキュラム全体のふりかえり
このコースでは次のステップで進めてきました。
| コース | 学んだこと |
|---|---|
| Java 基礎 | 変数・型・条件分岐・ループ・メソッド |
| Java オブジェクト指向 | クラス・フィールド・ArrayList |
| 開発環境 | ローカル環境での開発(VS Code・javac・jar) |
| フロントエンド | HTML・CSS・JavaScript・fetch |
| DB / SQL | テーブル・SQL の CRUD・JOIN |
| Spring Boot | REST API・JPA・画面連携 |
最後に作った CRUD 画面は小さなアプリですが、Web アプリケーションの基本構造がすべて含まれています。
- フロントエンド が画面を描画し、ユーザー操作を受け取る
- API がリクエストを処理し、レスポンスを返す
- データベース がデータを永続化する
この「フロント → API → DB」の3層構造は、実務の Web アプリケーションでもそのまま使われます。ここから先は、フレームワーク(React、Spring Security など)や本番用データベース(MySQL、PostgreSQL)を学ぶことで、実務レベルのアプリケーション開発に進めます。