Gatsby Starter Blogで記事のディレクトリ構成を整理しURLをシンプルにする方法

Gatsby Starter Blogでは、content/blogに記事を置くだけで静的ページを生成してくれる。 大変便利で素晴らしいのだけど、記事が増えていくとディレクトリ内にフラットに記事が並ぶようになり管理が面倒になるので、 それをどう解決するかを模索した。

現状

現状は以下のようになっている。

記事ディレクトリ

何の工夫もない「年月日」のディレクトリを作ってその中にindex.mdを置く方式。 元々が見切り発車気味にスタートしたブログなので工夫もヘッタクレもないのだけど、 この程度の記事数でも既に不満が募ってきてるので、これが50記事100記事となったときなんて想像に難くない。

不満ポイント

何がどう不満なのかを挙げてみると

  • 全ての記事が同じディレクトリに入ってるから見づらくて生きるのが辛い
  • 日付でディレクトリを作ると目的の記事を探しづらくてハゲる
  • 一旦記事を保存して別の日に書くとディレクトリ名も変更が必要で睡眠不足になる
  • そもそも名前が日付じゃない記事もあるから順番がおかしくて血圧が上がる

このように、健康で文化的な最低限度の生活に支障をきたす恐れがあるためこれら全てを解決したい。

ディレクトリ構造の変更

どう分けるか?

まず、ディレクトリ構造を見直していく。 何よりはまず「同じディレクトリに全記事が鎮座する」という状況を変えたいのでどのように変えるかを検討する。

一般的にブログの記事を分類する場合、年月やカテゴリで分類するとかが思いつくのだけど、 後者はカテゴリを変更したりするとまたメンテが必要になって維持が面倒臭い。 よって、特にここはひねらず年月で括ることにする。 年月で分類する場合は、以下のどちらかの分け方になると思う。

content/blog
- 2020
  - 11
  - 12
- 2021
  - 01
  - 02
content/blog
- 202011
- 202012
- 202101
- 202002

後者はcontent/blog以下のディレクトリが月毎に増えて結局同じことになりそうなので、 こちらも無難に年毎にする。

月毎のディレクトリは必要か

前述の案では年の下に月毎のディレクトリを作ると書いたけど、 現状では書いてない月もあったり、月に1記事だったりする場合もあるという状態。 そんなペースが続く場合は無駄に階層が増えるだけでかえって煩雑になる。

まぁ、そもそもそんな更新頻度の方がどうなんだという問題はあるのだけど、 よっぽどハイペースで書かない限りは月毎のディレクトリはいらないように感じた。 無理のない程度に運用したいので、月ディレクトリは作らず1年単位で年毎ディレクトリを増やすのみにする。 ついでに、パスを極力短くしたいので年ディレクトリも4桁→2桁にする。

content/blog
- 20
  - about
  - hello-world
  - ...
- 21
  - 20210517
  - 20210528
  - ...

記事ディレクトリ名の変更

とりあえず階層が決まったので、次に記事ディレクトリ名を変える。 最初に言ったように日付でディレクトリを作ると何の記事だか分からなくて管理しづらいので、 わかりやすいようにタイトルの英語表記にする。

それだけだとアルファベット順に並ぶので、ちゃんと記事の順番が維持されるようにしたい。 そうなると先頭に日にちをくっつけるのが無難なのだけど、 これも最初に書いた通り、一旦記事を保存して別の日に完成させたりすることが割とよくあるので日付だと面倒。 よって連番にする。

それぞれ反映して以下のようにする。

content/blog
- 20
  - 001__hello-world
  - 002__keep-writing-articles
  - ...
- 21
  - 001__gatsby-starter-blog-web-fonts
  - 002__resolve-web-font-jaggy
  - ...

英語力についてはALCと無二の親友になる程度なので、 命名センスについては大目に見て頂きたい。

とにかくこれで管理がしやすくなるはず。 連番にすることで「今年はこれで何記事書いたぜ」ってのが見えるのも何気に良い。 その年に多く記事を書くとまた見づらくなるかもしれないけど、そうなったらそうなったで考えることにする。 記事が多くて面倒って年ができるなら、それはそれで喜ばしいことだし。

URLを変更する

ディレクトリの変更が終わったら、階層通りに記事のURLを作ってくれる。

  • https://ryzca.com/20/001__hello-world

このままでも問題はないけど、せっかく記事のディレクトリ名を分かりやすくしたし、 タイトルのみのURLにしたいと思う。年とか連番は別にURLとしては不要だし、 記事を後から更新した場合は年の情報がノイズになる。

なので、上記のURLを以下のようにする。

  • https://ryzca.com/hello-world

記事のURLを変更する方法は以下を参考にした。

GatsbyJSで記事のURLをカスタマイズする

gatsby-node.jsの変更

ほとんど先のサイトを真似て以下のように変更。怒られませんように。 Gatsby Starter Blogベースだけど、MDXになっている箇所はこの記事の内容を前提としている。

gatsby-node.js
exports.onCreateNode = ({ node, actions, getNode }) => {
  const { createNodeField } = actions

+ const SLUG_SEPARATOR = `__`
+ const getSlug = path => {
+   const [prefix, slug] = path.split(SLUG_SEPARATOR)
+   return slug ? `/${slug}` : path
+ }

  if (node.internal.type === `Mdx`) {
    const value = createFilePath({ node, getNode })
+   const slug = getSlug(value)

    createNodeField({
      name: `slug`,
      node,
+     value: slug,
    })
  }

7行目の部分は少し変えた。

/20/001__hello-world/
本来は上記のようにslugが作られる処理を
hello-world/
こんな感じに変更するので、このままでは先頭の/まで取り除いてしまう。

これにより、記事下部にあるprev/nextリンクが正常に動作しなかったりする問題が起きるので、 ベースとなるGatsby Starter Blogの構造に準拠するようにした。

とにかく、これでディレクトリ名の__より後ろがURLとして生成される。

重複チェックをする

年と連番の情報を省いてURLをシンプルにすることができたが、 例えば以下のように同じ英字タイトルをつけた記事があった場合、 記事のURLが全く同一になってしまう。

content/blog
- 20
  - 001__hello-world  - 002__keep-writing-articles
  - ...
- 21
  - 001__gatsby-starter-blog-web-fonts
  - 002__hello-world  - ...

この場合何が起こるかというと、後に書いた記事で前の記事が上書きされる。 つまり、記事一覧から過去の記事を押したとしても開かれるのは同一URLの新しい記事の方になってしまう。 これは非常にマズイ。

そんなに頻繁に起きることではないとも思うけど、 せいぜい数個の英単語を繋げただけのディレクトリ名は、 日本語のタイトルよりも被る可能性が高いと言える。 更に、この問題が起きる可能性は記事の量に比例して増大していくことになる。

なので、タイトルの重複チェックを行いたい。 そこでgatsby-node.jsに以下のような処理を追加した。

gatsby-node.js
+ const log = require(`loglevel`);
// ...

exports.createPages = async ({ graphql, actions, reporter }) => {
  // ...

  if (posts.length > 0) {
    // ...

+   if (process.env.NODE_ENV !== `production`) {
+     const slugs = posts.map(item => item.fields.slug)
+     const uniqueSlugs = [...new Set(slugs)]
+     slugs.length !== uniqueSlugs.length ? log.error(`Some slugs are duplicated.\nSlug name must be unique.`) : null
+   }
  }
}

最初は記事ノードを作るときに毎回チェックを入れようとしたけど、 ビルド時のオーバーヘッドは極力小さくしたかったのでこんな感じになった。 全部の記事のslugの重複チェックをして、重複があればエラー文を表示するというだけのもの。 1回でも気づけばそれで良いので、gatsby developの時のみ実行。

もうちょっとスマートなやり方があるかもしれないけど、とりあえずこれで重複チェックは可能になった。 以下のように表示される。

重複エラー表示

英語力についてはGoogle翻訳と義兄弟の契りを交わす程度なので、 エラー文のセンスについては大目に見て頂きたい。


止まらない実行を目指すITエンジニア。

© 2020-2022, Ryz