Notion BlogをヘッドレスCMSとして、Next.jsで個人ブログを作ってみた

## はじめに

年末年始1週間の自由研究として、Next.jsを使って何か作ってみたい…ということで、NotionをヘッドレスCMSとした個人ブログを作ってみました。

せっかくなので制作日記として、
- 作ったものを紹介
- Notion Blogについて
- Next.js（React）を使って自分で実装した機能

などまとめておきたいと思います。

※ヘッドレスCMSとは…従来型のCMS（WordPress等）は入稿画面・DB・表示画面をまとめて管理するのに対し、ここから表示画面を除いたもの。ヘッドレスCMSでは、API経由で取得したコンテンツを基にフロントエンド部分は自前で実装する必要がある。

## 制作物について

実際に作ったものがこちらです。

https://hinako-blog.vercel.app/blog

### 構成

構成を簡単に説明すると、

- Notion上にブログ記事を保管するDBを作り、Notionで記事を執筆
- Next.jsのgetStaticPropsでNotionのAPIを叩いてブログコンテンツを取得
- Next.jsでレスポンスを成形してビューに表示

という感じです。

NotionのAPIを叩いてレスポンスを成形する部分は、Notion Blog（詳しくは後述）というOSSのテンプレートをベースに使わせていただきました。

デプロイはVercelで行いました。

### 実装した機能

- MITライセンスのOSSとして公開されているNotion Blog（Vercel社のJJ Kasperさん作）をベースに使用
- DBにcategoryのカラムを追加し、NotionのAPIからのレスポンスの成形やビューへの表示をカスタマイズ
- フロント側での「カテゴリーの選択」「投稿した月の選択」「フリーキーワードによる検索」によるリアルタイム検索機能
- 記事のカテゴリーに応じてReact iconsでアイコンを表示する
- CSS Modules
- UIパーツのコンポーネント化
- propsやstateの値により、条件付きレンダリングを行ったりCSSを動的に切り替える

## Notion Blogについて

ベースに使用したNotion Blogというのは、NotionのAPIを叩いてNotionからコンテンツを取得しNext.jsでビューを作ったテンプレートです。

GitHub：[https://github.com/ijjk/notion-blog](https://github.com/ijjk/notion-blog)
公式のサンプルページ：[https://notion-blog.vercel.app/](https://notion-blog.vercel.app/)
![スクリーンショット 2022-01-09 113324.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/1ce795a9-080d-2ce8-8ba1-717abb01e8f9.png)

こんな感じでシンプルで綺麗なUIをしています。

コードをいじったりしなくても、DeployボタンからVercelに連携し、NotionのDBのID・Tokenを環境変数に設定してVercelでデプロイするだけで、このテンプレート通りの個人ブログが出来るというものです。

### Notion Blogでブログコンテンツを取得する仕組み

あらかじめNotionで記事を登録するDB（Page, Slug, Published, Date, Authorsの5つの要素を持つインラインテーブル）を作り、このテーブルに記事を入れておきます。

![table-view.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/dda879e1-3bde-f1ac-fabc-9c308ef46f56.png)
画像引用：[https://github.com/ijjk/notion-blog#manually-creating-the-table](https://github.com/ijjk/notion-blog#manually-creating-the-table)

Notion Blogのソースを見ると、Next.jsのgetStaticPropsでNotionのAPIを叩いてデータベースのコンテンツを取得しています。
使っているのは非公式のAPIで「[https://www.notion.so/api/v3」](https://www.notion.so/api/v3%E3%80%8D) というエンドポイントを叩いているようです。

### Notion Blogを使った理由

Notion 公式のAPI（ベータ版）も2021年5月に公開され、OSSとして公開されているテンプレートで公式APIが使われているものもある中で、Notion Blogを選んだ理由は以下の通りです。

- 公式のNotion APIはコードブロックに対応していない（もしアップデートを見落としていたらすみません。）
- 公式のNotion APIでは、コンテンツを取得するだけでなくAPIを使ってNotionのDBへ追加・更新・削除する機能もあるが、これらの機能は不要と思った。
- Notion Blogでは、決められた5つのカラムを持つテーブルをNotionで作るだけでテンプレート通りに表示される。簡単！はやい！


ざっくりいうと、**とりあえず最速・簡単な方法で小さく作って、残った時間でカスタマイズしてみよう**という感じです。

### Notion Blogのセットアップ方法

セットアップ方法は、公式サンプルのページに動画付きで分かりやすく書かれているので省略します。

[https://notion-blog.vercel.app/blog/my-first-post](https://notion-blog.vercel.app/blog/my-first-post)

Deployボタンからコードを目にすることも無くデプロイすることも出来るようですが、今回はAPIから受け取るレスポンスの成形やUIはカスタマイズする前提だったので、git cloneして編集したうえでデプロイしました。

### Notion Blogを使ってみて

- テンプレートのコードを読んで

    APIへのリクエストを行うメソッドのファイル切り分け・ディレクトリ構成が分かりやすく、カスタマイズしやすかったです。
    Next.jsのダイナミックルーティングを使う際に、ヘッダのメタ情報を出しわける書き方も初めて見て勉強になりました。

<br>
ここからは、ブログ記事をブラウザで表示した時の見え方についてです。

- 画像・動画の挿入

簡単に挿入できるものの、画像のアスペクト比が若干おかしくなっていました…。
![スクリーンショット 2022-01-09 134459.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/8ae8f269-0dd7-73f0-0cd4-f0fb8fe57153.png)


動画はNotionで見たまま反映され、再生速度の調整まで出来るのでこれはスゴイ！感激でした。
![スクリーンショット 2022-01-09 134608.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/5e74162f-d88f-7e09-6593-11bddab2c7c7.png)

- コードブロックの挿入

公式APIでは対応していないコードブロックも、非公式APIを使っているNotion Blogでは表示できました。
文字色のCSSなんかも自由にカスタマイズできます。
![スクリーンショット 2022-01-09 135011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/e2eb8817-fc88-2f3b-6186-97920dbd2ff8.png)

ただしNotion Blogで使っているのは非公式APIなので、

- 動作が不安定（実際、稀にコンテンツが取得できないという事が起こりました。）
- 今後このAPIが使えなくなる可能性もある

というデメリットはあると思われます。

## 自分で実装した機能

自分で追加実装した機能や、その他使用技術について書いていきます。

### 検索機能

カテゴリーで絞り込み・投稿月で絞りこみ・フリーキーワード検索の機能を実装しました。

ページ上部
![スクリーンショット 2022-01-09 143437.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/5dd9d065-a46a-911a-5ecc-18d0c3e03f48.png)

フッター
![スクリーンショット 2022-01-09 143514.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/35c1f520-67ea-fdd3-2826-d6ecef8a5d22.png)

【実装手順】
①　APIから取得したブログコンテンツに存在するカテゴリーを配列に格納して、記事が存在する分のカテゴリーボタンを表示。投稿月についても同様。
②　ボタンを押したときや検索欄に入力をした際に発火する絞り込み処理を実装。

フロント側で保持している記事データに対して絞り込みを行い表示を切り替える機能なので、リロードなく瞬時に表示が切り替わります。
検索機能の書き方は非常に勉強になったので、別記事にまとめようと思います。

### React Icons

記事のカテゴリーに応じてアイコンを表示しました。
![スクリーンショット 2022-01-09 150043.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/2b278849-619f-fff4-9adb-23909ebd533f.png)

アイコンといえばFontAwesomeと思っていましたが、React用のライブラリがありました。

https://react-icons.github.io/react-icons/

このライブラリをインストールすると、Font Awesome や Material、Typicons、Codicons（VSCode のアイコン）など色んな種類のアイコンを簡単に利用することができます。

使い方は以下の記事を参考にさせて頂きました。

https://zenn.dev/taichifukumoto/articles/how-to-use-react-icons

### CSS Modules

CSSは、Notion BlogでCSS Modulesが使われていたので、そのままCSS Modulesを使いました。
**`.module.css`** で定義したクラス名がそのまま付与されるのではなく、実際はユニークなクラス名が自動的に生成されて付与されることも検証ツールで確認できて、勉強になりました。

Reactのプロジェクト下でのCSSといえば代表的な選択肢は、CSS Modules、styled-component、Tailwindあたりなのかと思いますが、それぞれの違いやメリットデメリットについては正直勉強不足です。。

https://kenzoblog.vercel.app/posts/css-manage

業務ではTailwindを使っており、今回初めてCSS Modulesを使い、両者の個人的な感想を書いておきます。

- Tailwind
    ファイルの行き来をせずスタイルを確認できて便利。小規模なプロダクトに向いていそう。
    
- CSS Modules
    コンポーネント単位でスタイルをまとめるという思想なので、コンポーネント指向のReactとの相性は良い（のだと思う）。
    スタイルの共通化がしやすくスッキリ整理整頓されている感じで、使いやすかった。




## 全体の感想
長くなってきたので細かい実装については書きませんが、「実装したい機能がある→どうやったら実現できるか調べる」というプロセスでReact・JavaScriptの書き方も色々勉強になりました。

今回NotionをヘッドレスCMSとして使ってみて、フロントエンドを自前で作るという事は見た目を自由に作れるだけでなく、SG・SSRを使って機能性の高いサイトを作ることが出来ることが大きなメリットだと感じました。


先輩に良いアドバイスもいただいたので、今度はまた他のヘッドレスCMSのサービスを使って個人ブログを作ってみたいなと思います。

## 参考記事

https://notion-blog.vercel.app/


https://zenn.dev/st43/articles/7982e6d371f8b8#notion-api%E3%81%A7%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%93%E3%81%A8%E4%B8%80%E8%A6%A7


https://zenn.dev/st43/articles/9a714916c50b80#%E3%81%BE%E3%81%A8%E3%82%81