👌

WordPressブログをNext.js(App Router)に移行する

2023/10/18に公開

0.はじめに

Next.js 13.4 から、App Router が正式リリースとなりました。この新しいバージョンでは、これまでの Pages Router とは大幅に異なる変更が加えられており、より強力で柔軟なルーティングオプションを提供しています。

Wordpress をヘッドレス CMS(Headless CMS)として活用します。Wordpress はコンテンツ管理の強力なツールとして広く知られており、その柔軟性を活かしてコンテンツを管理し、Next.js アプリケーションと統合することができます。

App Router を使用して Wordpress のコンテンツを表示し、豊富なコンテンツとパフォーマンスを組み合わせた魅力的なウェブアプリケーションの開発を目指します。

この記事では、実際にアプリケーションを開発していきます。

1.開発環境の準備

nextjs のインストール

まずは、nextjs をインストールしてきます。

npx create-next-app@latest

すると、いくつか質問されるので、次の通り設定します。

✔ What is your project named? … sample-nextjs-wordpress
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … Yes
✔ What import alias would you like configured? … @/*

WordPress の準備

次に WordPress の準備をします。
今回は、dockerを用いて、WordPress を用意します。

docker インストールが済んでない人は、下記公式サイトよりダウンロードください。
https://docs.docker.com/

docker-compose.yml

まずは、docker-compose.ymlを作ります。

docker-compose.yml
version: "3.7"
services:
  db:
    image: mysql:5.7
    platform: linux/amd64 # M1Macを使う場合
    container_name: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password # rootユーザのパスワード
      MYSQL_DATABASE: db_local # WordPress用データベース名
      MYSQL_USER: wp_user # WordPress用データベース接続ユーザ名
      MYSQL_PASSWORD: password # WordPress用データベース接続パスワード
    volumes:
      - db_data:/var/lib/mysql

  WordPress:
    depends_on:
      - db
    image: wordpress:latest
    container_name: wordpress
    ports:
      - 8000:80
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306 # データベースサーバ名:ポート番号
      WORDPRESS_DB_USER: wp_user # WordPress用データベース接続ユーザ名(dbの内容に合わせる)
      WORDPRESS_DB_PASSWORD: password # WordPress用データベース接続パスワード(dbの内容に合わせる)
      WORDPRESS_DB_NAME: db_local # WordPress用データベース名(dbの内容に合わせる)
      WORDPRESS_DEBUG: 1 # デバッグモードON
    volumes:
      - ./wp-content:/var/www/html/wp-content/

  app:
    build:
      context: . # contextはdocker buildを実行する時のワーキングディレクトリ
      dockerfile: Dockerfile # dockerfileはDockerfileのパスを指定

    # ホスト側のディレクトリ./srcを、コンテナのディレクトリ/appにマウント
    volumes:
      - .:/app # 現在のディレクトリとコンテナ内の/app/ディレクトリが同期
      - node_modules:/app/node_modules

    # ホットリロード
    environment:
      - WATCHPACK_POLLING=true

    # ホスト側のポート:コンテナ側のポート
    ports:
      - "3000:3000"

    # コンテナを起動したままにするために設定
    tty: true

volumes:
  node_modules:
  db_data:

Dockerfile

次に Dockerfile を作っていきます。

Dockerfile
# ベースイメージを指定
FROM node:20.8.0

# 作業ディレクトリを設定
WORKDIR /app

# キャッシュ利用で効率化するために別でコピー
COPY ./package.json ./

# 依存関係をインストール
RUN npm install

# アプリケーションを起動
CMD ["npm","run","dev"]

docker を起動

docker-compose up -d

で環境構築・立ち上げを行なっていきます。


Docker の画面はこんな感じ
Dockerの画面


nextjs と wordpress がそれぞれ起動しているか、チェックしていきましょう!

nextjs:http://localhost:3000

nextjsの画面

WordPress:http://localhost:8000

wordpressの画面

これで環境設定は完了です。

2.WordPress に GraphQL を設定する

WordPress に GraphQL を設定していきます。
ここでは、WordPress のプラグインであるWP GraphQLを導入していきます。

https://www.wpgraphql.com/


まずは、WordPress を設定

WordPress(http://localhost:8000)から言語や必要情報を設定していきます。

必要情報設定画面

WordPress のパーマリンク設定

/graphql エンドポイントで GraphQL を公開するには、パーマリンクの変更が必要です。
設定 > パーマリンクより WordPress パーマリンク設定を開け、基本(http://localhost:8000/?p=123)以外を選択します。


次に、プラグイン > 新規追加 > WPGraphQL > 今すぐインストールと進みます。

プラグイン導入画面


有効化が終わったら、GraphiQL IDE をクリックします。


GraphiQL IDE ではクエリを簡単に作って試すことができます。

GraphiQL IDE


3.Next.js から呼び出す

まずは、実装

Dcocker を起動したままsrc/app/page.tsxに下記コードを貼り付けて下さい。

src/app/page.tsx
type Post = {
  node: { title: string; id: string; content: string };
};

export default async function Home() {
  const query = `query NewQuery {
    posts {
      edges {
        node {
          title
          id
          content
        }
      }
    }
  }`;

  const posts = await fetch("http://wordpress:80/graphql", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ query }),
    next: { revalidate: 10 },
  })
    .then((res) => {
      if (!res.ok) {
        throw new Error("HTTPエラー " + res.status);
      }
      return res.json();
    })
    .then((json) => {
      return json.data.posts.edges;
    })
    .catch((error) => {
      console.error("エラー: " + error.message);
    });

  return (
    <main>
      {posts.map((post: Post, i: number) => {
        return (
          <div key={i}>
            <div>{post.node.title}</div>
            <div>{post.node.id}</div>
            <div dangerouslySetInnerHTML={{ __html: post.node.content }}></div>
          </div>
        );
      })}
    </main>
  );
}


その後、nextjs:http://localhost:3000を開くと次のようなページが開くと思います。

表示画面

App Router Fetch vs Pages Router GetStaticProps

Pages Router を使用する場合、データの取得には getStaticProps メソッドを活用していました。
しかし、App Router を使用する場合、拡張された fetch 機能を使います。

Next.js は、ネイティブの Web API である fetch を拡張し、サーバー上での各フェッチリクエストのキャッシュと再検証動作を拡張しています。これにより、データの取得とキャッシュの管理を簡単に行えるようになっています。

React コンポーネントツリーのレンダリング中に自動的にフェッチリクエストをメモ化します。つまり、同じデータを複数回フェッチすることなく、必要なデータを一度取得すれば、そのデータは再利用されます。これにより、パフォーマンスが向上し、無駄なデータのフェッチが減ります。

App Router Fetch Revalidate vs ISR Revalidate

Incremental Static Regeneration(ISR)は、静的なページ生成を前提としながら、一定の間隔でページを再生成する仕組みです。この再生成の間隔は、getStaticProps の revalidate オプションを使用して設定できます。これにより、ページの最新のデータを保持しながら、定期的にページを更新できます。

App Router でも、ISR に相当する機能が存在し、以前よりも細かい制御が可能です。以前の ISR はページ単位での制御でしたが、App Router からは個々の fetch()リクエストごとに revalidate 期間を制御できます。具体的には、fetch()のオプションに revalidate を指定することで、個別のリクエストの再生成タイミングを設定できます。これにより、データの取得と再生成をさらに細かく制御できます。

Revalidate について

実際に体験してみましょう。

WordPress に記事を新しく投稿してみてください。

  1. 投稿した瞬間(revalidateで設定した秒数(10 秒)よりも早く)リロードした場合、画面が変わりません。
  2. revalidateで設定した秒数(10 秒)が立ったのちに、アクセスしても画面は変わりません。
  3. もう一度リロードすると、画面が切り替わります。

2 のタイミングで build が行われ、次のアクセスでその結果が表示されることがわかります。ISR と同じ仕組みですね。

おまけ fetch URL

fetch URL が http://wordpress:80/graphql になっています。

直接 Nextjs を立ち上げた場合(npm run dev)、fetch URL は、http://localhost:8000/graphqlになります。

docker で立ち上げている時に何か不具合が出て、原因がはっきり掴めないときは、まずは、

docker-compose logs app

にてログをチェックしてみましょう。先ほどの fetch URL 関係だと

  • http://localhost:8000/graphqlを使った場合

    TypeError: fetch failed
        at Object.fetch (node:internal/deps/undici/undici:13220:11)
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
      cause: Error: connect ECONNREFUSED 127.0.0.1:8000
          at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1595:16)
          at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
        errno: -111,
        code: 'ECONNREFUSED',
        syscall: 'connect',
        address: '127.0.0.1',
        port: 8000
      }
    }
    
  • http://wordpress:8000/graphqlを使った場合

    TypeError: fetch failed
        at Object.fetch (node:internal/deps/undici/undici:13220:11)
        at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
      cause: Error: connect ECONNREFUSED 172.18.0.4:8000
          at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1595:16)
          at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
        errno: -111,
        code: 'ECONNREFUSED',
        syscall: 'connect',
        address: '172.18.0.4',
        port: 8000
      }
    }
    

こんな感じでエラーを捕捉することができます。

もし記事があなたのお役に立ったなら、ぜひ「いいね!」ボタンをクリックしてくださいね。

Discussion