Next.jsをSPAとして動作させる

基本的に Next.js は React のアプリケーションを SSR,SSG 等の仕組みを使ってデプロイを行う。 公式サイトにも Docker を利用したデプロイ方法が紹介されているが、 yarn start で Node.js のサーバを立ち上げている。

https://nextjs.org/docs/deployment

Static な HTML サイトの出力もサポートされていないわけではないが、Vercel にデプロイしないと使い物にならない。 例えば src/pages/todo/[id].tsx ファイルを作成した際には、 out/todo/[id].html ファイル等が生成される。 これを Nginx 等でホストするのは非常に難しい。 404 フォールバック等を行う仕組みもデフォルトで提供されておらず、デプロイ先が固定化されてしまう。

https://nextjs.org/docs/advanced-features/static-html-export

Static HTML export with Nginx + Next.js

ネット上の文献を探すといくつか方法が出てくるが、Next.js の内部 API を利用する方法・動的パスを扱えない方法などが紹介されていたので新しい方法を考えた。

まず Next.js の設定を記述し、パスの形式を変更する。 デフォルト挙動では src/pages/about/index.tsx ファイルを作成すると out/about.html ファイルが出力されるが、この設定により out/about/index.html が作成されるようになる。

// next.config.js

module.exports = {
  trailingSlash: true, // 追記
};

そして 404 ページに以下の内容を記載する。 Next.js の Router が認識するパスを元に 1 度リダイレクトを行うというコードを記述している。

// src/pages/404.tsx
import Error from "next/error";
import { useRouter } from "next/router";
import React, { useEffect, useRef } from "react";

export default function NotFoundPage(): React.ReactElement {
  const first = useRef<boolean>(true);
  const router = useRouter();
  useEffect(() => {
    if (first.current) {
      router.replace(router.asPath);
      first.current = false;
    }
  }, [router]);

  return <Error statusCode={404} />;
}

そして最後に適当に nginx の設定ファイルを記述する。 try_files の最後に先程記述した /404.html を配置している。

// nginx.conf

worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server_tokens off;
    server {
        listen       80;
        server_name  localhost;
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri $uri/ /404.html;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}

現状では大きな問題は発生していないように見える。