第16回 Astroブログのタグ重複を仕組みで防ぐ―タグマスタ、alias、正規化処理を追加

一度修正したはずのタグ重複が記事追加後に再発。Frontmatterの個別修正ではなく、タグマスタ、alias、共通のnormalizeTag、旧URL維持、未登録タグ警告まで追加した内容を記録します。

執筆:ゆまラボ運営者

公開:更新:
第16回 Astroブログのタグ重複を仕組みで防ぐ―タグマスタ、alias、正規化処理を追加

第15回では、Aboutページ、パンくず、構造化データ、カテゴリページの重複ナビ、記事本文のH1重複を修正した。

同じタイミングでタグ一覧も確認していたが、こちらは別に記録することにした。

理由は、第12回で一度修正したはずのタグ重複が再発していたから。

修正前のタグ一覧では、

#cloudflare workers
#Cloudflare Workers

#Web制作
#web制作

#トラブル対応
#トラブル対応

のように、同じ意味のタグが別々に集計されていた。

表記揺れしたタグが分裂している修正前のタグ一覧

第12回ではタグ処理を一度見直している。それでも新しい記事を追加すると再発した。一度直したつもりだったので、ここはかなり気になった。

問題が出たMarkdownだけを直して終わらせず、タグをサイト側で一元管理する仕組みへ変更する

第12回の修正だけでは表記揺れを吸収しきれなかった#

まず、なぜ再発したのかを確認した。

既存のtaxonomy.tsにも正規化処理はあった。

行っていたのは主に次の処理。

Unicode NFKC
前後空白の除去
ASCII英字の小文字化
一部タグのalias変換

つまり、何もしていなかったわけではない。

ただし、aliasとして個別に登録されていたのは静的サイトなど一部だけで、タグ全体の正式名称と別表記をまとめて管理する仕組みにはなっていなかった。

このため、例えば、

cloudflare-workers
cloudflare workers

は別の値として残る。

どちらも英字を小文字化しても、

cloudflare-workers
cloudflare workers

のままなので一致しない。

Web制作web制作のような日本語とASCII英字の組み合わせも、表示名をどこで決めるかが統一されていないと、Frontmatterへ入力した値の影響を受ける。

第12回の修正は「今ある表記揺れをまとめる」には効いていたが、今後どの表記を正とするかを1か所で定義するところまではできていなかった

記事を追加すると再び分裂した原因はここ。

Frontmatterを全部直す方法は使わない#

一番簡単なのは、既存記事を検索してFrontmatterのタグを書き換える方法。

例えば、

tags:
  - cloudflare workers

を見つけて、

tags:
  - Cloudflare Workers

へ直していけば、現在の一覧はきれいになる。

ただし、この方法だと新しい記事で同じ表記揺れを入れればまた再発する。

記事A  Cloudflare Workers
記事B  cloudflare workers
記事C  cloudflare-workers

を毎回人間が意識して完全に揃える必要がある。

ゆまラボでは記事をMarkdownで増やしていくので、記事側へ厳密な表記統一を要求するより、サイト側で正式なタグへ変換する方が再発防止になる。

そこで今回はFrontmatterの一括修正をせず、入力値を受け取った後の処理を変更した。

tagMasterで正規ID・表示名・aliasをまとめて持つ#

taxonomy.tstagMasterを追加した。

考え方は、1つのタグに対して、

正規ID
表示名
別表記(alias)

を持たせること。

例えばCloudflare Workersなら、概念的には次のようになる。

'cloudflare-workers': {
  label: 'Cloudflare Workers',
  aliases: ['cloudflare workers']
}

記事側ではCloudflare Workersと書かれていても、cloudflare workersと書かれていても、最終的には同じcanonical IDへ寄せる。

主な対応をまとめる。

Cloudflare Workers / cloudflare workers
→ cloudflare-workers
→ 表示名 Cloudflare Workers

Web制作 / web制作
→ web制作
→ 表示名 Web制作

トラブル対応
→ troubleshooting
→ 表示名 トラブル対応

静的サイト
→ static-site
→ 表示名 静的サイト

一方で、

Cloudflare
Cloudflare Workers

は同じ会社・サービス群に関係していても意味が違うので統合していない。

aliasへまとめるのは、表記が似ているタグではなく意味が同じタグだけ。

normalizeTag()の処理順を1つに統一#

タグマスタを作るだけでは足りない。

タグ一覧ではマスタを使うが、記事カードでは別の処理、タグ詳細ではさらに別の処理、という状態だと再びずれる。

今回は入力されたタグをcanonical IDへ変換する順番も統一した。

処理は次の流れ。

Frontmatter入力

Unicode NFKC

不可視文字の除去

trim

連続空白の統一

ASCII英字の小文字化

alias解決

canonical ID

表示名・URL

Unicode NFKC#

見た目が似ていても、全角・半角など内部の文字コードが異なるケースを寄せる。

不可視文字の除去#

画面上は同じトラブル対応に見えるのに別タグになる場合、目に見えない文字が混入している可能性もある。

今回は正規化の段階で不可視文字も除去する。

空白の統一#

前後空白はtrimし、途中で連続している空白も同じ状態へ寄せる。

ASCII英字の小文字化#

Cloudflare Workerscloudflare workersのような大文字小文字の差を比較しやすくする。

ただし、最終的な表示名まで小文字にするわけではない。表示時にはタグマスタのlabelを使う。

alias解決#

最後に、正規化した入力がどのcanonical IDへ対応するかをタグマスタから判断する。

この順番をnormalizeTag()へまとめた。

タグを使う場所すべてで同じ処理を使う#

今回の修正では、タグ一覧だけを直していない。

少なくとも次の箇所が同じnormalizeTag()を使うようになった。

  • タグ一覧の集計
  • 記事カードのタグ表示
  • 記事詳細のタグ表示
  • タグリンク生成
  • タグ詳細ページ生成
  • タグごとの記事抽出

これで、一覧画面ではCloudflare Workersが7件なのに、リンク先を開くと6件しかない、といったずれを起こしにくくなる。

タグを表示するとき、URLを作るとき、記事を抽出するときの基準が同じcanonical IDになるため、処理の入口と出口を1つのルールで揃えられる。

修正後は重複タグが1つにまとまった#

修正後のタグ一覧はこちら。

タグマスタと正規化処理を適用した修正後のタグ一覧

実際に生成されたHTMLで確認した結果、対象タグは次の件数へ統合された。

横にスクロールできます
タグ統合後
Cloudflare Workers7件
Web制作4件
トラブル対応3件

修正前に存在していた、

#cloudflare workers
#Cloudflare Workers

の2つは、修正後には#Cloudflare Workers 7件の1つになっている。

#Web制作#web制作#Web制作 4件へ統合。

見た目が同じなのに1件と2件へ分裂していた#トラブル対応も、#トラブル対応 3件になった。

Frontmatterを全記事手作業で書き換えず、この結果になっている。

タグURLは正規URLを維持する#

タグの内部IDを統一すると、次に問題になるのが既存URL。

公開済みサイトなので、タグを整理するために正常なURLまで変えるのは避けたい。

現在の正規URLはそのまま維持した。

/tags/cloudflare-workers/
/tags/web制作/
/tags/troubleshooting/

一方で、以前の表記揺れによって生成されていたURLもある。

今回はtags/[key].astro側で、旧aliasを正規タグへ誘導するようにした。

例えばこうなる。

/tags/cloudflare workers/
→ /tags/cloudflare-workers/

/tags/トラブル対応/
→ /tags/troubleshooting/

/tags/静的サイト/
→ /tags/static-site/

旧ページ側にもnoindex, followと正規URLのcanonicalを設定している。

第13回で設定した「個別タグページはサイト内では使うが検索結果には出さない」という方針も維持している。

個別タグページは引き続きsitemap.xmlから除外される。

つまり今回の修正は、

タグ表示を統一

記事集計も統一

タグ詳細も統一

正常なURLは維持

旧URLは正規URLへ誘導

noindexも維持

まで含めている。

未登録タグはビルド中に警告する#

タグマスタ方式にすると、次は「新しいタグを追加したいときどうするか」という問題が出る。

未知のタグをすべてエラーにしてしまうと、記事を書くたびにビルドが止まりやすい。

ここはビルド失敗ではなく警告にしている。

新しい記事にタグマスタ未登録のタグがあった場合、

  • 対象記事
  • Frontmatterに入力されたタグ
  • 正規化後のID
  • タグマスタの追加先

をビルド中に表示する。

これなら新しいタグを追加すること自体は妨げず、意図せず表記揺れしたタグを増やした場合にも気付きやすい。

目的はタグの完全固定ではない。自由に記事を追加できる状態を残しつつ、表記揺れを発見できるようにすることにした。

CI/CDを旧URLにしようとして一度ビルドが失敗#

今回の実装中には別のエラーも発生した。

最初のビルドでは、表示名として使っているCI/CDまで旧URLのaliasとして自動生成しようとした。

CI/CDには/が含まれている。

URL生成ではこのスラッシュがパス区切りとして扱われるため、想定していないルートになりエラーになった。

ここで、

タグとして受け付ける別表記

と、

過去のURLとして残すalias

は同じものではないことが分かった。

最終的には、正規化で受け付ける表示名と、旧URLとして明示的に維持するaliasを分離した。

例えば入力値としてCI/CDを扱えても、その文字列をそのまま旧URL候補として自動展開しない。

タグマスタを入れたことで、表示名、正規ID、入力alias、URL aliasの役割を分けて考えられるようになった。

その修正後、最終ビルドは正常に完了した。

最終ビルドでタグ以外の機能も確認#

タグ処理はサイト内の複数箇所から使われるため、タグ一覧だけ見て完了にはしなかった。

最終確認では、

npm run build
node scripts/verify.mjs
node .astro/browser-current.mjs
git diff --check

を実行した。

結果は以下のようになった。

npm run build         成功
生成ページ             83ページ
Pagefind               19記事 / 2,353語
RSS                    生成成功
sitemap.xml            生成成功
個別タグnoindex        維持
git diff --check       成功

タグの正規化を変更したあとも、記事カード、タグリンク、タグ詳細ページ、検索、RSS、サイトマップが正常に生成されることを確認している。

Cloudflare Workersの構成や既存記事URLも変更していない。

タグマスタとaliasの変更点#

変更点をまとめる。

  • 第12回のタグ重複修正後に再発した原因を確認
  • Frontmatterの一括手修正は行わない方針に変更
  • taxonomy.tstagMasterを追加
  • 正規ID、表示名、aliasを1か所で管理
  • NFKC、不可視文字、空白、大文字小文字を共通正規化
  • normalizeTag()をタグ利用箇所で共通利用
  • Cloudflare Workersを7件へ統合
  • Web制作を4件へ統合
  • トラブル対応を3件へ統合
  • 正常な既存タグURLを維持
  • 旧表記URLを正規URLへ誘導
  • 旧URLにもnoindex, followとcanonicalを設定
  • 未登録タグをビルド中に警告
  • CI/CDのスラッシュで発生したURL生成エラーを修正
  • RSS、サイトマップ、検索、タグ詳細ページまで再確認

第12回では、目の前に出ていたタグ重複を直した。

記事を増やしたことで同じ問題が再発し、個別データより仕組み側に不足があることが分かった。

表記揺れを見つける

Frontmatterを直す

ではなく、

入力されたタグ

共通正規化

alias解決

canonical ID

表示名・URL

という流れをサイト側へ持たせたことで、新しい記事を追加したときも同じルールを使えるようになった。