ReactとPatternflyでQuarkusアプリにEyecandyを追加する
コンテナ化された分散型のQuarkusアプリケーションで一般的なパターンとして、別のフロントエンドアプリケーションのための効率的でスケーラブルなバックエンドとして機能することがあります。Kubernetesの世界では、フロントエンドアプリケーションはReact、Angular、Vue.jsなどの純粋なウェブレイヤーPodであり、すべてのRESTコールをQuarkusのサービスレイヤーPodに委ねることができます。
このアプローチには、独立したコンテナのスケーリングと独立したライフサイクルという利点がありますが、小規模なアプリケーションには過剰なエンジニアリングとなる場合があります。
最新のWeb GUI技術に妥協することなく、自己完結型の小さなCRUDアプリケーションを作りたいと思っているのではないでしょうか? Angularでこれを実現する方法を見てきましたが、少なくともGitHubの星の数において、人気の点でAngularを上回っているReactはどうでしょうか?今回は、Reactを統合するだけでなく、 Patternflyと呼ばれるWebコンポーネントフレームワークを使用する方法を紹介します。Patternflyは、リッチで一貫性のあるインタラクティブなGUIを作成するためのパターン、コンポーネント、スタイルを集めたものです。
React/Patternflyのテーブルに表示される原子パーティクルオブジェクトを提供するRESTエンドポイントを持つQuarkus Appを作成してみましょう。最終的なアプリケーションのソースコードは こちらにあります。
ステップ1 - Quarkusプロジェクトの生成
スタータープロジェクトを生成するには、 https://code.quarkus.io にアクセスします。Maven Groupとして "io.quarkus" 、Artifactとして "quarkus-react" を入力します。エクステンションのリストで、 RESTEasy JAX-RS と RESTEasy JSON-B を選択します。その後、プロジェクトを生成してダウンロードし、ローカル・マシンで解凍して、お気に入りのIDEでプロジェクト・フォルダを開きます。
ステップ2 - Quarkus RESTエンドポイントの追加
サンプルの GreetingResource.java が存在するJavaパッケージフォルダ/src/main/java/io/quarkusに、 Particle.java という名前の新しいBeanクラスを以下の内容で作成します。
package io.quarkus;
public class Particle {
private String name;
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
ここで、 ParticleResource.java という新しいRESTリソースクラスを作成し、いくつかのテストパーティクルを返します(ここでは原子物理学的な意味合いは無視します)。
package io.quarkus;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("/particles")
public class ParticleResource {
@GET
public Set<Particle> getUsers() {
Set <Particle> particleList = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));
Particle particle = new Particle();
particle.setName("Graviton");
particleList.add(particle);
Particle particle2 = new Particle();
particle2.setName("Pentaquark");
particleList.add(particle2);
return particleList;
}
}
開発モードでQuarkusを起動して、すべてが動作するかどうかをテストします。
$ mvn quarkus:dev
新しい端末で、エンドポイントを呼び出して、パーティクルの応答を確認します。
$ curl http://localhost:8080/particles
[{"name":"Graviton"},{"name":"Pentaquark"}]
Quarkusのデータレイヤーの準備ができたので、次はReactのGUIを作りましょう。
ステップ3 - Reactプロジェクトの生成
お使いのシステムに Node.js と npm がインストールされていることを確認してください。必要に応じて、 公式ドキュメントを参照してください。
プロジェクト内に新しいフォルダ/src/main/ webapp を作成し、これをReactプロジェクトのベースフォルダとします。このフォルダで別のターミナルを開き、Reactプロジェクトイニシャライザを実行して、Patternfly Nodeモジュールを追加します。
$ npx create-react-app .
$ npm install @patternfly/patternfly --save
Reactアプリを起動すると、デフォルトではポート3000で実行されます。その後、ポート8080でQuarkusのエンドポイントにアクセスしようとすると、 CORSセキュリティポリシーのためにブラウザがこれを阻止します。本番環境では、コンパイルされた静的なReactアプリがポート8080でQuarkusから提供されるため、これは問題になりませんが、開発中はこれが問題になります。 |
幸いなことに、Reactには未知のエンドポイントへのすべてのリクエストを別のURLに転送する proxy というちょっとした機能があります。 webapp フォルダで package.json ファイルを開き、proxyの行を追加して、すべてのリクエストをQuarkusのポート8080に転送します。
{
"name": "webapp",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.3",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": "http://localhost:8080/",
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
ステップ4 - Patternflyのテーブルコンポーネントを作成して、データで埋める
Reactプロジェクトの /webapp/index.js にPatternflyのCSSをインポートします。
...
import './index.css';
import '@patternfly/patternfly/patternfly.css';
import App from './App';
...
webapp/srcに components という新しいフォルダを作り、その中に particles.js というファイルを作り、パーティクルオブジェクトのリストをループさせてPatternflyのテーブルを生成します。
import React from 'react'
const Particles = ({ particles }) => {
return (
<div>
<center><h1>Particles List</h1></center>
<table class="pf-c-table pf-m-grid-md" role="grid" aria-label="Supersonic Subatomic Particles" id="table-basic">
<caption>Supersonic Subatomic Particles</caption>
<thead>
<tr role="row">
<th role="columnheader" scope="col">Name</th>
</tr>
</thead>
{particles.map((particle) => (
<tbody role="rowgroup">
<tr role="row">
<td role="cell" data-label="Particle name">{particle.name}</td>
</tr>
</tbody>
))}
</table>
</div>
)
};
export default Particles
次に、 /webapp/src/App.js を調整して、Quarkusサービスのエンドポイントを呼び出し、レスポンスデータを使ってパーティクルコンポーネントをレンダリングします。
import React, {Component} from 'react';
import Particles from './components/particles'
class App extends Component {
state = {
particles: []
}
componentDidMount() {
fetch('/particles')
.then(res => res.json())
.then((data) => {
this.setState({ particles: data })
})
.catch(console.log)
}
render () {
return (
<Particles particles={this.state.particles} />
);
}
}
export default App;
これで、Reactの開発モードでGUIをテストする準備が整いました。
別のターミナルでQuarkusがまだ起動していることを確認します。
/webapp フォルダ内で実行します。
$ npm start
ブラウザで http://localhost:3000 を開きます。開かない場合は、そのURLに移動してください。Quarkusのパーティクルバックエンドからテーブルが動的に入力されているのがわかると思います。ブラウザの開発者ツールでは、データが実際に http://localhost:3000/particles から取得されていることがわかります。
現在、ダブルデベロッパーモードで運用しています。JavaとJavascriptのどちらかのコードに加えられた変更は、すぐに適用されます。これは開発者にとって至福の時ではないでしょうか。
しかし、コンテナを使ったデプロイについてはどうでしょうか?新しいGUIアプリが完成したら、実行可能なjarやイメージを作成するために、いくつかの調整が必要になります。
ステップ5 - プロジェクトの本番ビルドへの準備
webapp/package.json にprodのビルドフェーズを追加:
...
"eject": "react-scripts eject",
"prod": "react-scripts build --dest && rsync -a build/* ../resources/META-INF/resources"
},
"proxy": "http://localhost:8080/",
...
これにより、静的なReactアセットのプロダクションビルドが行われ、Quarkusのデフォルトの静的ファイル用ディレクトリにコピーされます。
rsyncコマンドは、Linux専用です。他のOSの場合は、同等のコマンドで代用してください。 |
最後のステップでは、優れた frontend-maven-pluginを pom.xml に追加して、通常の maven ビルドから React のビルドフェーズを起動します。npmとnodeのバージョンを、ローカルにインストールされているものに合わせて調整します。mavenのインストールフェーズでは、実際にこれらの2つのパッケージがインストールされますが、これはCI/CDビルドやこのプロジェクトの起動時に便利です。
<build>
...
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.11.2</version>
<configuration>
<workingDirectory>${project.basedir}/src/main/webapp</workingDirectory>
<installDirectory>target</installDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v14.15.4</nodeVersion>
<npmVersion>6.14.10</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run prod</arguments>
</configuration>
</execution>
</executions>
</plugin>
</build>
それでは、実行可能なjarをビルドしてみましょう。
$ mvn clean package
他の稼働中の環境をすべて停止してポートを空け、実行します。
$ java -jar target/quarkus-app/quarkus-run.jar
ブラウザで http://localhost:8080 を開くと、最適化された単一のQuarkus jarから実行されているテーブルが表示されます。
まとめ
いくつかの簡単なステップで、ReactとPatternflyの力を使って、Quarkusアプリに洗練された見た目のインタラクティブなGUIを追加することができます。
このアプリを次は ネイティブにコンパイルして、極めて軽量なWebアプリを展開するのはどうでしょうか。あるいは、他の Patternflyコンポーネントを使用して、より洗練されたウェブインターフェイスを作成することもできます。いずれにしても、Quarkusアプリを輝かせるためには、あなたの創造性以外に制限はありません。