技術

Next.jsで実現するMDXブログ実装ガイド:生成AIとの協働開発

App Router時代のサーバーコンポーネント設計の形と実践的な解決策

2025-04-02
25分
Next.js
生成AI
MDX
App Router
サーバーコンポーネント
AI協働開発
Memory Bank
吉崎 亮介

吉崎 亮介

株式会社和談 代表取締役社長 / 株式会社キカガク創業者

Next.jsで実現するMDXブログ実装ガイド:生成AIとの協働開発

AI との協働開発の旅 前編から続く実装の道のり

前編の記事、プロンプトだけでは作れない:本格的なブログを AI と 10 日で構築した実践記録では、私が AI との協働でブログを構築するまでの戦略的アプローチについて紹介した。その中で、ビジョン設定から情報設計、デザイン実装に至るまでの過程を共有した。

前編が「なぜ」と「何を」に焦点を当てていたのに対し、この後編では「どのように」に注目する。AI と協働して技術的に複雑な実装課題をどう解決したか、その試行錯誤の過程を詳細に記録する。

図表を生成中...

この記事で取り上げることは Next.js v15 の App Router 環境におけるサーバー/クライアントの境界設計である。これは MDX 拡張機能(図表、数式、シンタックスハイライト)の実装において、最も苦労し、かつ最も多くの学びを得た部分でもある。

「MDX って Next.js でどう実装するの?」といった単純な疑問への答えは ChatGPT でも十分だろう。しかし、本記事の価値は複数の実装選択肢を試し、失敗を重ね、最終的に辿り着いた最適解とその判断基準にある。

技術選定とアプローチ:基礎からのスタート

フレームワーク選択というスタートライン

Web 開発の世界では、フレームワーク選択が全ての基盤となる。特に 2025 年現在、Next.jsは React ベースの Web アプリケーション開発において最もメジャーな選択肢の 1 つである。この選択は当たり前のように思えるかもしれないが、実は重要な決断点だ。

Next.js をブログシステム構築に選んだ理由は以下の通りである。

  1. ブログと将来的な Web アプリの両立:単なるブログだけでなく、将来的に高度な機能を持つ Web アプリケーションへと発展させる可能性を確保できる
  2. 静的生成と動的処理の両方に対応:SSG(Static Site Generation)と SSR(Server Side Rendering)の両方をサポートし、コンテンツ重視のプラットフォームに最適な選択肢となる
  3. AI との協働におけるメリット:Next.js は広範なドキュメントとコミュニティがあり、AI に質問した際の情報の質が高い。これは単なる「メジャー」という以上の価値を AI 駆動開発にもたらす
  4. MDX との親和性:マークダウンと JSX を組み合わせた MDX との統合が特に優れている

特に App Router が導入された Next.js 15 は、サーバーコンポーネントという新しいパラダイムをもたらした。これは高度な MDX 実装において重要な意味を持つことになる。

図表を生成中...
情報

Next.js v15における注意点: Next.js v15 では動的ルートパラメータが Promise ベースに変更された。具体的にはparams.slugなどの直接アクセスはawait paramsで解決する必要がある。この変更はルートハンドラの実装に大きな影響を与えるため、マイグレーション時に特に注意が必要である。

この問題は私が何度も遭遇したものであり、AI の学習カットオフ日の関係で自動的な解決策が提示されないことも多い。実際のエラーメッセージは以下のようなものだ。

Route used 'params.slug'. 'params' should be awaited before using its properties.

AI 協働開発の真実:「目 grep」と技術力の重要性

本プロジェクトは AI との協働開発プロセスを通じて構築された。しかし、その実態は世間一般で語られるような単純な「AI によるコード生成」とは大きく異なる。ここで重要な概念を紹介する。

「Vibe Coding」と「目 grep」の対比

Vibe Coding」とは、AI に頼り切ってコードを生成させるアプローチである。これは表面的には魅力的だが、多くの場合、長期的には持続不可能なアプローチである。対して、私が実践している通称「目 grep」は、AI が生成するコードを常に技術者が監視し、問題があれば修正するプロセスを指す。

図表を生成中...

目 grepの実践において、私は自分でも時間をかければ全て実装できるという大前提がある。他の開発者より速く作業できる方だが、AI と比較すれば雲泥の差である。しかし、重要なのは AI の出力を技術的に評価できる能力だ。コードを「目で追いながら」問題点を発見し、修正する。これこそが「目 grep」の本質である。

技術力と AI 成果物の質の相関

極めて重要な事実として、作り手の技術レベルが AI の成果物の質に直接影響することが明らかになった。具体的には:

  1. 適切な指示の形成:技術的理解があるからこそ、AI に対して的確な指示を出せる
  2. 出力の評価能力:生成されたコードの問題点や最適化の余地を見抜ける
  3. 文脈の提供:プロジェクト全体における位置づけを適切に伝えられる

このブログ構築も、単なる「AI にお任せ」ではなく、積み重ねの作業である。1 つ 1 つの機能を丁寧に実装し、問題を解決していくプロセスだ。プロンプト一発でできるほど甘くはない。

「急がば回れ」の開発哲学

短期的な効率を求めるなら「Vibe Coding」も魅力的に見えるかもしれない。しかし、長期的な成長と品質を考えるなら「急がば回れ」の精神で自身の技術力向上に投資すべきだ。

AI はあくまでツールであり、それを効果的に使いこなすためには、まず自分自身が実装できる能力を持つことが前提となる。AI に聞きながら進められるので、私の世代よりも効率的に知識を吸収できるだろう。しかし、「Vibe Coding」という甘い誘惑に負けてはならない。

短期的には辛くとも、自力での理解と実装能力を高めることが、長期的には最適な成長戦略である。これは私の信念であり、このブログ構築プロジェクトを通じて改めて確信したことだ。

MDX 実装における理想と現実のギャップ

App Router における理想的なアプローチ

Next.js App Router では、サーバーコンポーネントがデフォルトとなった。これにより、理論上はできる限りサーバーで処理し、必要な部分のみをクライアントで処理するという理想的なアプローチが可能になった。

このアプローチの利点は明白である。

  1. 初期ロード性能の向上:JavaScript 削減によるページ読み込みの高速化
  2. SEO 最適化:サーバーでの完全な HTML 生成
  3. サーバーリソースの有効活用:計算負荷のサーバーへの移行

特に MDX(Markdown with JSX)実装においては、マークダウンのパースやシンタックスハイライト、図表の定義解析などをサーバーで行い、インタラクティブな要素のみをクライアントに委ねる分業が理想と考えられる。

図表を生成中...

現実における実装の選択

しかし、理想と現実との間には大きなギャップが存在した。実際の実装では、以下のような技術的制約に直面した。

  1. React 19 との互換性問題:サーバーコンポーネントとクライアントコンポーネント間のハイドレーションエラー
  2. ライブラリの制約:多くの MDX 拡張ライブラリがクライアントサイド実行を前提としている
  3. 開発体験の複雑さ:サーバー/クライアント境界を適切に設計することの難しさ

特に深刻だったのは次のようなハイドレーションエラーである。

Error: Hydration failed because the initial UI does not match what was rendered on the server.

これらの課題に対処するため、理想的なアプローチからの妥協が必要となった。しかしこの妥協は単なる「諦め」ではなく、現実的な制約の中での最適解の探求である。

シンタックスハイライト実装:Shiki の事例

理想的なシナリオと設計

技術ブログの核心部分とも言えるシンタックスハイライト機能において、Shikiは最適な選択肢と考えられた。VSCode と同じハイライトエンジンを使用するこのライブラリは、高品質な構文強調表示を提供する。

理想的な実装では、以下のようなアプローチが想定された:

  1. ビルド時の静的生成:MDX のビルド処理中に Shiki でコードをハイライト
  2. サーバーコンポーネントでの処理:動的コンテンツの場合もサーバーでハイライト処理
  3. 純粋な HTML/CSS 出力:クライアント JavaScript を使わない実装

next.config.mjsには rehype-pretty-code プラグインを設定し、サーバーサイドでの処理を期待した。

// next.config.mjs
import rehypePrettyCode from 'rehype-pretty-code';
 
const prettyCodeOptions = {
  theme: 'github-dark',
  keepBackground: true,
};
 
// rehypePlugins配列に追加
rehypePlugins: [
  rehypeKatex,
  rehypeSlug,
  [rehypePrettyCode, prettyCodeOptions],
],

現実の実装とその理由

しかし実際には、components/mdx/CodeBlock.tsx'use client';ディレクティブを持つクライアントコンポーネントとして実装する必要があった。これには複数の理由がある。

  1. ハイドレーションエラーの回避:サーバーとクライアント間の出力不一致を防ぐ
  2. インタラクティブ機能の実装:コードコピーボタンなどの機能追加
  3. 動的なスタイル適用:テーマ切り替えなどの対応

結果として、シンタックスハイライト処理はビルド時の rehype プラグインとクライアントコンポーネントの組み合わせというハイブリッドアプローチとなった。

図表を生成中...

この実装により、高品質なシンタックスハイライトを維持しつつ、コードのコピー機能などのインタラクティブ要素も実現できた。しかし、完全なサーバーサイド実装という理想からは離れることになった。

得られた教訓

シンタックスハイライト実装から学んだ重要な教訓は以下のとおりである。

  1. 理想と現実のバランス:App Router 環境でも完全なサーバーコンポーネント化は常に最適解ではない
  2. 段階的な最適化:基本機能の安定した実装を優先し、その後パフォーマンス最適化へ
  3. 明確な責任分担:ビルドプロセス、サーバー処理、クライアント処理の役割を明確に区分する重要性

Mermaid.js 図表実装:複雑なクライアントサイド処理

構想したアプローチと理論

Mermaid.js 図表を実装する際、理想的には以下のような責任分担を構想した。

  1. サーバーコンポーネント:図表定義の解析と検証(静的な作業)
  2. クライアントコンポーネント:SVG レンダリングとインタラクティブ機能(動的な作業)

この構想に基づき、次のような実装パターンを検討した。

// サーバーコンポーネント(MermaidChart.tsx)
import dynamic from "next/dynamic";
 
// クライアントコンポーネントを動的インポート
const ClientMermaidRenderer = dynamic(() => import("./ClientMermaidRenderer"), {
  ssr: false,
});
 
export function MermaidChart({ children }) {
  const chartDefinition = children.trim();
 
  return (
    <div className="mermaid-container">
      <ClientMermaidRenderer chartDefinition={chartDefinition} />
    </div>
  );
}
// クライアントコンポーネント(ClientMermaidRenderer.tsx)
"use client";
 
import { useEffect, useRef } from "react";
import mermaid from "mermaid";
import { mermaidConfig } from "@/lib/mermaidConfig";
 
export default function ClientMermaidRenderer({ chartDefinition }) {
  // レンダリングロジック
}

実際の実装と直面した課題

しかし実際には、MermaidChart.tsx 自体が'use client';ディレクティブを持つクライアントコンポーネントとして実装された。この判断に至った主な理由は以下のとおりである。

  1. 「mermaid is not defined」エラー:サーバーサイドレンダリングとの不整合
  2. ハイドレーションの問題:サーバーとクライアントの出力差異によるエラー
  3. 図表タイプの検出ロジック:クライアントサイドでの柔軟な処理が必要

特に難しかったのは図表タイプの検出である。Mermaid.js は様々な図表タイプ(フローチャート、シーケンス図、ER 図など)をサポートしており、それぞれに最適な表示設定が異なる。この検出と最適化をサーバー側で行うことは技術的に複雑であった。

図表を生成中...

結局、図表定義の解析からレンダリングまで一貫してクライアントサイドで処理する実装を採用した。これにより、サーバー/クライアント間の同期問題を回避し、安定した図表表示を実現できた。

視覚的一貫性の確保

クライアントサイド実装に移行する中でも、視覚的一貫性の確保は重要な課題であった。これを解決するために、mermaidConfig.tsで詳細な設定を行った。

// lib/mermaidConfig.ts(一部抜粋)
export const mermaidConfig: MermaidConfig = {
  theme: "base",
  themeVariables: {
    // 共通設定
    primaryColor: "#3d8fa9", // 強調要素背景
    primaryTextColor: "#ffffff", // 強調要素文字
    // 各図表タイプ別の設定
    // ...
  },
};

この設定により、様々な図表タイプにおいても一貫したデザインを実現した。特に以下の点に注力した。

  1. Wadan ブランドカラーの統合:図表がブログデザインと調和するよう色設定を調整
  2. 図表タイプ別の最適化:各図表タイプの特性に合わせた視覚設定
  3. モバイル対応:小さな画面でも視認性を確保するレスポンシブデザイン

KaTeX 数式表示:スタイリングの最適化

想定した実装方針

数式表示においても、理想的には以下のような分業が想定された。

  1. サーバー側:数式の解析と検証
  2. クライアント側:レンダリングと表示調整

実装上の工夫と課題解決

実際の実装では、mdx-components.tsx での span 要素のスタイリング調整を中心としたアプローチを採用した。

// mdx-components.tsx(一部抜粋)
"span": ({ className, children, ...props }: MDXComponentProps) => {
  // KaTeX数式ブロック用の特別なスタイリング
  if (className?.includes('katex-display')) {
    return (
      <div className="math-container" style={{
        width: '100%',
        overflowX: 'auto',
        overflowY: 'hidden',
        padding: '0.5rem 0',
        textAlign: 'center',
        margin: '1.5rem 0'
      }}>
        <span className={className} style={{ display: 'inline-block', fontSize: '1em' }} {...props}>
          {children}
        </span>
      </div>
    );
  }
  // インライン数式の場合
  else if (className?.includes('katex')) {
    return <span className={className} style={{ fontSize: '1em' }} {...props}>{children}</span>;
  }
 
  // 通常のspan要素
  return <span className={className || ''} {...props}>{children}</span>;
}

この実装では、以下の課題に対処した。

  1. スクロール対応:長い数式がはみ出すのを防ぐための横スクロール実装
  2. フォントサイズ調整:数式と通常テキストの視覚的バランスの最適化
  3. モバイル対応:小さな画面でも可読性を確保する対応

これらの工夫により、複雑な数式表示を維持しつつも、ユーザーエクスペリエンスを向上させることができた。

実装判断の体系化:境界設計の指針

境界設計のための決定フレームワーク

MDX 拡張機能の実装経験から、サーバー/クライアント境界設計のための判断フレームワークを構築できた。この指針は他の Next.js App Router 開発にも応用可能である。

図表を生成中...

この判断フレームワークに基づいて、以下の原則を導いた。

  1. 設計原則 1:静的要素と動的要素の明確な区分
  2. 設計原則 2:開発効率とパフォーマンスのバランス考慮
  3. 設計原則 3:段階的な最適化アプローチ(まず動作を確保し、後に最適化)

App Router 環境での実装パターン

Next.js App Router 環境では、以下の実装パターンが効果的であることが分かった。

  1. 完全サーバーパターン:静的コンテンツやデータ取得などサーバーで完結できる機能
  2. クライアントパターン:ユーザーインタラクションが中心の機能
  3. ハイブリッドパターン:データ準備はサーバー、表示とインタラクションはクライアント

これらのパターンを状況に応じて適切に選択することが、効率的な開発と高いパフォーマンスの両立につながる。

AI 協働開発における実装判断

AI 協働開発においては、以下の点が重要であることが判明した。

  1. 複数アプローチの並行検討:AI の提案する複数の実装方法を比較検討
  2. 技術的評価の徹底:AI 生成コードの技術的妥当性を常に検証
  3. Memory Bank の活用:過去の判断や知見を体系的に蓄積・活用

これにより、AI の生成能力と人間の技術的判断力を最適に組み合わせた開発が可能になる。

今後の発展と最適化戦略

サーバーサイド処理の拡大可能性

現在の実装は技術的制約の中での最適解だが、将来的には以下の方向性での改善が期待できる。

  1. Next.js/React の進化:サーバーコンポーネントとクライアントコンポーネントの連携強化
  2. 部分的ハイドレーション:必要な部分のみをインタラクティブにする技術の発展
  3. ビルド時最適化:より多くの処理をビルド時に完了させる手法の開発

特に、React 19 の安定化と Next.js App Router のエコシステム成熟に伴い、現在クライアントサイドで行っている処理の一部をサーバーサイドに移行できる可能性がある。

ユーザー体験とパフォーマンスの最適化

最終的なユーザー体験の観点からは、以下の最適化が重要である。

  1. 初期表示の高速化:クリティカルレンダリングパスの最適化
  2. インタラクティブ性の向上:必要な場所での適切なクライアント処理
  3. アクセシビリティの確保:様々なデバイスや条件での利用可能性

これらのバランスを取りながら、継続的に実装を改善していくアプローチが有効である。

結論:技術力と AI 協働の融合がもたらす本当の価値

MDX 拡張機能の実装を通じて、技術的な教訓と AI 協働開発における深い洞察を得ることができた。これらは単なるブログ構築を超えた、現代のソフトウェア開発において普遍的な価値を持つものだ。

技術実装における核心的教訓

  1. 理想と現実のバランス:技術的な理想を追求しつつも現実的な制約を認識する重要性
  2. 段階的な実装アプローチ:基本機能の安定動作を確保した上で最適化を進める手法の有効性
  3. 責任分担の明確化:サーバー/クライアント間の責任境界を明確に定義することの価値

Next.js App Router の環境では、サーバーコンポーネントを「できるだけ使う」のではなく、「適切に使う」ことが重要だ。そして、その判断には技術的な理解と経験が不可欠となる。

AI 協働開発の本質

AI 時代の開発において最も価値があるのは、実は「AI への依存」ではなく「技術的自立」である。

AI は道具であり、その道具を使いこなす技術者の能力こそが決定的に重要だ。

私自身、このブログ構築において AI を活用したが、それは常に「私ならどう実装するか」という視点を持ちながらのことだった。ときには提案されたコードを根本から書き直し、ときには部分的に採用しながら、常に技術的判断を行使した。

「急がば回れ」の開発哲学

再度になるが大切なことなので何度でも伝えたい。

短期的には遠回りに見えるかもしれないが、自分自身の技術力を高めることこそが、長期的には最も効率的な道である。AI ツールは強力だが、それを効果的に使いこなせるのは、自力で実装できる技術的基盤を持つ開発者だけだ。

「Vibe Coding」の誘惑に負けず、地道に技術を磨くこと。これこそが、AI 時代においても揺るがない真理である。最終的には、AI と人間の能力が掛け合わさったときに、真の革新が生まれるのだ。

図表を生成中...

このブログ構築の旅を通じて、私は AI と協働する未来の開発のあり方を模索し続けている。それは決して AI に依存する道ではなく、AI を賢く活用しながらも、自らの技術力と判断力を磨き続ける道である。

「急がば回れ」。この古いことわざは、最先端の AI 時代においてこそ、その真価を発揮するのだ。

  1. 完全サーバーパターン:静的コンテンツやデータ取得などサーバーで完結できる機能
  2. クライアントパターン:ユーザーインタラクションが中心の機能
  3. ハイブリッドパターン:データ準備はサーバー、表示とインタラクションはクライアント

これらのパターンを状況に応じて適切に選択することが、効率的な開発と高いパフォーマンスの両立につながる。

AI 協働開発における実装判断

AI 協働開発においては、以下の点が重要であることが判明した。

  1. 複数アプローチの並行検討:AI の提案する複数の実装方法を比較検討
  2. 技術的評価の徹底:AI 生成コードの技術的妥当性を常に検証
  3. Memory Bank の活用:過去の判断や知見を体系的に蓄積・活用

これにより、AI の生成能力と人間の技術的判断力を最適に組み合わせた開発が可能になる。

今後の発展と最適化戦略

サーバーサイド処理の拡大可能性

現在の実装は技術的制約の中での最適解だが、将来的には以下の方向性での改善が期待できる。

  1. Next.js/React の進化:サーバーコンポーネントとクライアントコンポーネントの連携強化
  2. 部分的ハイドレーション:必要な部分のみをインタラクティブにする技術の発展
  3. ビルド時最適化:より多くの処理をビルド時に完了させる手法の開発

特に、React 19 の安定化と Next.js App Router のエコシステム成熟に伴い、現在クライアントサイドで行っている処理の一部をサーバーサイドに移行できる可能性がある。

ユーザー体験とパフォーマンスの最適化

最終的なユーザー体験の観点からは、以下の最適化が重要である。

  1. 初期表示の高速化:クリティカルレンダリングパスの最適化
  2. インタラクティブ性の向上:必要な場所での適切なクライアント処理
  3. アクセシビリティの確保:様々なデバイスや条件での利用可能性

これらのバランスを取りながら、継続的に実装を改善していくアプローチが有効である。

結論:技術力と AI 協働の融合がもたらす本当の価値

MDX 拡張機能の実装を通じて、技術的な教訓と AI 協働開発における深い洞察を得ることができた。これらは単なるブログ構築を超えた、現代のソフトウェア開発において普遍的な価値を持つものだ。

技術実装における核心的教訓

  1. 理想と現実のバランス:技術的な理想を追求しつつも現実的な制約を認識する重要性
  2. 段階的な実装アプローチ:基本機能の安定動作を確保した上で最適化を進める手法の有効性
  3. 責任分担の明確化:サーバー/クライアント間の責任境界を明確に定義することの価値

Next.js App Router の環境では、サーバーコンポーネントを「できるだけ使う」のではなく、「適切に使う」ことが重要だ。そして、その判断には技術的な理解と経験が不可欠となる。

AI 協働開発の本質

AI 時代の開発において最も価値があるのは、実は「AI への依存」ではなく「技術的自立」である。

AI は道具であり、その道具を使いこなす技術者の能力こそが決定的に重要だ。

私自身、このブログ構築において AI を活用したが、それは常に「私ならどう実装するか」という視点を持ちながらのことだった。ときには提案されたコードを根本から書き直し、ときには部分的に採用しながら、常に技術的判断を行使した。

「急がば回れ」の開発哲学

再度になるが大切なことなので何度でも伝えたい。

短期的には遠回りに見えるかもしれないが、自分自身の技術力を高めることこそが、長期的には最も効率的な道である。AI ツールは強力だが、それを効果的に使いこなせるのは、自力で実装できる技術的基盤を持つ開発者だけだ。

「Vibe Coding」の誘惑に負けず、地道に技術を磨くこと。これこそが、AI 時代においても揺るがない真理である。最終的には、AI と人間の能力が掛け合わさったときに、真の革新が生まれるのだ。

図表を生成中...

このブログ構築の旅を通じて、私は AI と協働する未来の開発のあり方を模索し続けている。それは決して AI に依存する道ではなく、AI を賢く活用しながらも、自らの技術力と判断力を磨き続ける道である。

「急がば回れ」。この古いことわざは、最先端の AI 時代においてこそ、その真価を発揮するのだ。

吉崎 亮介

吉崎 亮介

株式会社和談 代表取締役社長 / 株式会社キカガク創業者

「知の循環を拓き、自律的な価値創造を駆動する」をミッションに、組織コミュニケーションの構造的変革に取り組んでいます。AI技術と社会ネットワーク分析を活用し、組織内の暗黙知を解放して深い対話を生み出すことで、創造的価値が持続的に生まれる組織の実現を目指しています。

最新のインサイトを受け取る

定期的なニュースレターで、技術とビジネスの境界領域に関する最新の記事や独自のインサイトをお届けします。