yamaの気ままに
yamaの気ままに

このポートフォリオ(yamaの気ままに)の技術周りの話

※ 画像・動画等のコンテンツが上手く生成されないことがあります。その際はお手数ですが、ページの更新を複数回お願いします。

目次
  1. はじめに
  2. 開発期間
  3. 使用した技術
  4. Next.js
  5. Vercel
  6. TailwindCSS
  7. MaterialUI
  8. Email.js
  9. NotionAPI
  10. こだわりポイント
  11. 記事詳細はSSGでで取得
  12. ブログの記事一覧をISRで取得し、ビルドを最適化する
  13. お問い合わせ機能
  14. 記事内のコンテンツテーブル
  15. 解決したい点
  16. liタグやolタグの子要素を描画できない
  17. ページネーション
  18. まとめ

はじめに

自身のポートフォリオとして簡単なホームページとブログを構築しました。 今回は制作にあたってのこだわりや苦労した点について紹介しようと思います。。。

開発期間

3週間(30時間ほど)

使用した技術

Next.js

ベースはNext.jsを採用。サーバーサイドレンダリングや静的サイト生成を簡単に実現でき、状況に応じて高速なページ表示やSEO対策が可能なため。また、ルーティングの設定が簡単でTypeSctiptにも対応しているため、非常に開発体験が良いと感じている。そして、なんと言ってもデプロイが非常に簡単である事。

Vercel

デプロイに使用。 pngを読み込んでくれないのが嫌い。

TailwindCSS

スタイリングで使用

MaterialUI

ナビゲーションバー、テキストフォーム、ページネーション

Email.js

お問い合わせ機能

NotionAPI

Notion上で作成したコンテンツを簡単に取得することができるAPI。Notionは直感的な操作性を持ち、テキストや画像などのコンテンツを自由に配置できるため、自分自身でブログを構築する上での柔軟性が向上すると判断し、採用しました。

NotionAPI使用背景

前提として、簡単なアウトプットをするためのブログ機能を実装したいと思いまいした。私の中で、個人サイト等のブログ運営(CMS)といえばmicroCMSだったのですが、最近台頭し始めた登場したNotionAIでブログのベース作成や雛形形成を行なってくれるということで、ブログ記事執筆ひよっこの私にとってNotionをどうにかして使いたいなと感じていました。そこで見つけたのがNotionAPIでした。

こだわりポイント

記事詳細はSSGでで取得

記事内容は更新が少なく、ビルド時にサーバー側で静的ファイルを生成しとくことで、記事詳細の表示スピードが爆上がりする。 最初からHTMLで書けばええやんて話ですが、中身のコンテンツはAPIで取得しているため、ビルド時にデータをフェッチしときたい。

ブログの記事一覧をISRで取得し、ビルドを最適化する

ISR…Incremental Static Regenerationのこと(段階的な静的サイト生成) SSGとは異なり、ISRは事前にすべてのページを生成せず、初回アクセス時にのみレスポンスが生成され、次回以降はその生成された内容がレスポンスされる。revalidateを指定することで使用できる。

SSGには以下のようなデメリットが挙げられる。

  • 静的なページを生成する際にページ数が多いとビルドに時間がかかる。
  • 1度しかビルドしないので、再度すべてのページをビルドし直さないと内容が更新されない。
  • このデメリットをISRが補っており、

  • アクセス時に初めて生成されるので初回ビルドが高速。
  • ISRでページ生成後も再度アクセスがあった際に次回以降の内容をビルドするので内容が更新される。
  • と言った利点がある。 今回の場合、記事が増えれば増えるほど、静的なページを生成する際にページ数が多いとビルドに時間がかかる。という欠点に当てはまのでってしまうため、採用した。 revalidate: 10で前回から10秒以内のアクセスを無視すると指定してる。実際にはキャッシュの再検証を必要とするまでの秒数である。キャッシュが作られた後、10秒間はそのキャッシュを返し続けるので、10秒以内に100回アクセスされてもキャッシュの再生成はされないという機能。

    お問い合わせ機能

    テキストフォームはMUI(TextField)で実装。propsを渡すことでシンプルなバリデーションを設定できることもあってスムーズに作れた。 メール送信ロジックはEmail.jsを使用。 メール本文のテンプレート作成して、POST通信するだけでメール送信できるため、バックエンドの工数が省けてラッキーと思い採用した。無料枠で制限があるが、個人の小規模レベルなら多分大丈夫だろう。。 き

    記事内のコンテンツテーブル

    これ。notionでは「目次」で作成でき、APIではtable_of_contents というtype名で中身のアイテムを取得できる。今回はあらかじめ記事コンテンツ内のh2タグを全て取得して、表示している。

    JavaScript

    import { Link as Scroll } from 'react-scroll'; case 'table_of_contents': return ( <details open className='my-4 rounded-md bg-slate-50 p-2 shadow-md hover:cursor-pointer focus:outline-none white:bg-stone-900 md:p-6' > <summary className='text-base font-semibold text-stone-800 focus:outline-none duotoneLight:text-stone-900 md:text-lg'> 目次 </summary> <ol className='p-2 md:p-4'> {headerBlocks.map((block: any, index: number) => { return ( <li key={index}> <Scroll to={block.id} smooth={true} className='inline-flex items-center py-1 text-base text-stone-700 duration-300 hover:text-stone-500 duotoneLight:text-stone-900 duotoneLight:hover:text-stone-300 md:text-lg' > <BsCheck2Square className='mr-2' /> {block[block.type].rich_text[0].text.content} </Scroll> </li> ); })} </ol> </details> );

    react-scrollライブラリを使うことで、指定したidの要素に画面をスクロールしてくれる。(これ地味にありがたいと思う)ただ、#xxxx のようなリンクにはなっていない。URLフラグメントを含んだリンクを踏んでも、Next.js側で簡単に実装できる記事を見つけたので検討中、、

    解決したい点

    liタグやolタグの子要素を描画できない

    ネストした箇条書きを書くことはよくあるとは思うのだが、NotionAPIのレスポンスの都合上、かなり遠回りなやり方でワンチャン実装できる感覚だった。

    JavaScript

    const renderNestedList = (block: any) => { const { type } = block; const value = block[type]; if (!value) return null; // ネストした子要素を取得 const isNumberedList = value.children.results[0].type === 'numbered_list_item'; if (isNumberedList) { return ( <ol> {value.children.results.map((block: any, index: number) => renderBlock(block, index) )} </ol> ); } return ( <ul> {value.children.results.map((block: any, index: number) => renderBlock(block, index) // ブロックタイプによって分岐し、jsx要素で表示する関数 )} </ul> ); }; case 'bulleted_list_item': case 'numbered_list_item': return ( <li> <Text text={value.rich_text} key={index} /> {!!value.children && renderNestedList(block)} </li> );

    実際、Notionのネストした箇条書きを見てみると、li,ulタグを使わずにdivタグでゴリ押ししていたので、気が向いたら実装してみることにする。 ちなみに現時点では箇条のネストの代用で、h3タグを利用している。

    ページネーション

    NotionAPIにはクエリを投げて、取得条件を絞れたり、取得件数を設定したり、並び替えができる。ページネーションというわけで、取得件数を設定するという選択肢を取ろうと思ったのだ、ページ切り替えごとにfetchが走るのが気に入らなかったのでやめた。

    代わりに、全件取得して、useStateでページ数の状態を持たせて実装した。これによって、ページ切り替え時に更新せずとも、早い切り替え表示が可能になった。 MUIのPaginationコンポーネントを使用。

    JavaScript

    import Pagination from '@mui/material/Pagination'; import Stack from '@mui/material/Stack'; import React, { FC, Dispatch, SetStateAction } from 'react'; const BasicPagination: FC<{ setPageProp: Dispatch<SetStateAction<number>>; currentPage: number; allPages: number; }> = ({ setPageProp, currentPage, allPages }) => { return ( <div className=' flex justify-center'> <Stack spacing={2}> <Pagination count={allPages} onChange={(e, page) => setPageProp(page)} page={currentPage} /> </Stack> </div> ); }; export default BasicPagination;

    まとめ

    Next.jsとNotionAPIを使用することで、自分自身で簡単にブログを構築することができる。DBも必要ないので、開発も楽に進んで行き、Notionの柔軟な操作性でブログ管理が非常に楽で、さらにNext.jsの高速な表示速度やSEO対策により、多くの人々にアクセスされるブログの構築も可能なのでぜひ、この方法を試してみてほしい。