スクールのポータルサイトにブログ機能を追加した振り返り
投稿日: 2024年09月30日
こんにちは。
先日、スクール内にて匿名式の満足度・要望アンケートを実施させていただきました。
ご協力いただいた皆様、ありがとうございました。
みなさんかなり満足度は高い回答をいただいて嬉しかったのですが、
僕が気づけていなかった点のご指摘もあり、今後1つずつ改善していきたいと思っています。
設問の1つに「スクール内ブログを書いてみたいか」を聞かせていただき、
いただいた回答内では「書きたい」が100%だったので、早速作ってみることにしました。
やりたいことは、こんな感じ。
開発期間2日でしたが、
頑張った点や学びのあった点など、まとめていきます。
Figmaでざっくりとだけ、作りました。
ブログ機能、いざ運用開始してみたものの、あまり使う人がいないという未来も考えられたので、
まずはできるだけミニマム機能で出そうと思いました。
スマホデザインは作らずに実装しましたw
基本的な技術構成は、Next.js + Supabase + Prismaで、
皆さんが学習している内容と同じです。
今回の開発で悩んだポイントや、初めてやって勉強になった点を4つ紹介します。
記事を書く画面をどうしようか一番の悩みで、エディタを0から作っても楽しそうですが、
「ミニマム機能でリリース」を考えた時に、
エディタを作るのはかなり時間がかかりますし、
かといって突貫実装でエディタがお粗末だと、使いづらいから書かない、となってしまえば本末転倒。
今回はmicroCMSの管理画面で記事を書いて、それをポータルサイト上で閲覧する方式にしました。
ただし、誰が書いたのかなど、ポータルサイト上のユーザー情報と紐付けたかったので、
microCMSのアカウントは皆さん共通にしつつ、
記事を書く画面に「ポータルサイトのユーザーID」の入力欄を設け、
これを各自、執筆時に入力いただくことで、ユーザー情報と紐づけることにしました。
もしブログ文化が定着して長く続きそうであれば、
将来的にはエディタを開発し、ポータルサイト内で記事を書けるようにして、
microCMSの記事データも移行しようと思います。
Next.jsのv.13から登場した新機能で、文字通りサーバー側でコンポーネントの処理を実行するというもの。
この記事でサーバーコンポーネントの詳しい説明はしないですが、
ブログサイトであればSEO面も意識したいので、
クローラーが直接HTMLを解析できるサーバーコンポーネント(SSR)は必須だなということで、使いました。
一昔前はSSRという書き方がメインでしたが、サーバーコンポーネントの概念がでて、だいぶ開発しやすくなっていました。(やってることはほぼ同じ)
ざっくり、Reactコンポーネント内で以下のような書き方ができます。
const Page = async ({ params: { id } }: Props) => {
const profile = await prisma.profile.findUnique({
where: {
id,
},
});
return (
<div className="">{profile?.name}</div>
);
};
サーバー側で実行されるので、マウントなどの概念がなく、故にコンポーネントの関数を非同期(async)にできます。
また、apiリクエストなど行わず、直接コンポーネントからprismaを使ってデータを取得できます。
この処理がクライアント側で行われてしまうと、DBのURLなどが露出してしまうのでセキュリティ的にNGなのですが、
このコンポーネントがサーバーサイドで実行されるので、セキュリティ上も問題なく、かつ処理も早く、さらにはクライアント側の処理の量も減らせるというメリットがあります。
とても良い機能なのですが、React初学者にはあまりお勧めできないかもしれません。
現状、Next.jsでバックエンド含めて作っている会社さんってまだまだ少ないです。
バックエンドはRubyやPHP、とプロジェクトごと分けているところが多く、
その場合、Next.jsを使うメリットはあまりなく、クライアントコンポーネントのみでの開発が主流です。
なので、初学のうちはあまり手を出さない方がスキルの汎用性的にはおすすめです。
(ShiftBのカリキュラムでもサーバーコンポーネントは扱わず、全てuse clientしてもらっているのもこの理由です。)
自分は人のブログを読んでいて、よく、いいねが1回では足りないと感じることがあります。
特に、悩んでいる中でドンピシャの記事を見つけたときは、100回くらいいいねしたくなります。
ということで、そのようないいね機能実装してみました。
複数回押せるようにすることで、自ずと押したユーザー情報との紐付けが不要になります。
なので、ログインしていないユーザーでもいいねを押せるのがメリットかなと思います。
逆にデメリットとは、自分がいいねしたのかどうか分からなくなってしまう点がありますが、SNSでもないですしあまり重要ではないかなと思い、決めました。
実装的には、連打のたびにDB保存処理を行っていたら大変なので、「1度クリックされてから、1秒以上次のクリックを検知しなかったら、その間クリックされた回数をまとめて保存」という処理にしました。
スマホ表示の場合、押しやすい位置にあるので、たくさん押してみてください。
ちなみに、いいねボタンに表示する絵文字の種類は、microCMSの記事作成画面から指定できます。
一番苦労したのがこちらの機能です。(徹夜しましたw)
QiitaやZennの記事URLって、LINEやSNSで共有された時に、文字が入った画像が表示されますよね。
あの画像はOGP画像で、Webサイトが各SNSなどのクローラーに引き渡すことで表示されていますが、
画像内に記事タイトルが入っているということは、記事によって動的に画像を生成する、ということをやっています。
確かに、記事のリンクをもらった時に画像に情報が書いてあると読みやすいと感じるので、作ってみることにしました。
一昔前(10年前とか、例えばクックパッドのレシピ詳細ページのOGP)などは、記事公開時にダミーのページを作成して、そこにOGPの見た目をHTMLで作成し、その画面をスクリーンショットする形で実装をしていたようです。
それがここ数年は、imgixなどの画像生成用のサービスを使って割とお手頃に作れるようになっていて、
自分もそれらを使うのかなと思っていましたが、
なんとNext.js、その辺りの機能もフレームワーク内に組み込まれていました。
例えば、src/app/blog/[id]/page.tsx
のOGPを動的に作りたければ、
src/app/blog/[id]/opengraph-image.tsx
というコンポーネントファイルを作って、そこに 1200*630サイズのUIをReactで実装してあげれば、
それが画像に変換されて、OGP画像になるというものです。
しかも、このコンポーネントでもpropsでidを受け取れるので、opengraph-image.tsx
内でAPIなどを使ってデータ取得すれば、記事のタイトルや執筆者の情報もコンポーネントに埋め込んだ状態で画像化できるというものです。
ただ、opengraph-image.tsx
は一癖あって、実行される場所がエッジサーバー上という特徴があります。
そのため、prismaクライアントが使えなかったり、Font情報を普通の方法では取得できなかったりなど、細かいトラップがたくさんあったのですが、
なんとか実装できました。
(この動的OGP生成機能、もしオリジナルアプリで作りたい方いたら、ポータルサイトのリポジトリお見せします。)
最低限の機能で実装までしましたが、今後、追加したい機能が盛りだくさんです。
この辺り、実務課題として、皆さんにも体験していただきながら作って行けたら良いなと思っています。
また、生徒さんの書いた記事は、ぶべコードアカウントでもどんどんシェアさせていただき、
生徒さんと僕のフォロワーさんがつながるみたいな現象が起きれば、面白いかなとも思っています。