この記事はデブリです
デブリ
https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%83%87%E3%83%96%E3%83%AA
「なぜGatsby.jsを推すか」の通り、Apache単体で配信できるGatsby.jsが良いという趣旨で記事化したところ
わずか3時間後に「Next.jsもNode.js無しでOK」という情報を頂戴し、めでたくデブリ記事と化しました。
独力で見つけられなかった結構重要な情報が手を動かさずに3時間で得られることもあるので
アウトプットは大事!ということで。
それぞれのフレームワークでHTML生成した場合の流れが知りたい方は
「Next.js の環境を作成する」から読み進めて、最後にnext.js側で以下の手順を追加してください。
nano docker-compose.yml
version: '3'
services:
next:
build:
context: .
# dockerfile: dockerfile.genepro
dockerfile: dockerfile
volumes:
- ./nextjs/workspace/pages:/usr/node/workspace/pages
ports:
- 33000:3000
container_name: nextjs-trial
tty: true
# command: >
# npm run dev
nano package.json
{
"name": "workspace",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export": "next build && next export"
},
"author": "",
"license": "ISC",
"dependencies": {
"next": "^10.0.6",
"react": "^17.0.1",
"react-dom": "^17.0.1",
}
}
docker-compose up --build
docker-compose exec next bash
npm run export
ls out
http経由での確認は省略していますが、outディレクトリの中に分かりやすくhtmlファイルが出力されているので様子がわかると思います。
ただどういうわけか pages/articles/index.js は articles.htmlになり、
もう一階層掘って pages/articles/books/index.js を作ると articles/books.htmlに出力されます。
開発サーバとURLが違ってしまうのでこの辺りは変更可能なのか今後調べてみたいところです。
はじめに
Reactはとても気に入っているのですが、Node.jsはどうもツールの入れ替わりが激しくて
環境のメンテナンスとかを考えると使いたくないなあと思っていたNode.js食わず嫌い勢です。
Node.jsは使いたくないので、ReactとReact-domをJSファイルでダウンロードして、
バックエンドは別の言語でAPIを組むのがベストプラクティスだと信じて生きてきました。
バックエンドに関しては今でも「Node.jsに固定される状況は避けたい」と思っていますが、
Reactフレームワークのトレンドを調べたら、バックエンドとして使わなくてもNode.jsを入れて
Node.js依存のフレームワークを使う機運は高まっているなあ思うようにはなりました。
で、結論は「Gatsby.jsがなかなかいいんじゃない?」という話なんですが、
実際にNext.jsとGatsby.jsでホスティングまで手を進めてみてその結論に至るまでの記録を書こうと思います。
今回は Windows 10 + WSL2 + Dockerが作業環境です。
WSLとDockerが使える状態でない方は"WSL2 Docker VSCode"辺りで検索するとたくさん出ます。
(とても良い記事が2つほどあったのですが、忘れてしまいました。)
Why did I try Next.js & Gatsby.js ?
まず個人的な好みですが、バックエンドはAPI化してJavascript環境と切り離したいのです。
JSONでやり取りするAPIの形にしておいたほうがバックエンドの使用言語等による制約がなくなります。
バックエンドを別の環境でAPIサーバーとして立てることを考えると、
フロントエンド側は静的ファイルの生成とホスティングができればよいのでSSGが最適解になってきます。
加えて現在トレンド1位と言われているNext.jsがSSGの機能を強化していることもあり、
SSGを使った設計が廃れることはしばらくないのではないかと思います。
https://tsh.io/state-of-frontend/#jamstack
によると、SSGを使ったフレームワークのツートップがNext.jsとGatsby.jsとなっています。
調査方法はよくわかりませんが、2つともダントツですね。
なぜGatsby.jsを推すか
投稿時点でのNPMでのダウンロード数などを見る限り、Next.jsの方が優勢に見えます。
Next.jsならばSSRにも対応できるので、Gatsby.jsではできないことがあるという考え方はありそうです。
Gatsby.jsは拡張機能をサードパーティープラグインに依存するためリスクがあるという記述も見かけます。
確かにフロントエンドをゴリゴリ作りこんでいくとそういう苦悩にはぶつかるかもしれませんが、
それよりもまず触って違いを感じたのは
- Next.jsは静的ファイルを生成しても、結局Node.jsでサーバーを立てる必要がある
- Gatsby.jsは生成されたファイルを配置するだけでApacheなどのWebサーバーで配信できる
という部分です。
これは利用可能なリリース環境や、その環境で与えられる権限にとらわれないという点で有利です。
Next.js の環境を作成する
まずはWSL2のUbuntu上のホームにディレクトリを切っていきます。
cd ~
mkdir nextjs-trial
cd nextjs-trial
mkdir -p nextjs/workspace/pages
pagesという名前はnext.jsのソースを格納するディレクトリに合わせてあります。実際に開発する際は
このディレクトリの中のソースを変更していきます。次にdocker-compose.ymlを作成します。
node.jsの環境を作成する場合、何度かやってみて考えたのですが最終版と途中までのビルドスクリプトは
分けて作ったほうが楽だなあと思います。
なぜかというとnode.js環境を新しく作る場合は npm init -> npm install が必要で、
一度完成させた環境を再現する場合は package-lock.json から npm ci でビルドするためです。
この2つを分けて残しておくために
途中までの部分は
dockerfile: dockerfile.genepro
tty: true
最終版は
dockerfile: dockerfile
commands: >
npm run dev
としています。
ちなみにgeneproというのはオーケストラとかの楽団が本番前に行う最終リハーサルのことです。
一般的にどんな名前が使われるのかは知りません。なんかいい名前があったら教えてください。
nano docker-compose.yml
version: '3'
services:
next:
build:
context: .
dockerfile: dockerfile.genepro
# dockerfile: dockerfile
volumes:
- ./nextjs/workspace/pages:/usr/node/workspace/pages
ports:
- 33000:3000
container_name: nextjs-trial
tty: true
# command: >
# npm run dev
genepro版のdockerfileを作成します。
nano dockerfile.genepro
FROM node:lts
WORKDIR /usr/node/workspace
RUN npm init -y
RUN npm install react react-dom next
本当はnode:ltsではなくて、ちゃんとバージョン指定をしたほうがいいかもしれませんね。
next.jsのダミーソースを作っておきます。中身はnext.jsのチュートリアルページと同じです。
https://nextjs.org/docs/getting-started
"npm init"のコマンドに"-y"オプションをつけ忘れると入力待ちの状態になって失敗します。
npmに慣れていない人(わたしです)は特に要注意。
nano nextjs/workspace/pages/index.js
function HomePage() {
return <div>Welcome to Next.js!</div>
}
export default HomePage
ついでにもう1つ、階層を作るとどうなるかの確認用に
nano nextjs/workspace/pages/articles/index.js
function ArticlePage() {
return <div>Here's Article page!</div>
}
export default ArticlePage
コンテナを立ち上げます。
docker-compose up
別のシェルでコンテナへ接続してbashを操作します。
docker-compose exec next bash
中にnode_module, package.json, package-lock.json (と、pages)があることを確認します。
このpackages.jsonとpackage-lock.jsonはビルドに使うファイルになるので、ローカルにコピーします。
コピーの仕方は何でもよいですが、ローカルと接続されているディレクトリがあるので
cp -a package*.json pages/
cp -a nextjs/workspace/pages/package*.json ./
とすると早いと思います。
ここでpackage.jsonに開発サーバーの起動コマンドを登録しておきましょう。
nano package.json
{
"name": "workspace",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"author": "",
"license": "ISC",
"dependencies": {
"next": "^10.0.6",
"react": "^17.0.1",
"react-dom": "^17.0.1",
}
}
さて、このjsonを使う最終版のビルドスクリプトを作ります。
nano docker-compose.yml
version: '3'
services:
next:
build:
context: .
# dockerfile: dockerfile.genepro
dockerfile: dockerfile
volumes:
- ./nextjs/workspace/pages:/usr/node/workspace/pages
ports:
- 33000:3000
container_name: nextjs-trial
# tty: true
command: >
npm run dev
nano dockerfile
FROM node:lts
WORKDIR /usr/node/workspace
COPY package.json /usr/node/workspace/
COPY package-lock.json /usr/node/workspace/
RUN npm ci
最初にdocker-compose.ymlにcommandを書かず、dockerfile側に"RUN npm run dev"
を書いていたんですが、これだとdocker-composeで管理された構成のコンテナは落ちてしまって
無名のコンテナの中で"npm run dev"が走っている状態になってつながらず、長時間スタックしてました。
docker-compose up --build
再ビルドをかけつつコンテナを起動します。これでコマンドが使える状態のpackage.jsonが配置され、
package-lock.jsonから環境が再現される完成状態のコンテナになります。
"npm run dev"で"next dev"が実行されているので、コンテナの3000番ポートが開発サーバにつながります。
コンテナの3000番はdocker-compose.ymlでlocalhostの33000番と接続してあるので
ブラウザから localhost:33000 にアクセスして
"Welcome to Next.js!"が表示されればOKです。
articles/index.jsは localhost:33000/articles のURLからルーティングされるみたいですね。
Next.js のホスティングについて調べる
Next.jsの場合、先ほどdocker-composeで最後に"npm run dev"を実行するようにしていました。
これを一旦とりやめて、"tty: true"にします。
version: '3'
services:
next:
build:
context: .
# dockerfile: dockerfile.genepro
dockerfile: dockerfile
volumes:
- ./nextjs/workspace/pages:/usr/node/workspace/pages
ports:
- 33000:3000
container_name: nextjs-trial
tty: true
# command: >
# npm run dev
コンテナを立ち上げて接続します。
docker-compose up
docker-compose exec next bash
next.jsのSSG機能を使ってソースのビルドを実行し、完成品を見てみます。
npm run build
cd .next/server/pages
ls
ビルド済みファイルが隠しディレクトリになってます。
index.html, article.html が同じ層に入っているし、ブラウザからURLを叩いた感じとは明らかに違います。
Web検索で調べてもnext.jsのビルド済みコンテンツをnode.jsでサーバを立てずにホスティングする例は見当たりませんし、このディレクトリ構造を見る限りnode.jsのサーバーなしで動かすのは難しそうです。
Gatsby.js の環境を作成する
次はGatsby.jsを構築していきます。ディレクトリを切っていきます。
cd ~
mkdir gatsby-trial
cd gatsby-trial
mkdir gatsby/workspace/src
next.jsはソースディレクトリのルートがpagesでしたが、Gatsby.jsではsrcになっています。
srcの下にNext.js同様pagesが入りますが、pages以外にcomponentsディレクトリを切って
テンプレートを格納しておけるディレクトリ構造になっているみたいです。
docker-compose.ymlを作成します。next.jsの時と基本的に同じ作り方をしています。
nano docker-compose.yml
version: '3'
services:
gatsby:
build:
context: .
dockerfile: dockerfile.genepro
# dockerfile: dockerfile
volumes:
- ./gatsby/workspace/src:/usr/node/workspace/src
ports:
- "38000:8000"
container_name: gatsby-trial
tty: true
# command: >
# npm run develop
genepro版のdockerfileを作成します。
nano dockerfile.genepro
FROM node:lts
WORKDIR /usr/node/workspace
RUN npm init -y
RUN npm install gatsby react react-dom
gatsby.jsのダミーソースを作っておきます。中身はまたも公式からパクります。
https://www.gatsbyjs.com/docs/using-gatsby-professionally/setting-up-gatsby-without-gatsby-new/
nano nextjs/workspace/pages/index.js
import React from "react"
export default function Home() {
return <h1>Hello Gatsby!</h1>
}
今回も階層を作るとどうなるかの確認用に
nano nextjs/workspace/pages/articles/index.js
import React from "react"
export default function ArticlePage() {
return <h1>Here's Article page!</h1>
}
コンテナを立ち上げます。
docker-compose up
別のシェルでコンテナへ接続してbashを操作します。
docker-compose exec next bash
中にnode_module, package.json, package-lock.json (と、src)があることを確認します。
packages.jsonとpackage-lock.jsonをローカルにコピーします。
cp -a package*.json src/
cp -a gatsby/workspace/src/package*.json ./
package.jsonにコマンドを登録します。
nano package.json
{
"name": "workspace",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"develop": "gatsby develop -H 0.0.0.0",
"build": "gatsby build"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"gatsby": "^2.32.3",
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
}
Next.jsとちょっと違うのは、開発サーバーのコマンドにオプションが必要な点です。
Next.jsは開発サーバーに外からアクセスすることが許可されていますが、
Gatsby.jsはデフォルトで許可されていない仕様です。
このため、"-H 0.0.0.0"をオプションに与えてコンテナの外から接続することを許可する必要があります。
最終版のビルドスクリプトを作ります。
nano docker-compose.yml
version: '3'
services:
gatsby:
build:
context: .
# dockerfile: dockerfile.genepro
dockerfile: dockerfile
volumes:
- ./gatsby/workspace/src:/usr/node/workspace/src
ports:
- "38000:8000"
container_name: gatsby-trial
# tty: true
command: >
npm run develop
nano dockerfile
FROM node:lts
WORKDIR /usr/node/workspace
COPY package.json /usr/node/workspace/
COPY package-lock.json /usr/node/workspace/
RUN npm ci
docker-compose up --build
ブラウザから localhost:38000 にアクセスして
"Hello Gatsby!"が表示されればOKです。
articles/index.jsは localhost:38000/articles/ で確認できます。
Next.jsと同じ見え方ですね。
Gatsby.js のホスティングについて調べる
ここまでだとNext.jsもGatsby.jsも同じように見えますが、コンテナに入って中身を調べてみます。
version: '3'
services:
next:
build:
context: .
# dockerfile: dockerfile.genepro
dockerfile: dockerfile
volumes:
- ./gatsby/workspace/src:/usr/node/workspace/src
ports:
- 38000:3000
container_name: nextjs-trial
tty: true
# command: >
# npm run dev
コンテナを立ち上げて接続します。
docker-compose up
docker-compose exec next bash
ビルドして生成物を確認します。
npm run build
cd public
ls
publicの中にindex.htmlとarticles/index.htmlがあり、URLと対応していることがわかります。
node.jsのないapache環境で配信ができるか確認します。
version: '3'
services:
gatsby:
build:
context: .
dockerfile: dockerfile
# dockerfile: dockerfile.genepro
volumes:
- ./gatsby/workspace/src:/usr/node/workspace/src
- public:/usr/node/workspace/public
ports:
- "38000:8000"
container_name: gatsby-trial
tty: true
# command: >
# npm run develop
hosting:
image: httpd:latest
volumes:
- public:/usr/local/apache2/htdocs
ports:
- "81:80"
container_name: gatsby-trial-hosting
tty: true
volumes:
public:
開発環境(gatsby)側の/usr/node/workspace/publicを
ホスティング側の/usr/local/apache2/htdocs
とつなぎ、ビルドファイルがホスティング用コンテナのApacheドキュメントルートに入るようにします。
ホスティングコンテナの80番ポートはlocalhostの81番ポートに接続します。
docker-compose up
docker-compose exec next bash
npm run build
ブラウザからhttp://localhost:81とhttp://localhost:81/articles/にアクセスすると
"Hello Gatsby!"と"Here's Article page!"が表示されています。
Gatsby.jsはApacheのみの環境でも表示できることが確認できました。
終わりに
実際に技術選定をする場合ですが、先進的な開発環境が使える現場では
ホスティングサービスを使ってサーバーレスの構成を使う事が多いかもしれません。
なので通常あまりこういう比較の仕方をしないと思いますが、
いざとなったらLinuxサーバ立ててサービスが維持できるというのは泥臭くて好きですね。
今回は開発効率とか表示速度とか、本当はもっと大事な話をガン無視して触っただけなので
この後ちゃんと触ってみようと思います。