「CodeGrid」の概要と構成

中村享介氏:次に自社サービスの「CodeGrid」をJamstackにした話に入ります。CodeGridはもう8年ぐらい運用しているメディアで、継続して毎週3本のフロントエンドの記事を編集者が編集して、読みやすい日本語で出しています。

サブスク型の有料メディアで、ログインやお気に入り、検索、購読、購読の解除などの機能があるサービスです。現在4,500ページぐらいビルドしています。

こういうメディアサイトのようなものはけっこうJamstackに向いていると思っていて、例えば海外の「Smashing Magazine」は、2020年にWordPressからJamstackに移行したニュースが上がっていました。アクセスが多いサイトは、Jamstackが有利ですね。

次に、今までのCodeGridの構成です。(図では)基本的な構成はすごく単純化していますが、Herokuにサーバーがあり、裏側にMongoDBがあって、それで全部サービスを配信している、すごくシンプルなサーバー構成になっています。

ただ、Herokuが全部担当するので、アクセスがちょっと増えた時や、DBのクエリが複雑なページは遅くなったりします。Heroku自体のサービスはあまり落ちませんが、自分のコードのミスで落ちたりはするので、そうするとサービス全体が止まってしまうことがあります。

「CodeGrid」の構成のデメリットをどう改善したか

では、それをどういうふうにしていったかという話です。まずは「静的サイトジェネレーターを何にするか?」をいろいろ検討して、11tyを使うことにしました。先ほど紹介したとおり、すごくシンプルなHTMLを吐き出す静的サイトジェネレーターで、リニューアルした当初はまだ使われていなかったのですが、(現在では)jamstack.orgでも使われているものです。

それと、GraphCMSです。GraphQLでコンテンツのAPIを作りたかったので、GraphCMSを採用して、GraphCMSにコンテンツデータを移すようにしました。

(スライドを指して)11tyはこんな感じのサイトです。

GraphCMSはこんな感じです。GraphCMSはけっこうおもしろくて。GraphQLのAPIしかないCMSで、感触としてはデータベースに近く、GraphQLに特化した仕組みになっています。

ホスティングサービスはNetlifyを使っています。一部、どうしても作らなければいけない機能はNetlify Functionsを使いました。

(スライドを指して)Netlifyはこういう感じで、今はけっこうすっきりしたサイトになっています。

このNetlifyのCEOであるMattさんは、Jamstackという言葉を作った人です。そのため、NetlifyはJamstackのためのサービスかなと思い、Netlifyを採用しました。

やっていることとしては、GitHubが更新されたらGraphCMSからデータ取ってきて、11tyでデータを埋め込みつつビルドして、静的なHTMLをNetlifyに上げることです。

機能に関して、お気に入りしたりするAPIなどは、Netlify Functionsに上げるようにしました。

(スライドを指して)全体を単純化するとこのような感じで、Netlifyにほとんどのデータがあり、機能を使う時はNetlify Functionsにアクセスします。Netlify Functionsは、もともと使っていたMongoDBにアクセスしたりしなかったりとかして、ブラウザーにデータを返す感じです。

Jamstackに変えてよかった3つのこと

Jamstackに変えてよかったことを3つ紹介します。1つ目はページ表示速度の改善です。「高速なサイトを作る手法」と言っているので、ここができないと意味がありません。(スライドを指して)今、CodeGridのトップページの結果を最新のもので測ってみましたが、静的HTMLなので非常に速いです。

もともとフロントエンドは得意で、その最適化は簡単にできたので、だいたいこのような感じです。100点になっていないのは、トラッキング用のJavaScriptなどが入っているせいですが、100点であればいいものでもないので、ユーザーにとっては十分速いし、いいと思っています。

この速度はCore Web Vitalsなどで非常に大事になってきているので、Jamstackにすると、わりと簡単にすごい速度のサイトが作れておすすめです。

2つ目は「開発のしやすさ」です。構成が分かれているので、HTMLとCSSが得意な人がサイトをいじって、DBに詳しい人はAPIを、という分業がすごくしやすくなりました。

あと、大事なのはプルリクごとにプレビューができることです。(スライドを示しながら)GitHubでプルリクを送ると、Netlifyのデプロイの行が足され、スライドの赤枠部分をクリックするとドメインが振られ、サブドメインがデプロイプレビューで作られて、自動的にアプリが立ち上がる感じです。

こういうことをやりやすいのは、おそらく静的サイトだからです。これがサーバーでDBにアクセスするような状況だと、本番DBの構造との差があったりしてスナップショットにはならないし、ほかの更新とぶつかってしまうようなことも起こりやすいかと思います。その点、静的サイトなら、その時点でのきれいな状態のものを吐き出して、プレビューできるようになっています。

3つ目、リニューアルして一番よかったと思っているのは、Jamstackをよく理解できたことです。弊社はお客さんのサイトやサービスを作っているので、実際の事例ができたし、それによって紹介もできる感じです。気をつけなければいけないポイントもよくわかったので、提案しやすくなったと思っています。

基本的にはJamstackにして正解だったと思っています。

運用してわかった3つの失敗

一方、しばらく運用してみて「このあたりはこうじゃなかったほうがよかったな」ということが出てきたので、そこを紹介していきたいと思います。

この作業をやったのは2年ぐらい前で、あまりJamstackについて言われていなかった頃だったので、試行錯誤しつつやりました。そのため、いろいろな失敗もありました。ただ、こういう失敗こそが一番のノウハウだと思うので、今日、共有できたらいいかなと思っています。

1つ目が、毎回すべてをビルドする構成を取っていたことです。Jamstackにリニューアルするにあたって、モノレポ(モノリポジトリ)でいっぱいパッケージを作り、それぞれライブラリなどを全部入れ、ビルドする構成にしました。

モノレポにしたこと自体はぜんぜん問題なかったですが、モノレポにする際によくやりがちな、必要なパッケージを全部ビルドして、その上でサイトをビルドするようなことをしていました。

そうすると、全パッケージに含まれている依存するものすべてをインストールしなければいけなかったので、コマンドは一発ですが、3分ぐらいかかります。

CI(Continuous Integration)開始から、全パッケージビルドして終わるまで約10分ぐらいかかります。そうするとあまりよくなくて、今思うと、サイトを作るところ以外は分離したほうがよかったなと思っています。

Jamstackは、ビルド時間が問題になることがとても多いです。コンテンツ量が多いならやはり工夫が必要で、1万ページ超えたらわりと大変だと思います。Jamstack関連は、いろいろな新しいソリューションがいろいろなところから出てきますが、たいていはビルド時間を改善するものが多いです。

例えば、Gatsby.jsはインクリメンタルビルドと言い、差分だけビルドすることでビルドの時間を短くしようとしているし、NetlifyはDPR(Distributed Persistent Rendering)というソリューションを出しています。

DPRは、あまりアクセスされないページは初期ビルドしないで、はじめてアクセスされた時にビルドしようとするものです。Jamstackのビルドの時間を短くしようとするものがいくつか出てきています。

なので、Jamstackを採用した場合は、ビルド時間を常に意識するようにしたほうがいいと思います。

ちなみに、現在のCodeGridはチューニングしたり、モノレポで「ここはいらない」というところを解体しているので、今ビルド時間を見てみたら、CIの開始から終わりまでで4分ぐらいで終わっています。以上が、ビルド時間が大事という話です。

次がインフラ特有の機能を使ったことで、これも失敗だったと思っています。せっかくNetlifyの機能に便利そうなものがあるので、いろいろ使ってみようと思っていました。

例えば、Netlify Formsでは、ちょちょっと書くだけで簡単にフォームが作れるので、お問い合わせフォームを作ってみたりしました。あとNetlify Functionsは構成のところで出てきましたが、簡単にAPIが作れるので採用しました。

ほかに変わったところでいくと、Role-based access controlという機能があります。これはおもしろくて、ログインユーザーの属性でURLに対してリライトやリダイレクトのようなことができるものです。

少しわかりづらいので、具体例を出します。購読してくれている人と購読していない人に対して、ページの表示要素はけっこう変わってきますが、購読している人は本文も含めて全部読める。購読していない人は本文が読めない。さらに、購読フローに入るためのリンクがあったりなど、ぜんぜん違うものが出ている感じです。

これらを2つとも先に生成しておいたら、pre-renderingとしてはすごく正しいんじゃないかと思い、そういう構成にしました。2つ吐き出しておいて、Role Based Redirectsという機能で出し分けるようなことをしました。これによってメチャクチャ早くページが表示される構成になりましたが、今考えると失敗だったなと思っています。

なぜ失敗なのかというと、先ほどビルド時間が大事という話をしましたが、購読している人、していない人の両方をやると、ページ数を2倍にしないといけないので、ビルドの時間が延びます。

もう1つ大事なことが、このようにNetlify Formsからなにからたくさん使ってしまうと、Netlify以外にデプロイができなくなってしまうんです。

これはきちんと分離して、1ページだけビルドしておいて、権限に応じて記事を表示するかしないかを返すAPIを用意すべきだったと思っています。

なぜそう思うかというと、Jamstackには、ポータビリティという特性があります。ただの静的ファイルでインフラを選ばないので、どこでもデプロイできるということです。

これは公式サイトにも記載されていますが、ざっと訳すと、「Jamstackのサイトはあらかじめ生成されているので、どこでもホストできるよ」ということです。

「インフラストラクチャーのロックインとはおさらばです」と書いてありますが、多くの機能を使ったせいで、インフラロックインされまくっています。

例えば、会社のサイトでも、同じようにJamstackで作っています。こちらはインフラ特有のサービスはぜんぜん使っていなくて、NetlifyとAWSのAWS Amplify Console、Cloudflare Pagesの3つのサービスにデプロイしています。

今本番で使っているのはCloudflare Pagesで、プルリク(プルリクエスト)1個を作るだけで3つのサービスが動き出して、3つデプロイされるようになっています。もし何かトラブルがあっても、DNSを切り替えればいつでも移行できる体制になっています。

Jamstackとしては、こういうふうにしておいたほうがいいのではないかと思っています。しかし、便利な機能はどうしても使えなくなってしまうので、インフラの特有の機能を使う時は、ポータビリティという点が失われないか、失われても、それ以上にメリットあるかを、きちんと考える必要があると思いました。

最後、3つ目の失敗したことはAPIを完全分離しなかったことです。これはどういうことかというと、何度も出てきているNetlify Functionsです。簡単にAPIができるし、フロントと一緒にデプロイもできるし、CodeGridはたいしてAPIの数はないので、「いいんじゃないかな」と思って採用しましたが、これもよくなかったと思っています。

なにがよくなかったのかというと、ビルド時間です。APIだけ更新するのでもサイトをビルドしなければいけないし、サイトを更新するのにも無駄にAPIを更新しないといけない。そして、先ほども出てきたように、インフラはロックインされることになります。

Jamstackの基本原則に戻りますが、基本原則はpre-renderingとdecouplingです。decouplingについては、用語の説明としてもう少し詳しくきちんと書いてあります。「decouplingは、システムやサービスをきれいに分離することで、独立して交換や更新ができる」ということです。

Netlify Functionsだと、サイトとAPIが独立して更新ができないので、APIはリポジトリごとに分けて、別に開発・デプロイというかたちを取ったらよかったと思っています。

Jamstackは正しく理解することが大切

まとめです。Jamstackとは、ビルドで静的HTMLを作って、動的機能をAPIで分離する手法のことです。CDNで配信できるので、高速でセキュアという特徴があります。

ライブラリは特に規定されていないので、好きなSSGを使えばよく、ホスティングサービスもたくさんあるので選び放題です。

問題になるものとして、Jamstackではビルド時間は常に気をつけましょうということです。

そして、静的ホスティングサービスの機能依存に注意です。便利な機能がたくさんありますが、サービスとしては、みんなロックインされたほうがおそらくビジネス的にはいいと思うので、そこは気をつけましょうというところです。

基本原則はpre-renderingとdecouplingで、APIをいかに完全分離するかが大事です。

正しく使えば、高速でセキュアでメンテナンスをしやすいサイトやアプリが作れるので、Jamstackを正しく理解して使いこなしてほしいと思います。あと、間違って紹介されているサイトも多いので、公式サイトをきちんと読むといいと思います。

以上で、僕のセッションは終わりにしたいと思います。ありがとうございました。