このブログは WordPress を使わず、モダンなフロントエンド技術で一から構築しています。この記事では、システムの構成と各技術の役割を記録しておきます。
全体アーキテクチャ
EnBlogSystem/ # ブログシステム(モノレポ)
├── apps/
│ └── main-blog/ # メインブログ(Astro アプリ)
└── packages/
├── ui/ # 共通UIコンポーネント
├── seo/ # SEO コンポーネント
├── content-schema/ # Keystatic スキーマ定義
└── api-client/ # 商品DB クライアント
ProductDB/ # 商品DBサービス(別リポジトリ)
├── src/ # Hono API サーバー
└── prisma/ # DB スキーマ(PostgreSQL) pnpm workspace によるモノレポ構成です。将来ブログを複数に増やしたとき、packages/ 内のコンポーネントやスキーマをそのまま再利用できるよう設計しています。
商品管理・比較表機能は ProductDB として別サービスに切り出し、Fly.io にデプロイしています。ブログのビルド時に API を叩いてデータを取得する構成です。
主要技術スタック
Astro 5(フレームワーク)
サイト全体のフレームワークには Astro(v5)を採用しています。ビルド時にすべてのページを静的HTMLとして出力する SSG(静的サイト生成) モードで動作します。
ヒント
SSGのメリットはサーバーレスポンスが不要なため、表示速度が非常に速いことです。CDNからHTMLをそのまま返すだけなので、サーバー負荷もほぼゼロです。
Astroは「必要なところだけJavaScriptを使う」という設計思想で、デフォルトではJSを一切クライアントに送らないため、軽量なサイトを作りやすいのが特徴です。
Keystatic(ヘッドレスCMS)
記事の管理には Keystatic を使っています。コンテンツはすべてローカルのファイル(.mdoc 形式)として保存され、Git で管理されます。
開発環境(pnpm dev)では /keystatic にアクセスするとGUI管理画面が起動します。本番ビルド時は管理画面を除いた純粋な静的サイトとして出力されます。
// 本番時は Keystatic を読み込まない
const isProduction = process.env.NODE_ENV === 'production';
integrations: [
...(!isProduction ? [keystatic()] : []),
] 管理できるコンテンツ
- posts(ブログ記事)
- pages(固定ページ、プライバシーポリシーなど)
- lp(ランディングページ)
- authors(著者プロフィール)
- tags(タグ)
- siteSettings(サイト名・URL などのサイト設定)
Markdoc(記事フォーマット)
記事本文は Markdoc 形式(.mdoc)で書きます。Markdownの上位互換で、カスタムコンポーネントをタグとして記事中に埋め込めるのが特徴です。
{% callout type="warning" %}
注意事項をここに書く
{% /callout %} コードブロックのシンタックスハイライトには Shiki を使用しており、github-dark テーマでレンダリングされます。
Tailwind CSS v3(スタイリング)
スタイルは Tailwind CSS(v3)で管理しています。@tailwindcss/typography プラグインを使い、記事本文の prose スタイルも Tailwind ベースで調整しています。
ダークモードは darkMode: 'class' 設定で、<html> タグへの .dark クラスの付け外しで切り替えます。テーマの選択はユーザーが手動で行い、localStorage に保存されます。
情報
ページ読み込み時に一瞬ライトモードが光るFOUC(Flash of Unstyled Content)を防ぐため、<head> 内のインラインスクリプトで即座にテーマを適用しています。
Pagefind(全文検索)
サイト内検索には Pagefind(v1)を採用しています。pnpm build を実行するとビルド後のHTMLを自動的にインデックス化し、検索用のデータファイルを生成します。
astro build && pagefind --site dist サーバーサイドの処理が不要なため、静的サイトのまま高速な全文検索を実現できます。
SEO 設計
packages/seo パッケージに SEO 関連コンポーネントをまとめています。
-
メタタグ・OGP一括出力
title / description / canonical / robots / OGP(og:title, og:image など)をまとめて出力します。
-
構造化データ
BlogPosting / BreadcrumbList / WebSite の JSON-LD を出力します。Google のリッチリザルト対応に役立ちます。
-
パンくずリスト
ページ階層をナビゲーションとして表示しつつ、JSON-LD の BreadcrumbList も同時に出力します。
記事で使えるコンポーネント
Markdoc タグとして記事中に埋め込めるコンポーネント一覧です。
| コンポーネント | 用途 |
|---|---|
AffiliateBox | Amazon・楽天などの商品紹介ボックス |
CTASection | 行動喚起ボタン付きセクション |
Callout | info / tip / warning / danger の注意書き |
Timeline / TimelineItem | 時系列コンテンツ |
ChatContainer / ChatBubble | 会話形式の吹き出し |
CompareTable | 商品・プランの比較表 |
AdSlot | Google AdSense 広告スロット |
ExternalHtmlEmbed | 外部HTMLの埋め込み |
各コンポーネントの具体的な使い方は「コンポーネント一覧と使い方」の記事を参照してください。
商品DB(ProductDB)
アフィリエイト記事で使う商品情報を管理するため、ProductDB を別サービスとして構築しました。
| 技術 | 役割 |
|---|---|
| Hono 4.x | APIサーバー(Node.js) |
| Prisma 6.x | ORM |
| Neon | クラウド PostgreSQL |
| Fly.io | ホスティング |
Neon(クラウド PostgreSQL)
Neon はサーバーレス PostgreSQL サービスです。使っていないときは自動でスリープし、リクエスト時に起動します。無料枠の範囲内で本番運用できるのが採用理由です。
Fly.io(APIホスティング)
Fly.io は Docker コンテナをそのままデプロイできるホスティングサービスです。アクセスがないときはマシンを自動停止し、リクエスト時に起動する設定にしています。Vercel のビルド時にAPIを使用し比較表データを取得、静的HTMLとして生成します。
管理UI
Hono JSX で構築した管理画面があり、商品の登録・編集・スペック管理しています。
データ取得の設計
ページコンポーネントは直接 Keystatic を叩かず、data-access/ 層を経由してデータを取得します。
pages/ → data-access/ → Keystatic(記事コンテンツ)
→ ProductDB API(商品・比較表) 比較表(CompareTable コンポーネント)は、ビルド時に ProductDB の API から商品データを取得して静的 HTML を生成します。実行時のサーバー処理は不要です。
デプロイ
ブログ本体は Vercel に静的サイトとしてデプロイしています。main ブランチへのプッシュで自動デプロイが走ります。商品DB(ProductDB)は Fly.io に別途デプロイしています。
ローカルの開発フローは以下の通りです。
-
記事を書く
pnpm devで開発サーバーを起動し、/keystaticの管理画面から記事を作成・編集します。 -
Git にコミット
Keystatic が生成した
.mdocファイルを Git にコミットします。 -
GitHub にプッシュ
mainブランチにプッシュすると Vercel が自動的にビルド・デプロイします。 -
公開完了
ビルド時に Pagefind が検索インデックスも生成するため、デプロイと同時に検索も使える状態になります。
まとめ
WordPressって管理しやすいけど、表示速度とセキュリティが気になるな…
Astro + Keystatic に移行したら、表示速度もセキュリティも一気に解決した!コードでコンテンツ管理できるのもエンジニアには嬉しい。
記事の書き心地は?
GUIの管理画面もあるし、Markdocで直接書いてもいい。コンポーネントを埋め込めるので、WordPress のショートコードより柔軟性が高い。
WordPress と比べて構築コストはかかりましたが、表示速度・セキュリティ・カスタマイズ性のすべてが向上しました。このシステムが誰かの参考になれば幸いです。