ポートフォリオブログの管理・表示の仕組み
ポートフォリオブログの管理・表示の仕組み
目次
- はじめに
- 全体構成
- 記事の管理方法
- 記事の表示方法
- まとめ
1. はじめに
このサイトのブログ記事は、WordPressのようなCMSを使わず、MarkdownファイルをCloudflare R2に置いてNext.jsで取得・表示する構成にしています。
シンプルな構成ですが、記事の作成から公開まで一連の流れがあるので、その仕組みをまとめておきます。
2. 全体構成
[ローカル: /work/posts]
├── markdown/ # 記事本体(Markdownファイル)
├── posts-list.json # 記事メタデータ一覧
├── createMD.sh # 新規記事作成スクリプト
└── upload.sh # R2へのアップロードスクリプト
↓
[Cloudflare R2]
posts.shoat-portfolio.com/posts/
├── posts-list.json
└── markdown/{id}.md
↓
[Vercel: Next.js]
fetch で取得 → Markdownをレンダリング → ブログ表示
- 記事の保管: Cloudflare R2
- フロントエンド: Vercel(Next.js)
- CDN: Cloudflare
3. 記事の管理方法
新規記事の作成
createMD.sh を実行すると、今日の日付で自動的にMarkdownファイルが生成されます。
bash createMD.sh
ファイル名は YYYYMMDD-NNN.md(例: 20260409-001.md)の形式で、同日に複数作成した場合は連番が振られます。
生成されるファイルには以下のフロントマターが含まれます。
---
title: 新しい記事タイトルをここに記述
date: 2026-04-09
update: 2026-04-09
description:
---
Markdownファイルの構成
記事はフロントマター(YAML形式のメタデータ)+本文の構成です。
| フィールド | 内容 |
|---|---|
title | 記事タイトル |
date | 投稿日 |
update | 最終更新日(upload.sh が自動更新) |
description | 記事の説明文(OGPや一覧ページに使用) |
posts-list.json
記事一覧ページ用のメタデータをまとめたJSONファイルです。upload.sh がMarkdownのフロントマターを読み取り自動生成します。
[
{
"id": "20260409-001",
"title": "ポートフォリオブログの管理・表示の仕組み",
"date": "2026-04-09",
"update": "2026-04-09",
"description": "...",
"published": true
}
]
published: false にすると一覧に表示されなくなるため、下書き管理に使えます。デフォルトは true です。
Cloudflare R2 へのアップロード
記事を書き終えたら upload.sh を実行します。
bash upload.sh
処理の流れは以下の通りです。
① Markdownの update 日付をファイルの最終更新日で自動更新
② フロントマターから posts-list.json を生成
③ dry run で差分を確認
④ 確認後、Cloudflare R2 へ同期(aws s3 sync)
③のdry runで変更内容を確認してから実行できるので、誤ってファイルを削除するリスクを減らせます。
4. 記事の表示方法
データの取得
Next.jsサイド(app/lib/blog/blog.ts)でR2からデータを取得しています。
記事一覧の取得:
// posts-list.json を fetch して一覧を返す
// published: false の記事は除外、日付降順でソート
const getSortedPostsData = async () => { ... }
記事本文の取得:
// markdown/{id}.md を fetch してフロントマター+本文を返す
const getPostData = async (id: string) => { ... }
どちらも ISR(Incremental Static Regeneration) を使用しており、60秒ごとにキャッシュを再検証します。
fetch(url, { next: { revalidate: 60 } })
Markdownのレンダリング
記事詳細ページ(app/blog/[id]/page.tsx)では react-markdown でMarkdownをHTMLに変換しています。
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{content}
</ReactMarkdown>
remark-gfm を使うことでテーブル・チェックボックス・取り消し線などGitHub Flavored Markdownの記法が使えます。
シンタックスハイライトは highlight.js で対応しています。
ページ構成
| URL | 役割 |
|---|---|
/blog | 記事一覧(app/blog/page.tsx) |
/blog/{id} | 記事詳細(app/blog/[id]/page.tsx) |
記事詳細ページはタイトル・descriptionを使って動的にOGP/メタタグを生成するため、SNSシェア時にも各記事の情報が正しく表示されます。
5. まとめ
このブログの管理・表示の流れをまとめると以下の通りです。
createMD.shで新しいMarkdownファイルを生成- Markdownを編集して記事を執筆
upload.shでCloudflare R2に同期- Next.js(Vercel)がR2からデータをfetchして表示
CMSを使わないシンプルな構成ですが、スクリプトで自動化できる部分はまとめているので、記事の公開作業は比較的楽になっています。