logo

アルパカログ

easy-notion-blog v0.9 アップデート内容の紹介

先日 easy-notion-blog バージョン0.9をリリースしました。

バージョン0.8の紹介記事を書く前に0.9をリリースしてしまったので、この記事ではバージョン0.8と0.9の内容を紹介します。

また、バージョン0.9ではこれまで長らく問題だった画像がリンク切れしてしまう問題が解決しています。何が原因で画像が表示できなかったのか、そしてどうやって解決したのかについても解説します。

バージョン0.8の主な内容
  • コードブロックのスタイル修正とテキストカラー対応 #76 #78
  • タグ別ページのページネーション #79
  • シンプルテーブルのスクロール対応 #84
  • 依存ライブラリのアップデートと整理 #86 #87

バージョン0.8の詳細は下記をご覧ください。

バージョン0.9の主な内容
  • マーメイド記法によるフロー図のセンタリング #90
  • YouTube 動画のサポート #93
  • SWRを使って画像の再取得(画像切れ問題の対応) #95

バージョン0.9の詳細は下記をご覧ください。

ℹ️
すでに easy-notion-blog をお使いでバージョンアップを行う方は easy-notion-blogをフォークした後に本家の変更を取り込む方法 をご覧ください。
画像が表示されない問題

これまで easy-notion-blog では画像が表示されない問題が長く続いていました。

実は、開発者の私には心当たりがありました。

バージョン0.6で対応した、ビルド時に記事中に使われている画像を取得するようにした下記の変更でした。

原因がわかっているなら元に戻せばいいじゃないと思われるかもしれませんが、実はこの変更にも経緯があったのです。しかも1つではなく2つです。

1番目の経緯

1つ目は、このブログを Vercel から Google Cloud へ移行したきっかけにもなったのですが、それは Vercel の Source Images Limit という画像表示の件数に関する制限事項でした。

ℹ️
アルパカログを Google Cloud へ移行した経緯と方法の詳細は Next.js製のNotion BlogをVercelからGoogle Cloud Runに移行した をご覧ください。

Next.js には next/image という画像表示を最適化するためのコンポーネントが用意されており、HTMLの <img> タグの代わりに <Image /> タグを使うことで簡単に使用することができます。

この next/image は、ページを閲覧しているユーザーの端末に合わせて自動で最適なサイズの画像を配信してくれる優れものですが、Vercel で使用するには上記の Source Images Limit を守らなければなりません。

VercelのHobbyプラン(無料プラン)ではこの画像最適化の上限は月1,000件になっており、これを超えると上記ツイートのように警告される事態になります。

「そんな1,000件も画像を使っているブログなんて稀では?」と思うかもしれません。私もそう思っていました。公式ドキュメントを読むまでは。

下記は 公式ドキュメント の抜粋です。

  • <Image src="/logo.png" width={100} height={100} /> 1件としてカウント
  • <Image src={'https://cdn.example.com/${path}'} width={100} height={100} /> N件としてカウント

おわかりいただけるでしょうか。

前者は画像ソースが固定なので1件としてカウントされます。

しかし、後者は画像ソースの一部が変数になっているためN件、つまり上記で言うと path が取りうる値の数だけカウントされてしまいます。

easy-notion-blog は Notion API から画像URLを取得しているため後者に該当します。

そして画像URLは取得のたびに変わります。あとは、、、お察しの通りです。

これが1番目の経緯です。

無料でできることが easy-notion-blog の強みなのに、有料になってしまっては元も子もありません。

仕方がないので next/image を諦めることにしました。

しかし、next/image を諦めて img タグに戻すだけで良いかというと、そんな単純な話でもないのです。

ここから2番目の経緯に繋がってきます。

2番目の経緯

Notion APIで取得した画像URLには有効期限があります。

公式には見当たりませんが現在は1時間となっているようです。

有効期限を過ぎると画像が見えなくなってしまいます。

さて、Next.js にはISRというイカした機能が搭載されており、easy-notion-blog でもフル活用しています。

ISRは主に次の2つの特徴から成り立っています。

  • ユーザーがページを訪れた際、ページを高速に表示するためにあらかじめ生成しておいたHTMLファイルをレスポンスする
  • ユーザーがページを訪れた際、ページの内容が更新されて生成し直す必要があったとしても古いHTMLファイルをレスポンスし、裏で再生成する

ISRはその圧倒的な高速化の代償として、いったんは古いHTMLファイルをレスポンスします。

ええ、勘の良い方はお気付きでしょう。

この「古いHTMLをレスポンスする」という特徴と「有効期限のある画像URL」の相性の悪さに。

つまり、ユーザーがページを訪れた際には前回生成されたHTMLがレスポンスされますが、そこに含まれる画像URLはすでに期限が切れてしまっているケースがあるということです。

ISRは期限の切れたHTMLを裏で再生成するので、何度かページを更新すれば最新のページを表示することができます。

しかし、普通のユーザーは画像のリンク切れを見つけた時点で帰ってしまうでしょう。

ここでひとつ疑問が湧いてきます。

もともと next/image を使っていたときもISRだったのに、なぜ画像がリンク切れしなかったのか?ということです。

答えはシンプルで、next/image をVercel上で使うと、Vercelが画像を自動でキャッシュ(保存)してくれるからでした。

つまり、Vercelが期限付きURLから画像を自動で取得・保存・配信することで、Notion側の都合に関係なく画像を扱えるようになっていたのでした。

ううむ、Vercelさすがですね。

話を戻しましょう。

ISRを使っている以上、単純に next/image をやめて <img> タグに戻すだけではダメということがわかりました。

ここで取りうる選択肢は2つあります。

  • ISRをやめる
  • next/image のように画像をキャッシュする

ISRをやめてサーバーサイドレンダリング(SSR)に変更するという手もありましたが、せっかくVercelでNext.jsを使っているのにISRを使わないのはもったいない(し、なんだか負けた気がする)ので、画像をキャッシュすることにしました。

そう、これが経緯の2番目です。

バージョン0.6で対応した、ビルド時に記事中に使われている画像を取得するようにした下記の変更でした。
今回どうやって解決したのか?

バージョン0.6では、ビルド時に記事中の画像を全て取得しVercelに保存していました。

しかし、ここは推測ですが、画像サイズが大きいなどの理由(他にも原因がありそう)で画像の取得に失敗することがあるようでした。

そうなってしまうと、常に画像が表示されなくなってしまいます。

今回のアップデートではビルド時の画像保存をやめ、Notion API から取得した期限付きの画像URLを <img> タグに設定するように変更しました。

期限付きURLはISRと相性悪かったのでは?

と思いますよね。実は同時に新しい仕組みも導入しています。

ISRで古いページをレスポンスした後、何もしなければ古いページのままです。

しかし、クライアントから JavaScript によって最新のデータを取得し、ページを再描画してあげれば、一瞬古いページが表示された後、すぐに最新の情報が表示されます。

このロジックを簡単に書くために React 向けに SWR という機能が用意されています。

そしてこの SWR を開発・提供しているのも Vercel です(神なのか?)。

バージョン0.9ではこの SWR を使っています。

もう少し詳しく説明すると、Notion API が返す画像ファイルの情報には URLの有効期限が載っている ので、有効期限が過ぎていたら SWR を使って最新のデータを取得しているというわけです。

変更の差分が気になる方は下記PRとリンクされたPRを参照してください。

このアップデートの後、これまで画像が表示されなかったユーザーも正しく画像が表示されるようになりました(検証にお付き合いいただきありがとうございます)。

easy-notion-blog の Twitter コミュニティ では、本日紹介したような最新情報をお届けしたり、ユーザー同士が活発にやりとりしているので、興味のある方は気軽にご参加ください。

以上です。

この記事では easy-notion-blog バージョン0.8と0.9の内容を紹介し、これまで長らく問題だった画像が切れてしまう問題について解説しました。