SNS

ポートフォリオのヘッドレスWordPressをやめて、記事をMarkdownで管理するようにした

このポートフォリオサイトは、Next.jsの静的エクスポート(SSG)で作り、記事はヘッドレスWordPressのREST APIからビルド時に取得していました。個人のサイトなのでWordPressはなくてもよいのでは、と考え、記事をリポジトリ内のMarkdownで管理する形に移行しました。

移行前の構成と課題

  • ビルド時にWP REST APIから記事・カテゴリ・タグ・画像情報を取得
  • GitHub Actionsで週1回ビルドし、Xserverにアップロード

困っていた点は次のとおりです。

  • 記事を書いても、次のビルドまで本番に反映されない
  • ビルドがWordPressの稼働状況に左右される(WAF対策のUser-Agentや認証情報の管理も必要)
  • 画像1枚ごとにメディアAPIを呼んでいて、ビルドが遅い

静的サイトでビルド時にしかデータを取得していないので、データの取得元をファイルに置き換えるだけで済みそうでした。

移行スクリプト

WP REST APIから全記事を取得し、本文のHTMLをMarkdownに変換して書き出す、一回限りのスクリプトを作りました。HTMLからMarkdownへの変換には turndown を使っています。

import TurndownService from "turndown";

const turndown = new TurndownService({
  headingStyle: "atx",
  codeBlockStyle: "fenced",
  bulletListMarker: "-",
});

// per_page=100 で、返ってくる件数が100未満になるまでページングする
const fetchAll = async (type) => {
  let all = [];
  for (let page = 1; ; page++) {
    const data = await getJson(`/${type}?per_page=100&page=${page}`);
    all = [...all, ...data];
    if (data.length < 100) return all;
  }
};

ポイントは次のとおりです。

  • URLを変えない:WPのスラッグ(blog-123)をそのままファイル名にし、URLは /blog/blog-123/ のまま
  • 画像もローカルに保存:本文中の <img> をダウンロードして public/images/blog/<ID>/ に置き、パスを書き換え
  • コードブロックの言語を引き継ぐ:<code class="language-php"> はturndownの codeBlockStyle: "fenced" で ```php になります。Markdownから戻すと同じクラスになるので、シンタックスハイライトの処理はそのまま使えました
  • frontmatterはJSON形式で書く:値を JSON.stringify で書き出すと、JSONはYAMLとしても有効なので、エスケープを気にしなくて済みます

読み込み側:zodでfrontmatterを検証

content/blog/*.md を gray-matter で読み、frontmatterを zod で検証してから、本文を marked でHTMLにしています。

const blogSchema = z.object({
  title: z.string(),
  date: dateSchema,
  category: z.string(),
  tags: z.array(z.string()).default([]),
});

const { data, content } = matter(fs.readFileSync(file, "utf8"));
const parsed = blogSchema.safeParse(data);
if (!parsed.success) {
  throw new Error(`${file} の frontmatter が不正です: ${parsed.error.message}`);
}

カテゴリとタグは別のJSONファイルで管理し、存在しないslugを書いたらビルドを止めるようにしました。WordPressの管理画面のような入力補助は無くなりますが、書き間違いはビルド時に気づけます。

カテゴリ・タグ別ページは廃止

WP時代は /blog/category/7/ のようにタクソノミーのIDでページを作っていましたが、記事数も多くないので、ブログ一覧ページの中で絞り込む形にしました。記事詳細のカテゴリをクリックしたときは、URLを変えずに絞り込んだ状態で一覧を表示しています。

デプロイ

  • 週1回の定期ビルドをやめ、mainにpushしたらGitHub Actionsでデプロイ
  • WP REST API用のSecretsや認証情報が不要に

移行してみて

  • 記事を書いてpushすればすぐ反映される
  • 記事がGitで履歴管理される
  • ビルドが外部に依存しないので速く、失敗しにくい

一方で、スマホから記事を書くのは難しくなりました。管理画面から書きたくなったら、Markdownを編集できるGitベースのCMS(Keystatic、Decap CMSなど)を足すことも検討しています。

CONTACT

サイト訪問ありがとうございます。
お仕事のご依頼、ご相談がありましたらお気軽にご連絡ください。
*は必須項目となります。

お名前 *
メールアドレス *
会社・組織名
お問い合わせ内容 *
0/300

当サイトはreCAPTCHAによって保護されており、Google のプライバシーポリシーおよび利用規約が適用されます。
当サイトのプライバシーポリシーをご一読の上ご連絡ください。