Next.js と microCMS と Vercel が面白い。それぞれ面白いし、組み合わせるとさらに面白い。なにせ、メディアサイトがデプロイも含めて 2 時間で出来る。
Next.js + microCMS + Vercel すごいな。メディアサイト(中身スッカスカだけど)がものの 2 時間でデプロイまでできた。
ということで、メディアサイトを作りながら、Next.js と microCMS と Vercel の面白さをまとめる。
2 時間で作るメディアサイト
例として「ラーメンまとめ!」というメディアサイトを作ってみる。このサイトには
- ラーメン屋
- ラーメン屋のまとめ記事
の 2 つの種類のコンテンツがある。「ラーメン屋」が「名前」「場所」「ラーメン写真」というプロパティを持っていて、個別のラーメン屋を表現する。それをいくつか束ねて記事とするのが「まとめ記事」である。「ひとことコメント」とともにラーメン屋を紹介する。
これがトップページ。まとめがリストアップされている。
まとめの個別ページがこれである。2 つの家系ラーメン屋が紹介されている。
それでは作ってみる。
Next.js の雛形
Next.js は言わずとしれた「React コンポーネントを扱うためのオールインワンパッケージ」である。というと未来感があるが、おじさんにとっては「MVC」の V に特化した Web フレームワークとまず解釈すると入りが楽だ。Twig でも Slim でも erb でも Jinja でも Emmbed Perl でも Template-Toolkit でも何でもいいんだけど、 include 'partial/header'
するとヘッダーが再利用できるよね〜、と似たノリで React のコンポーネントも捉えちゃう。
で、いざ使っていくと、Next.js だけじゃ足りないのと、便利な機能を追加したくなった。「設定ファイル」がどんどん増えていった。
package.json
eslintrc.js
jsconfig.js
next.config.js
postcss.config.js
stylelint.conf.js
tailwind.config.js
ちょっと、どんだけあるのよw もう、どれが何してるか分からないw
何度もやりたくないから boilerplate を作った。
こんだけ設定しておけば便利だ。例えば、VSCode では、Tailwind CSS のクラス名を補完してくれて、どんな色かまで色で教えてくれる。すごい!
microCMS で API を生やす
microCMS はよくできた国産のヘッドレス CMS である。ヘッドレスというのは「ガワ」を持たないってことで、管理画面はあるんだけど、コンテンツの情報を API 経由で取得するのが前提となっている。例えば、今回は
- ラーメン屋
- まとめ記事
という 2 つの種類のコンテンツ API を作った。それぞれ、プロパティを設定すれば、勝手に API が生える。優れものだ。「ラーメン屋」のスキーマはこんな感じ。
それに対してエンドポイントがあるので、例えば
$ curl "https://ramen-matome.microcms.io/api/v1/shop" -H "X-API-KEY: MY_API_KEY"
を叩くと、
{
"contents": [
{
"id": "e2s_0m9kzr",
"createdAt": "2021-09-16T00:37:44.213Z",
"updatedAt": "2021-09-16T00:37:44.213Z",
"publishedAt": "2021-09-16T00:37:44.213Z",
"revisedAt": "2021-09-16T00:37:44.213Z",
"name": "たかさご家 関内店",
"place": "関内",
"photo": {
"url": "https://images.microcms-assets.io/assets/44994daa95884a0b978f9d5d6130c8ff/999f4fa1936f43788413fec37a73e6c1/IMG_3788.JPG",
"height": 3024,
"width": 4032
}
},
{
"id": "lj0-1lmkos",
"createdAt": "2021-09-16T00:34:56.874Z",
"updatedAt": "2021-09-16T00:34:56.874Z",
"publishedAt": "2021-09-16T00:34:56.874Z",
"revisedAt": "2021-09-16T00:34:56.874Z",
"name": "たまがった",
"place": "横浜駅西口",
"photo": {
"url": "https://images.microcms-assets.io/assets/44994daa95884a0b978f9d5d6130c8ff/52456c5cc0904c8999a6e08719eef302/IMG_4983.JPG",
"height": 3024,
"width": 4032
}
},
{
"id": "f9sb50o1a7cw",
"createdAt": "2021-09-16T00:31:57.808Z",
"updatedAt": "2021-09-16T00:31:57.808Z",
"publishedAt": "2021-09-16T00:31:57.808Z",
"revisedAt": "2021-09-16T00:31:57.808Z",
"name": "らーめん まつや",
"place": "茅ヶ崎",
"photo": {
"url": "https://images.microcms-assets.io/assets/44994daa95884a0b978f9d5d6130c8ff/e6da83cb6032468393485616f0ada16a/IMG_4526.JPG",
"height": 3024,
"width": 4032
}
},
{
"id": "0q1-7rw7n1su",
"createdAt": "2021-09-16T00:30:01.008Z",
"updatedAt": "2021-09-16T00:30:24.719Z",
"publishedAt": "2021-09-16T00:30:01.008Z",
"revisedAt": "2021-09-16T00:30:24.719Z",
"name": "維新商店",
"place": "横浜駅西口",
"photo": {
"url": "https://images.microcms-assets.io/assets/44994daa95884a0b978f9d5d6130c8ff/5d53778942da47938e1c69c2a83b9dc6/IMG_4499.JPG",
"height": 3024,
"width": 4032
}
},
{
"id": "9t2ml0zbi0ck",
"createdAt": "2021-09-16T00:26:48.272Z",
"updatedAt": "2021-09-16T00:30:13.450Z",
"publishedAt": "2021-09-16T00:26:48.272Z",
"revisedAt": "2021-09-16T00:30:13.450Z",
"name": "吉村家",
"place": "横浜駅西口",
"photo": {
"url": "https://images.microcms-assets.io/assets/44994daa95884a0b978f9d5d6130c8ff/77781624aec34868b3c017d34cdaab32/IMG_4945.JPG",
"height": 3024,
"width": 4032
}
}
],
"totalCount": 5,
"offset": 0,
"limit": 10
}
こういう JSON が返ってくる。
microCMS で面白いのは、オブジェクトのネストみたいなことが出来ること。今回、「まとめ記事」の中に「ラーメン屋」を複数件、コメントとともに埋め込んでいる。
これを工夫すれば、ちょっとくらい難しいデータ構造なら表現できてしまう。「吉村家」を使いまわして「家系ラーメンまとめ」と「横浜駅周辺のラーメン屋まとめ」どちらにも表示できるし、なんなら、コメント添えて、ランキングにすることも出来る。
管理画面って作るのめんどいので、microCMS は「それを代行してくれてる」って考えると素晴らしい。
Next.js でページを作る
- ラーメン屋
ramen-matome.microcms.io/api/v1/shop
- まとめ記事
ramen-matome.microcms.io/api/v1/topic
この 2 つのエンドポイントができたので、これを叩いてコンテンツをとってきて描画する Next.js アプリを作る。これはまぁ愚直にコードを書けばいい。
Next.js で面白いのは、SPA っぽくするか、サーバーサイドでレンダリングするか(SSR)、静的 HTML を生成させるか(SSG)を切り替えられることだ。今回は SSG をした。
SPA/SSR 前提で書いてて、SSG にした時に問題となるが、「どのページをレンダリングすればいいのか、そのままでは分からない」という点である。そこで、getStaticPaths
を使う。まとめ記事、つまり /topic/[id]
にはこんな記事がありますよ〜ってのは topic
の一覧を API から取得して指定する。
export const getStaticPaths = async () => {
const data = await client.get({ endpoint: "topic", queries: { depth: 1 } })
const paths = data.contents.map((content) => `/topic/${content.id}`)
return { paths, fallback: false }
}
コンテンツを書く
管理画面ができて、ガワができたら、仮でもいいので最初のコンテンツを入れる。
コンテンツを入れながら、手元の Next.js のページの調整をする。microCMS の管理画面に「ラーメン屋」を投稿。localhost:3000
を確認。今回は目視。
ページを確認する
できたら next build
しよう。
Page Size First Load JS
┌ ● / (809 ms) 444 B 68.9 kB
├ /_app 0 B 68.5 kB
├ ○ /404 272 B 68.7 kB
├ ○ /about 271 B 68.7 kB
└ ● /topic/[id] (1603 ms) 3.73 kB 72.2 kB
├ /topic/rf3_kh8tm (807 ms)
└ /topic/7l3j1pv3bwh6 (796 ms)
+ First Load JS shared by all 68.5 kB
├ chunks/framework.895f06.js 42 kB
├ chunks/main.c4f254.js 23.6 kB
├ chunks/pages/_app.97eedb.js 2.12 kB
├ chunks/webpack.1a8a25.js 729 B
└ css/ee45ea2207ca4ade5bc9.css 306 kB
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps)
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
いちいち表示される文字列がおしゃれだよね。
Vercel へデプロイする
いよいよデプロイしよう。こういうのってデプロイが一番大変なんだけど、Vercel を使えば、以下の工程を一気通貫でやってくれる。CI と CDN とサーバーレスの機能を混ぜ込んだみたいだ。
- Git レポジトリと連携
- ソースの取得
- ビルドしてページの生成
- HTML ページを CDN へ置く
- ドメインにアサイン
master なり に push すると自動的にビルドが走って、いつの間にかデプロイが完了する。楽だし、裏で動いてる感が楽しい。
ドメインに関して、独自ドメインを持ってくることも出来るし、もともとついている vercel.app
のサブドメインが使える。ramen-matome.vercel.app
にした。SSL も付いてくる。
これ、面白いのは、プレビュー向けのドメインもアドホックに発行してくれる点だ。デプロイの度に ramen-matome-c1wgyfbyl-yusukebe.vercel.app
といっためちゃくちゃなホスト名の URL になる。この URL 経由のページに関してはヘッダに x-robots-tag: noindex
が付加される。つまりインデックスされない。この URL を検証用にして、それを他のメンバーが見て確認する、なんてことが出来る。
Webhook する
Vercel には SSG したファイルがのるわけだから、microCMS でコンテンツを更新したとしてもそのままではページは変更されない。その点は microCMS から Vercel に Webhook を飛ばして、都度ビルドしなおすってことが出来る。
まとめ
これでパーフェクトだ!2 時間以内にできただろうか!今回作ったサイトは以下で確認出来る。
面白い。
例えば、Vercel は Next.js だけじゃなくて、他のサイトジェネレーターにもデフォルトで対応していて、Hugo もそのひとつなので、このブログを Vercel でビルド&ホストすることも楽勝である。
この 3 つ。まだの人は体験するといいだろう。