個人ブログを作りました!!!

Next.js

2024-06-15

はじめに

始めまして!takuminesといいます!

今回、Next.jsを使って個人ブログを立ち上げたので、初記事としてブログを作ったモチベーションや、使用技術などについて書こうと思います!

モチベーション

普段はPHP/Laravelを使ってバックエンドの開発を行うことが多いのですが、最近Next.jsを使ったフロントエンドの開発をする機会が出てきたので、キャッチアップのついでにブログを作ってみようと思ったのが一番のきっかけです。

また、シンプルに個人ブログが欲しかったのもありますw

使用した技術

本ブログは主に下記の技術使って構築しました。

CMSとして今回はNotionを利用しています。

理由としては、普段Notionに学習したことをまとめており、記事もその流れで書くことができるので運用しやすいという点があります。

デプロイ先はなるべくコストがかからず、手軽にデプロイできるようにしたかったのでVercelにしました。

Nextjsのレンダリング方法はTime-based Revalidation(ISR) を採用しています。

実装内容の紹介

ブログを構築するにあたって、どのような実装をしたのかをいくつか紹介したいと思います。

より詳細な実装を知りたい場合は、下記のリポジトリを見てください!

https://github.com/takumines/takumines-blog

Notionから記事情報を取得

前提として、記事を格納するDatabaseは下記のプロパティを持つようにしています。

記事はNotion Databaseに格納し、@notionhq/client というNotion APIライブラリを使用して記事を取得します。

また、@notionhq/client はTypeScriptで書かれているため、型定義をインポートとして扱うことができます。

ただNotionの機能が柔軟すぎるが故に、型定義も柔軟な構造になっているので、Type Guard を行って扱いたい型を絞り込んでいく必要があります。

たとえば、Notion Databaseのプロパティは以下のような型定義がされています。

export type PageObjectResponse = {
    properties: Record<string, {
        type: "number";
        number: number | null;
        id: string;
    } | {
        type: "url";
        url: string | null;
        id: string;
    } | {
        type: "select";
        select: PartialSelectResponse | null;
        id: string;
    } | {
        type: "multi_select";
        multi_select: Array<PartialSelectResponse>;
        id: string;
    } |
    .
    .
  }
}

https://github.com/makenotion/notion-sdk-js/blob/main/src/api-endpoints.ts#L4516-L4691

propertiesフィールドはのユニオン型で定義されており、様々なプロパティタイプに対応できるようになっています。

なので、扱いたいプロパティの型をType Guard を使って絞り込まないと型推論が効かないので、以下のような関数を作って対応しました。

import { RichTextItemResponse } from "@notionhq/client/build/src/api-endpoints"

/**
 * PropertyがTitleかどうかを判定する
 *
 * @param property
 * @returns {property is {title: Array<RichTextItemResponse>}}
 */
export const isTitle = (
  property: any,
): property is { title: Array<RichTextItemResponse> } => {
  return property && property.type === "title"
}

Notionから取得した記事本文をMarkdownへ変換

Notionはページを様々なブロックを組み合わせて構成しているため、Notion APIで取得した記事本文データのJSONは記事を構成しているブロックの配列になります。

[
  {
    type: 'heading_2',
    blockId: '466fd391-18d2-42e2-b59e-07730114dfsfd',
    parent: '## はじめに',
    children: []
  },
  {
    type: 'paragraph',
    blockId: '51946525-d280-456c-b3b9-9499c44ewwfghf',
    parent: '始めまして!**takumines**といいます!',
    children: []
  },
  .
  .
]

このJSONからnotion-to-mdを使って、Markdownに変換します。

notion-to-mdにNotion APIのクライアントと、Markdown変換対象の記事IDを渡すだけで変換できるのでCMSにNotionを使う際は、必須ですね!

import { notionClient } from "@/app/_libs/notion/client"
import { NotionToMarkdown } from "notion-to-md"

export const n2m = async (id: string) => {
  const n2m = new NotionToMarkdown({ notionClient })
  const blocks = await n2m.pageToMarkdown(id)
  
  return n2m.toMarkdownString(blocks).parent
}

おわりに

ブログ構築を通して、Next.jsのキャッチアップが進みました。

やはり、作りたいものを決めて手を動かしながら学ぶのが一番効率が良いですね!!

せっかく作ったブログなので、最低でも月一記事は更新できるよう、自分用メモくらいのつもりで気軽に書いていこうと思います!