Next.jsとesaを使った個人サイト構築
個人サイトをNext.jsとesaを使って作った。
スタイルが適当だったりmetaとか色々設定してないけど、記事が見れればいいっしょ、ぐらいのノリなので、とりあえず公開している。
モチベ
r7kamura氏のエントリを見て作ろうと思った。
概ね理由は同じだが、フィードバックと適切な距離を置いて、アウトプットへのハードルを下げたいのが理由。前から作りたいなと思っていたので、えいやで構築した。
このネタどこで書こう、Qiita? Zenn? note? はてブ? というように選択コストが上がってきたのと、プラットフォームが燃えてると自分の記事まで燃えている気持ちになるのにつらみを感じたので、雑に個人サイトでアウトプットする形に戻した。技術ネタ以外も書きたいし。住宅購入の話とかね。
スタック
Next.js
- 仕事でVue.jsを書いてるので、Reactが書きたくなった
- Incremental Static Regeneration がすごい(でも今回は使ってない)
- ナウくてかっこいい
Vercel
- シンプルで便利
- ISRも対応してるしすごい
- 個人使用なら無料で使える
esa.io
- 個人で契約して使っていた(月500円)
- Markdownで書いてプレビューできる
- 画像のアップロード先に独自のS3バケットを設定できる
- APIで記事が取得できる
仕様
次の形で記事が公開される
- esaの特定のカテゴリ以下に記事を書いてShip It!
- WebHookがesa -> Vercelに飛んでビルド開始
- APIでesaから記事を取得して静的ビルドされる
構築するときに考えたこと
考えたことをつらつら書いておく
- Incremental Static Regeneration が必要かどうか
- APIのリミットとキャッシュ
- 画像の扱い
- MarkdownからHTMLへの変換
- プレビュー
- デプロイ(WebHook)
- メタ情報
- スラッグ
Incremental Static Regeneration(ISR) が必要かどうか
数百記事までなら正直不要だと考えたので、使ってない。キャッシュの有効期限を短くしすぎると、動的サイトを同じなので、API側に負荷がかかってしまう。長くすると、静的ビルドしたほうが公開が早くなる。また、esa側のAPIにリミットがあるので、ISRを選択する場合、何かしらのキャッシュを行う層を設けなくてはいけなくて、いろいろ大変なのでやらないことに決めた。
正直カッチョイイ機能なので使いたかったが、記事が増えてきたらトライする項目としておく。
APIのリミットとキャッシュ
dev/esa/api/v1 #noexpand - docs.esa.io
現時点では、ユーザ毎に15分間に75リクエストまで受け付けます。
なので、75記事以上ある場合、記事単体で取得するコードを雑にループすると死んでしまう。これを回避するために、静的ビルド時に記事の取得方法を工夫している。
esaは記事一覧のAPIを叩くとMarkdown本文まで取得できるので、一覧へリクエストした際に、個別の記事をローカルにキャッシュしている。キャッシュは .next/cache/posts/[id].json という形で保存しておく。個別ページのビルド時にgetPost(id) のような関数を用意しているが、リクエストを発生させずに、このキャッシュを見にいっている。次の内容しか保存していない。
{ id, title, body_md, updated_at, created_at }
記事一覧APIの取得上限は1リクエスト100記事なので、7500記事まで対応できる。 個人サイトなら十分すぎると思う。
画像の扱い
esaは画像のアップロード先として、独自のS3バケットを設定できる。esaのバケットにアップした画像を直接公開個人サイトで公開するのは明らかに逸脱した使用だと感じているので、Headless CMSとしてesaを使う場合は必ず設定すること。
help/ファイルアップロード先に独自のS3 Bucketを設定する - docs.esa.io
MarkdownからHTMLの変換
正直デファクトが分からない。Next.jsのTutorialでremarkを使っていたので、そのまま使っている。コードハイライティングにhighlight.jsを使用。プラグインもある。
実はesaのAPIを叩くとHTMLも返してくれるが、Markdown前提で作っておきたかったので、使っていない。
プレビュー
リアルタイムプレビューみたいなものを考え出すと色々面倒なので、esaのプレビューで十分だと考えている。
デプロイ
esaの記事更新時のWebHook + VercelのDeploy Hooks を組み合わせて実現している。記事をShipItで作成、更新すると、VercelにWebHookが飛び、自動で静的ビルドが走る。特定のカテゴリ以下のみWebHookされるように設定しているので、他の記事を書いたときに不用意にビルドが走ることもないのが良い。esa、便利です。
メタ情報
esaをHeadless CMSにするとメタ情報をどこに記述するかが問題になる。
今回やりたいこと
- https://corocn.dev/posts/1 のようなIDでの参照をやめる
- https://corocn.dev/posts/super-ultra-greate-page のようにページURLに固有の文字列を与えたい
スラッグ(slug)とも言うみたい。これを実現するため、Speaker Deck のような形でやることにした。タイトルに埋め込んでしまう方式。
esaは スラッシュ"/" がカテゴリの区切り文字として使われているので、 "--" で識別することにした。"-- about-this-site" と記述すると、公開されるURLが https://corocn.dev/posts/about-this-site になる。
もっと情報をもたせたくなったら、記事内に埋め込むことを検討したい。
まとめ
- 個人サイトをNext.jsとesaで作った
- 1日ぐらいでサクっと作れたのでよかった