
先日からの作業により,必要最低限のバックエンドの機能が出来た。とはいえ年表の作成UIはデバッグ用途でしかなく,到底実用には堪えない。表示だけがいちおうまともにできるようになった,という状態だ。
そういうわけで投稿用UIを作ることになるのだが,HTMLフォームでいったん投稿用UIを作ってからTypeScriptとReactなどを駆使して置換するというのもあまりに無駄であり,そもそも年表投稿UIを静的HTMLのみでやるのはかなり大変だ。そこで一旦フロントエンド環境を構築してHello Worldを表示させるまで行った。
npm
yarnって最近あまり聞かないんですけどnpmでいいんですよね?
% npm init
TypeScript + Webpack + React
基本的にここ見ながらひととおり構築します。次回あたりでtslint入れたり,tsconfig.jsonをオシャレにしようと思います。動けばヨシ!
% npm i -D webpack webpack-cli typescript ts-loader % npm i -S react react-dom @types/react @types/react-dom
僕はts-loaderっていう文字列を見るたびに頭痛がします。babel-loaderとかなんかいろいろが混在して百鬼夜行になっていたのを思い出すからです…………
そのへんはこれ読むとだいたい分かります
tsconfig.json
多分これが一番速いと思います(大嘘)
{ "compilerOptions": { "sourceMap": true, "target": "es5", "module": "es2015", "jsx": "react", "moduleResolution": "node", "lib": [ "es2020", "dom" ] } }
webpack.config.js
webpack何もわからない。
module.exports = { mode: "development", entry: "./src/entrypoint.ts", output: { path: `${__dirname}/dist`, filename: "entrypoint.js" }, module: { rules: [ { test: /\.tsx?$/, use: "ts-loader" } ] }, resolve: { extensions: [".ts", ".tsx", ".js", ".json"] } };
package.json
{ "name": "timeline", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack", "watch": "webpack -w", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "windymelt", "license": "MIT", "devDependencies": { "ts-loader": "^7.0.5", "typescript": "^3.9.5", "webpack": "^4.43.0", "webpack-cli": "^3.3.11" }, "dependencies": { "@types/react": "^16.9.35", "@types/react-dom": "^16.9.8", "react": "^16.13.1", "react-dom": "^16.13.1" }, "private": true }
というわけでこれでnpm run buildができるようになった。これを行うとfrontend/dist/entrypoint.jsが吐き出される。
entrypoint.tsには素朴なHello Worldを書いておく。
console.log("hello, typescript!")
nginx
さて,開発するにあたっては,ブラウザにはコンパイルしたての最新のjsを読み込ませたいが,ブラウザは最新のjsがどこにあるかなんて知らない。ブラウザと成果物JSとの橋渡しをnginxにやってもらう。ついでにアプリケーションサーバの前段にもなってもらう。
nginxをホストに直でインストールする時代は終わっているのでDockerを使う。
まずdocker-compose.ymlを書く。
version: '3' services: nginx: # js file distributing: local development only image: nginx:1.19.0 volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./frontend/dist/:/usr/share/nginx/js/:ro ports: - 8000:80
nginxサービスを作成し,TypeScriptをコンパイルして出来たJSファイル とnginx.confとをマウントしてコンテナから視えるようにする。
ここでnginx.confは以下の通り。デフォルトっぽいのをパチってきただけなので特に変哲なし。JSファイルの配信と,アプリケーションサーバへの転送だけが主たる記述である。
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location ~ ^/js/(.+) {
alias /usr/share/nginx/js/$1;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location / {
proxy_pass http://192.168.xxx.yyy:8080; # TODO: dockerize backend
}
}
}
proxy_passにはlocalhostと書きたかったが,アプリケーションサーバはdocker化されていないのでうまく接続できない。このためLAN上のアドレスを指定している。そのうちアプリケーションサーバもDocker化したいが,JVMアプリケーションをDocker化するの明らかにダルいんだよなー。
動作確認
アプリケーションサーバを起動する。
% sbt sbt:Timeline> jetty:stop;jetty:start
nginxも起動する。
% docker-compose up
ここでブラウザを使ってlocalhost:8000にアクセスする。

よしよし。

Hello Worldも動いているので,無事JSが配信されている。
まとめ
フロントエンドまわりの環境構築ができた。成果物をdocker+nginxでブラウザに読み込ませる,一般的(だと思う)なローカル開発環境だと思う。npmやアプリケーションサーバもDockerに載せられると色々助かる。
次回からは年表書くためのUIを書いていく。TypeScript書く環境は整っているので,あとはルータなどを書くところからやる。そろそろ複雑になってきたのでテスト環境も整えたい。