はじめに
お久しぶりです。お盆休みに入りましたね。この夏は比較的涼しいですが、皆様いかがお過ごしでしょうか。
私は、このお盆期間にホームページの大改修を行いました。ようやく区切りがついたところです。
以前、ブログのバックエンドをNotion基盤に変更したということを報告しましたが、それに変更してから、Notionのdata sourceをDBのテーブルの代わりに使って色々できるんじゃないかと考えました。そこで、以前からホームページに載せたかったイベントへの出演履歴ポータルを実装しようしたのが事の端緒です。ところが、開発を始めたら変なゾーンに入ってしまって、結果的にサイトのデザインなど色々なところに手を出して単なる機能追加ではなく、大規模な改修になりました。
以下では、変更ログ的な感じで今回の改修でぶつかった課題とどう解決したか、こだわりのポイントなどを説明したいと思います。また、NotionをCMS代わりに使って、同様にアーティストブログやイベントページを作りたいクリエイターの参考になればと考えております。そのため、ソースコードは公開しています。
イベントの出演履歴ページの作成とそこでぶつかった課題
目的
今回実装したイベントの出演履歴ページはNotionのdata sourceを使って出演依頼をいただいた際の告知とこれまで出演したイベントのアーカイブを目的としています。そのため以下が必要でした。
- 告知やアーカイブの際に必要な情報の提示
- フライヤー、イベント名、開催日時、場所のわかりやすい表示
- 開催場所のGoogle MapやアーカイブのYoutubeをiframeで埋め込み表示できること
- 外部サイト共有時にOGPやTwitter Cardとして主要情報が表示されること
イベント情報については、data sourceのプロパティで事足りるのですが、他の2点がNotionの仕様上結構厄介でして、下ではそれに対してどういうアプローチをとったかについて話そうと思います。
Notion Blockの埋め込みブロックの表示
Notionの便利なところは、外部URLなどを埋め込みで表示できるところです。その特性を活かして、イベント詳細ページでNotionのページ同様に開催場所のGoogle Mapやアーカイブ目的にYoutube動画を埋め込みで表示できるのが理想でした。そうすることでイベント詳細ページで開催情報とイベント後のアーカイブが一覧できるポータルとして活用できます。
以前のブログのバックエンドを移行した際に、Notionのブロックオブジェクトを自動的に必要なhtml要素に変換する機能を実装しました。そのため、今回は特に何か手を施さなくても理想のポータルを作れると思っていました(が、その考えは甘かった)。
実際にイベント詳細を開発環境で開くと残念なことに埋め込みが表示されません(泣)。
色々と調べてみたらNotionは以下の方法を使って、iframeの埋め込みを実現していたのです。
flowchart TD
a("共有リンクや短縮URLを貼り付ける")
--> b("iframelyを使って短縮URLをembed URLに変換")
--> c("Notionで埋め込みiframeを表示")ここのiframelyを通して埋め込みURLに変換しているというのが肝です。iframelyは短縮URLを埋め込みurlに変換するサービスを提供するSaaSで、利用はもちろん有料です。
そして、この変換プロセスはNotionのバックエンドで行われるため、実際にAPIから返却されるURLは元の短縮URLでした。そりゃ当然iframeとして当サイトで表示されないわけです。
今回のアプローチ
ということで、埋め込み表示に対応するために当サイトでも同様に短縮URLから埋め込みURLに変換するロジックをバックエンドに実装しました。
ただ、無料で実現したい…
そこで更に色々と調べると、多くのサービスはoEmbedというプロトコルを使って、サイトのURLを埋め込みURLに変換するAPIを提供しているとのこと。
これを利用すれば、iframelyのようなサービスを使わなくてもサービス側がoEmbedプロトコルに対応したAPIを用意していれば埋め込みURLの変換を実現できます。
ただし、Google Maps以外は…
厄介なことにGoogle Mapsはこの変換用APIが公開されていませんでした。
そこで以下のアプローチを取りました。
flowchart TD
a("記事のNotion Blockを取得")
--> b("Embedプロパティ")
b --> c(["Google MapsのURLの場合"])
subgraph Embed Service
c --> ca("Google Maps Embed APIにリクエスト")
--> cb("Embed URLの取得")
end
b --> d(["Google Maps以外の主要サービスURLの場合"])
subgraph Embed Service
d --> da("oEmbed変換APIにリクエスト")
--> db("Embed URLの取得")
end
cb --> e("Embed URLをフロントでiframeとして描画")
db --> e
Google MapsはoEmbed変換APIを提供していない代わりに、Google Maps Embed APIを提供しています。そして、ありがたいことにEmbed APIはリクエストに対して料金が発生しません。これとoEmbed変換APIを利用することで、記事にiframeで埋め込み表示を実現できました。
OGP、Twitter Card
先ほども話しましたが、このイベント詳細ページはイベント情報のポータルとして活用することが目的です。そのため、出演依頼をいただいた際は、SNSで共有した際に、イベントの詳細情報が併記してあるOGPやTwitterカードとして出したいという願望がありました。
しかし、ここでまたNotionの仕様上の問題に遭遇します。
それは、Notionにアップロードされた画像をAPIからで取得する際、画像のURLに1時間の有効期限が設けられているということです。OGPにフライヤーの画像が見られるようにしようと考えていたため、この制約が厄介です。せっかくX等にイベント詳細のリンクを貼っても、1時間後にそのポストを閲覧したユーザーはフライヤーの画像が見られません。
今回のアプローチ
そこで、バックエンドにイベント記事取得時に記事idをキーにして、フライヤー画像をdbとストレージに永続化する仕組みを設計しました。
そして、Notionには編集したタイムスタンプがプロパティにあるので、それもdbのカラムに追加して、記事を取得した際にdbのタイムスタンプと記事のタイムスタンプを比較して、記事が変更されていたら、永続化されている画像を更新するロジックを組んでいます。
flowchart TB
a("記事リスト取得")
-->b("supabase上のDBをクエリ")
-->c("DB上の該当記事idがあるか?")
c-->d
c-->e
subgraph Image Service
d(["idがない場合"])
-->da("idがない場合は新しいエントリーを追加してストレージに追加")
-->db("DBからストレージのurlを取得")
end
subgraph Image Service
e(["idがある場合"])
-->ea("記事のupdatedAtとDB上のupdatedAtを比較")
-->eb("更新されていた場合、DBとストレージを更新。更新されていない場合、urlをDBから取得。")
end
db-->f("urlをプロパティにマッピング")
eb-->fこれにより期限の制約を受けることなく、OGPやTwitter Cardに画像を表示できるようになりました。
また、アップロード処理にsharp.jsを使って画像を軽量なwebpに変換しているため、ストレージも圧迫せず、supabaseの1GBの無料枠で利用可能かつ、クライアントのデータ負担も少なくて済みます。
サイトデザインの刷新
イベントの出演履歴に加えて、サイトのデザインも大幅に刷新しました。このデザイン刷新で意識したことが二つあります。
- ライブコーディングのアーティストというアイデンティティを反映したトンマナとデザイン
- 記事の可読性を意識した文字組とレイアウト
ライブコーダーとしてのアイデンティティを反映したデザイン
今回サイトの刷新で大きく変わったのがトップページのヒーローセクションです。
以前はなんというか、いまいちパッとしないヒーローセクションだったのですが、今回はそこを変えたかったです。そこで意識したのが、私のライブコーダーというアイデンティティです。
もとから、キービジュアルがin the blue shirtのアリムラさんと合作したミュージックビデオのクリップでした。そのキービジュアルはガッツリライブコーディングをしている様子を映しているものです。
これほど、全面にライブコーディングを出しているため、ページのトンマナもエディタの画面や開発者によせたものにしました。
可読性向上のための文字組とUIレイアウト
今回意識したのが可読性です。この記事のようにこのサイトは単なるポートフォリオとしてだけでなく、ブログの機能も果たしています。そのため、読者にとって一番快適な体験を提供したいと考えていました。
ヘッダーのUIレイアウト
改修前から本サイトは、コンテンツをヘッダーが邪魔しないように、片手でもナビゲーションしやすいようにという目的でヘッダーのメニューを常時右側に配置していました。
以前は、ヘッダーに要素が少なかったので、これでも問題はなかったのですが、今回ダークモードのトグルなどを追加したため、UIが本文に被り、本文の表示領域が狭くなる問題が発生しました。
これは、使いやすさ以前の問題です。
そのため、今回の改修で、モバイルビューの際にヘッダーUIを一般的なバースタイルにしました。また、操作性を意識して、ヘッダーUIは3つのブレイクポイントで表示が変わるようになっています。
文字組
また、今回初めて、文字組を意識してデザインを行いました。以前は、line-heightがデフォルトのままで、全体的に窮屈な印象でしたが、今回はパラグラフに余裕を持ったline-heightとマージンをもたすことで、読みやすさを追求しました。また、ダークモードにも対応して、夜間での読みやすさに配慮しています。
ただ、文字組についてはまだ自分のなかで正解が固まっておらず、まだまだ勉強の必要性を感じています。
最後に
ということで、以上が今回の大改修の概要です。ここまで読んでくださった方はありがとうございます。
以前と比較して、だいぶ良いサイトになったと自負していますが、まだ課題はあります。たとえば、記事一覧や記事のロードタイムです。今回バックエンド側でクエリのキャッシュを実装したため、以前と比較して早くなっていますが、最適化されたサイトと比べるとロードが長いです。Vercel自体があまり早い印象がないので、ここはデプロイ先を変えるというのも手かもしれません。