Lab

Astro+Markdownで記事追加をコード変更から切り離す構成

Astro+Markdownで運営する『3分英語物語』では、記事追加をMarkdownファイルの追加だけで完結できる構成にしています。Content Collections、動的ルート、Cloudflare Pagesを使った継続運用の仕組みを整理します。

  • Astro
  • Markdown
  • Content Collections
  • Cloudflare Pages

3分英語物語は、AstroとMarkdownで作っている英語学習サイトです。

記事を継続して追加するサイトでは、追加のたびにページのコードまで触る構成だと運用の手間が増えていきます。そこでこのサイトでは、記事の内容とページ側の実装をできるだけ分離し、新しい記事はMarkdownファイルを追加すれば反映される形にしています。

この記事では、現在の構成を例に、記事型サイトを継続運用しやすくするためにどこを分けているかを整理します。

記事ごとにMarkdownファイルを分ける

物語記事はすべて src/content/stories/ に置いています。

各ファイルには本文だけでなく、タイトル、記事番号、公開日、レベル、タグ、説明文などもfrontmatterとして持たせています。3分英語物語ではさらに、日本語と英語の対訳、表現メモ、確認問題なども構造化したデータとしてMarkdown側に入れています。

記事ごとにページファイルを作る構成では、コンテンツが増えるほどページ側のファイルも増えていきます。現在の構成では、増えるのは基本的にMarkdownファイルだけです。

記事の見た目や表示ロジックは共通のページ側に置き、記事ごとの差分はコンテンツ側へ寄せています。

この分け方にしておくと、記事を書く作業とサイトの機能を直す作業を別々に扱えます。

Content Collectionsで記事の形を固定する

Markdownを自由形式のまま増やしていくと、記事ごとに項目名が違ったり、必要なデータを書き忘れたりしやすくなります。

3分英語物語ではAstroのContent Collectionsを使い、content.config.ts に記事データの形を定義しています。

現在は、たとえば次のような項目をスキーマとして管理しています。

  • title
  • number
  • publishedAt
  • level
  • learningTags
  • moodTags
  • description
  • sentences
  • phrases
  • comprehensionQuestions
  • todaySentence

レベルは beginner、intermediate、advanced のいずれかに限定しています。対訳部分も、日本語の ja と英語の en をセットで持つ形です。

Markdownは書きやすい反面、何でも書けてしまいます。そこでスキーマを挟み、記事を書く自由度よりも、サイト側が期待するデータ構造を毎回そろえることを優先しています。

項目が多いサイトほど、この境界がある方が後から扱いやすくなります。

詳細ページは動的ルートにまとめる

記事詳細は src/pages/stories/[slug].astro でまとめて処理しています。

Astroの getCollection() で全記事を取得し、getStaticPaths() から各記事のURLを生成します。記事番号で並べたうえで、前の記事・次の記事も同時に決めています。

つまり、新しい記事を追加するたびに、その記事専用の .astro ページを新しく作る必要はありません。

Markdownを追加すれば、次のビルド時にその記事もコレクションへ入り、既存の詳細ページの仕組みを使ってURLが生成されます。

記事が増えても、詳細ページ用のルーティングコードまで記事ごとに増えるわけではありません。

一覧・カテゴリ・レベルも同じ記事データを使う

記事データは詳細ページだけで使うわけではありません。

3分英語物語には、

  • 最新順の物語一覧
  • 古い順の物語一覧
  • カテゴリ別一覧
  • レベル別一覧

があります。

これらも同じContent Collectionから記事を取得しています。

たとえばレベルをMarkdownの level に書けば、その情報は記事ページの表示だけでなくレベル別一覧にも使えます。カテゴリ用のタグも同様です。

同じ情報を「記事ページ用」「一覧ページ用」と別々に持たないことで、片方だけ直し忘れる状態を避けやすくなります。

記事を追加する側から見ると、必要な情報をMarkdownにまとめて書けば、そのデータを各ページがそれぞれ利用する形です。

公開までの流れもできるだけ短くする

サイトはCloudflare Pagesで公開しています。

Production branchはGitHubの main で、main にpushすると自動でビルド・デプロイされます。

通常の記事追加では、Markdown記事を追加し、ビルドで問題がないか確認してGitHubへ反映します。

記事のたびにCloudflare側でページを作ったり、ルーティングを追加したりする必要はありません。

コンテンツを継続して増やすサイトでは、公開のたびに必要な操作が多いほど、その操作自体が運用コストになります。記事追加時に触る場所を少なくしておく方が、更新手順を一定に保ちやすくなります。

Markdownに寄せれば何でも楽になるわけではない

もちろん、この構成にも弱点はあります。

3分英語物語のMarkdownは、普通のブログ記事よりfrontmatterの項目がかなり多くなっています。対訳、表現メモ、確認問題などをすべて構造化しているため、記事ごとのデータ量は小さくありません。

また、記事ごとに大きく異なるレイアウトが必要なサイトでは、共通テンプレートへ寄せすぎるとかえって扱いづらくなります。

Markdown+共通テンプレートが向いているのは、同じ型の記事を継続して増やしていくサイトです。

逆に、各ページがほぼ別物になるポートフォリオやキャンペーンページなら、無理に同じ仕組みにまとめる必要はありません。

記事が増えても、追加手順は増やさない

新しい記事を追加するときの基本的な作業は、記事が増えても変わりません。Markdownを追加し、既存の仕組みに読み込ませます。

コンテンツサイトを作るときは、最初の記事を表示できることだけでなく、記事を継続して増やしても同じ手順で追加できるかを考えておくと、後から運用方法を作り直す必要が減ります。

同じ形式の記事を積み上げるサイトでは、「記事を増やすこと」と「サイトのコードを変更すること」を切り離しておくことで、記事追加のたびに触る範囲を小さく保てます。