Jamstack(Javascript+API+markup)なウェブサイトが流行の兆しを見せていますが、これをウェブ制作の現場にフィットするように、既存のリソースを活かしてアレンジしてみたので簡単にまとめます。Next.jsのソースコードなどはまた別の記事でとりあげます。
コスパの良いJamstackを目指す
日本国内でJamstackといえば、一般的にはmicroCMSとNext.js(or Nuxt.js)をVercelにデプロイするのがセオリーとされています。
今回は、microCMSの代わりにWordPress(レンタルサーバで運用)をヘッドレスCMSとして用いて、これを別のVPSでNext.jsを使ってSSR(サーバサイドレンダリング)/SSG(サーバサイド静的生成)で配信してみます。
microCMSは商用利用(Team契約)だと4,900円/月かかるので、これがWordPressに置き換えられれば超いいじゃんみたいな気持ちをモチベーションにしているわけですが、Vercelの利用料2,800円/月まで含めて考えると、コストダウンのメリットはかなり大きいのではないかと思います。
あと、なんだかんだWordPressのブロックエディタであるGutenbergが使えるのは利点です。Markdownが気持ちよく使えて優秀なエディタだと思います。Notionからのコピペも気持ちいいです。異論は認めます。
本題に入る前に結論を言うと、特に問題もおこらず、きちんとパフォーマンスの出るブログが完成しました。(このブログです)
よかったね。
今回の本番環境
- さくらのレンタルサーバ(スタンダード)
- さくらのVPS(1G/2Core)
本番環境のサーバ構成ですが、いたってシンプルです。レンタルサーバにはWordPressを、VPSにはNext.jsを設置します。レンタルサーバの方にはサブドメインを2つ、VPSには公開用となるドメインを1つ指定しています。
いっそVPSの方にWordPressを置いてもいいのですが、コスト重視の安めのプランなので、負荷を避けるため、もともとあったレンタルサーバにそのまま置いておきます。VPSのリソースに余裕がありそうなら、そのうちVPSにWordPressコンテナを作ってお引っ越しします。
使った技術
- WordPress
- GraphQL
- Docker
- Next.js(Typescript)
- Tailwind CSS
Docker
- VPS側の環境はDockerで構築します(docker-composeを使用)
- Docker内はプロキシサーバーコンテナとNext.jsのコンテナの2段構え
- プロキシサーバーはNginXでコンテナをlistenします。証明書はLet’sEncrypt(自動更新)
課題
このプロジェクトでは、着手した時から悩んでいた「課題」がありました。
画像配信です。
WordPressAPIから取得した画像のパスをそのまま使ってしまうとWordPressのURLが見えてしまいJamstackの利点のひとつである「セキュリティ」に水を差すことになってしまいます。
WordPressの居場所はしっかり隠蔽したいので、工夫が必要です。
対策
APIのURL隠蔽は、いくつかの方法が考えられますが、今回はWordPressのuploadsディレクトリをルートとした露出可能なサブドメインを画像配信用に用意することで、エコノミー計画をブレさせることなく対応できました。WordPressから記事の本文やサムネイル画像を吐き出す際にはフックでURLの置き換えが行われます。ここだけ隠しておけばあとはNext.jsが内部的にWordPressと通信してくれるので、URLが露出してしまうことはありません。
対策前 https://api.sample.com/wp/wp-content/uploads/2022/10/fuga.jpg
↓
対策後 https://files.sample.com/2022/10/fuga.jpg
爆速化手順
爆速ブログリリースまでの大まかな手順としては次のようになります
- WordPress側をAPI化する
- Next.jsを立ち上げる
- SSR/SSGで記事を取得・表示する
- 足りないものを作る
- デプロイして速さに感動する
以上です。
簡単そうですが、ちんたらやっていたらので100時間くらいかかりました。
1. WordPress側をAPI化する
WordPress側は、特に難しい作業はありません。
GraphQL対応のためのプラグインをいろいろ入れます。
入れたプラグインは
- WP GraphQL(GraphQLでやり取りするためのプラグイン)
- WPGraphQL for Advanced Custom Fields(ACFのGraphQL対応プラグイン)
- WPGraphQL Offset Pagination(ページネーション実装のためのlimitなどを使えるようにするためのプラグイン)
- YARPP WPGraphQL(YARPPのGraphQL対応プラグイン)
です。公式ディレクトリには存在しないプラグインも多いので、Githubから落としてきます。
WordPress側のテーマは、一部を除き不要になります。
- functions.php
- style.css
- index.php
だけ残して他は消します。APIに表示画面はいらないので。
WordPressの記事プレビュー機能を使いたい場合はsingle.phpとかheader.phpとかfooter.phpとかが必要です。
見せても良いURL、見られたくないURL
ファイル配信は見えても良いサブドメインで行うとして、見られたくない方のWordPressAPIのURLは少しだけ隠蔽工作します。と言ってもWordPressのインストールをルートからサブドメイン+サブディレクトリに変更するだけです。
とはいえ、サブドメインは証明書を取得した時点で証明書検索から見えてしまうので、セキュリティ面において直接的な効力はなく、気休め程度だと思ったほうが良いです。サブディレクトリは見えませんが、たとえ特定されても突破されない程度にはWordPressのセキュリティ対策が必要です。
まとめ
- 画像配信はuploadsディレクトリをルートにしたサブドメインを与える「https://files.sample.com/」(見せても良い)
- WordPressAPIもサブドメインを与える「https://api.sample.com/」(あまり見られたくない)
- 念のため、更にサブディレクトリに入れる→「https://api.sample.com/v1/hogehoge/」(APIなのでv1とかv2とか付けておく)
2. Next.jsを立ち上げる
Vercel公式のWordPress+Next.jsのリポジトリがあるので、ありがたくcloneしてDockerを使ってにサクッと立ち上げていきましょう。
Next.jsとWordPressのフェッチ用APIと、トップページと、それから投稿ページがあっという間に用意できました。画像もNext.jsでWebp形式で配信されます。
百聞は一見にしかずで、とりあえず立ち上げて、つつき回してみるのが良きです。
docker-compose.ymlに書いたコンテナのcommandですが、開発時はNext.jsをdevモードで立ち上げるため全ページがSSRになりアクセスするたびにページのレンダリングが行われます。(表示がかなりもたつきます)
開発時
docker-compose.yml
command: sh -c "npm run dev"
本番環境
docker-compose.yml
command: sh -c "npm run start"
でNext.jsを立ち上げるのでSSGになります。
3. SSR/SSGで記事を取得・表示する
実際にAPIと通信して記事を取得し、ページを作り込んでいきます。
インデックスページはSSR、投稿ページ・固定ページはSSGです。
4. 足りないものを作る
行けそうだと思うと同時に、あれもない、これもない、と気になり始めました。
作らなきゃだめかー
- タグ機能
- カテゴリ機能
- ページネーション
- 固定ページ
- 関連ページ
このあたりは詳しく別の記事で書きたいと思います。
いわゆるパーマリンク構造とか
サブディレクトリの第一階層にパラメータを求めると、処理が面倒になりそうだったので、下記のようにしました。
- ページネーションは/pages/[:number]
- 投稿は/post/[:category]/[:slug]
- 固定ページは/article/[:slug]
- タグ別一覧は/tags/[:slug]
ダイナミックルーティングの絡みでパーマリンク構造が従来と少し変わったので正規表現でリダイレクトするようにnext-config.jsに書きました。
5. デプロイして速さに感動する
さて、アプリ側の準備ができたので、本番環境のVPSにデプロイします。
Bitbucketのリポジトリからcloneしてきて、環境変数を用意したら、コンテナを立ち上げてゲストOSにログインします。Next.jsアプリのディレクトリに移動して
npm install
npm run build
ビルドが終わったらログアウト。コンテナ立ち上げで完了です。
プロキシサーバー側の準備ができていればこれでブログが表示されます。
終わり
やっと完成です。
肝心のパフォーマンスは、というと、Lighthouseのスコアが劇的に改善(パフォーマンス30〜45点が70〜98点に上昇)しました。
SSRのページ(インデックス)はそれなりに表示に時間がかかっていますが、SSGの速さは素晴らしいですね。
ブログのパフォーマンスとしては上出来だと思います。
せっかくなので阿部寛のホームページと比較してみましたが一歩及ばずでした。無念。
おわり🏆