Next.js15 + DockerでいつもよくやるSSG開発環境構築

Next.js15 + DockerでいつもよくやるSSG開発環境構築 のサムネイル画像
投稿日: ・ タグ: ReactNext.jsDockerNode

Next.js15 + Dockerを使ってSSG開発環境を構築する方法のメモです。
実はこのブログも同じ方法で開発してます。
環境構築が終わるとディレクトリ構成は下記のようになります。

ssg-blog/
├── docker-compose.yml
└── next-app
    ├── .env
    ├── .gitignore
    ├── README.md
    ...

Nodeローカル環境構築

まずはビルドする前の表示確認用にdocker-composeを使ってNodeを実行できるローカル環境を構築します。

# docker-compose.yml
services:
  ssg-blog-app:
    image: node:22
    volumes:
      - ./next-app:/usr/src/app
    working_dir: /usr/src/app
    tty: true
    ports:
      - 80:3000

この程度の小規模なプロジェクトならdocker-compose使わずにDokcerfileとDockerコマンドのみで済ますこともできるでしょうが、
自分は普段docker-composeで様々なプロジェクトをローカルで管理していますので、ここではdocker-composeを使います。

Docker環境の構築方法は割愛しますが、ここでターミナルからdocker-composeの起動確認をしてみましょう。

# ターミナルから実行
$ docker compose up

下記のようにDockerの起動が確認できればOKです。

user@user:~/ssg-blog$ docker compose up
[+] Running 2/2
 ✔ Network ssg-blog_default           Created                                      0.1s 
 ✔ Container ssg-blog-ssg-blog-app-1  Creat...                                     0.1s 
Attaching to ssg-blog-app-1
ssg-blog-app-1  | Welcome to Node.js v22.15.1.
ssg-blog-app-1  | Type ".help" for more information.

composeのコマンドに「-d」オプションを付ければバックグラウンドで起動できますが、
今回は説明のためにオプションなしで紹介します。 次のセクションに移りますが、Dockerは起動したままにしておいてください。

Next.jsプロジェクト作成

では皆さんお待ちかねのNext.jsプロジェクトを作成していきます。
まずは前回のセクションでDockerで立ち上げたNode環境の中に入っていきます。
「docker compose up」を実行したターミナルとは別のターミナルから下記を実行してください。

# 別ターミナルから実行
$ docker compose exec ssg-blog-app bash

下記のような画面になります。

user@user:~/ssg-blog$ docker compose exec ssg-blog-app bash
root@××××:/usr/src/app# 

では下記コマンドでNode環境の中にNext.jsプロジェクトを作成してください。

root@××××:/usr/src/app# npx create-next-app@latest .

下記のような感じで聞かれると思いますが、好みで設定してください。 今回の場合は全てYesで設定しました。

Need to install the following packages:
create-next-app@15.3.3
Ok to proceed? (y) 

✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like your code inside a `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /usr/src/app.

インストールに成功すると初めに紹介したようなNext.jsプロジェクトのディレクトリ構成ができあがります。

ssg-blog/
├── docker-compose.yml
└── next-app
    ├── .env
    ├── .gitignore
    ├── README.md
    ...

次のセクションに進む前にここでDockerを一旦止めておいてください。

# 別ターミナルから実行
$ docker-compose stop

Next.jsの表示確認

ではNext.jsが正しく表示されるかの確認をしていきます。 docker-compose.ymlを下記のように書き換えてください。

# docker-compose.yml
services:
  ssg-blog-app:
    image: node:22
    volumes:
      - ./next-app:/usr/src/app
    working_dir: /usr/src/app
    # environment追加
    environment:
      - WATCHPACK_POLLING=true
    # command追加
    command: ["/bin/sh", "-c", "npm install && npm run dev"]
    # ttyは必要なくなったのでコメントアウト
    # tty: true
    ports:
      - 80:3000

下記コマンドを実行してDockerを起動してください。
しばらく待つとNext.jsのログが出てきます。

user@user:~/ssg-blog$ docker compose up
[+] Running 1/1
 ✔ Container ssg-blog-ssg-blog-app-1  Recre...                                     0.1s 
Attaching to ssg-blog-app-1
ssg-blog-app-1  | 
ssg-blog-app-1  | added 6 packages, and audited 347 packages in 5s
ssg-blog-app-1  | 
ssg-blog-app-1  | 140 packages are looking for funding
ssg-blog-app-1  |   run `npm fund` for details
ssg-blog-app-1  | 
ssg-blog-app-1  | found 0 vulnerabilities
ssg-blog-app-1  | npm notice
ssg-blog-app-1  | npm notice New major version of npm available! 10.9.2 -> 11.4.1
ssg-blog-app-1  | npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.4.1
ssg-blog-app-1  | npm notice To update run: npm install -g npm@11.4.1
ssg-blog-app-1  | npm notice
ssg-blog-app-1  | 
ssg-blog-app-1  | > app@0.1.0 dev
ssg-blog-app-1  | > next dev --turbopack
ssg-blog-app-1  | 
ssg-blog-app-1  |    ▲ Next.js 15.3.3 (Turbopack)
ssg-blog-app-1  |    - Local:        http://localhost:3000
ssg-blog-app-1  |    - Network:      http://172.25.0.2:3000
ssg-blog-app-1  | 
ssg-blog-app-1  |  ✓ Starting...
ssg-blog-app-1  | Attention: Next.js now collects completely anonymous telemetry regarding usage.
ssg-blog-app-1  | This information is used to shape Next.js' roadmap and prioritize features.
ssg-blog-app-1  | You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
ssg-blog-app-1  | https://nextjs.org/telemetry
ssg-blog-app-1  | 
ssg-blog-app-1  |  ✓ Ready in 909ms
ssg-blog-app-1  |  ○ Compiling / ...
ssg-blog-app-1  |  ✓ Compiled / in 1468ms
ssg-blog-app-1  |  GET / 200 in 1683ms
ssg-blog-app-1  |  ○ Compiling /_not-found/page ...
ssg-blog-app-1  |  ✓ Compiled /_not-found/page in 636ms
ssg-blog-app-1  |  GET /.well-known/appspecific/com.chrome.devtools.json 404 in 680ms

http://localhost/もしくはhttp://127.0.0.1/にアクセスし、下記のような画面が表示されていれば成功です。 「Next.jsの表示画面

Next.jsのSSG設定

Next.jsプロジェクトを作成するときにDockerのroot権限でプロジェクトを作成していますので現状next-app配下のファイルをホスト側で編集できないと思います。
ですので下記コマンドでDocker環境の中に入った後、

# ホスト側(Docker環境の外)
$ docker compose exec ssg-blog-app bash

下記コマンドを実行して全てのファイルに書き込みの権限を付与します。
本番環境で全てのファイルに「777」を付与するのはあまり推奨されないかもしれませんが、
今回の場合SSGで生成された静的なファイルが本番環境のホスティング対象になるケースが多いと思いますのでご愛嬌ということで。

# ゲスト側(Docker環境の内)
root@××××:/usr/src/app# chmod 777 -R .

ではSSGで静的に書き出すために、next.config.tsに「output: 'export'」を追記してください。
次のセクションに移りますが、Dockerは起動したままにしておいてください。

# next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
  output: 'export', // ←これを追加
};

export default nextConfig;

Next.jsでSSG実行

ではNext.jsで実際にSSGを実行していきます。
ではDocker環境内から「npm run build」コマンドでビルドを実行してみてください。
基本npm系のコマンドなどはDocker環境内から実行します。
下記のように表示されれば成功です。

# ゲスト側(Docker環境の内)
root@××××:/usr/src/app# npm run build

> app@0.1.0 build
> next build

   ▲ Next.js 15.3.3

   Creating an optimized production build ...
 ✓ Compiled successfully in 3.0s
 ✓ Linting and checking validity of types    
 ✓ Collecting page data    
 ✓ Generating static pages (5/5)
 ✓ Collecting build traces    
 ✓ Exporting (3/3)
 ✓ Finalizing page optimization 

Route (app)                                 Size  First Load JS    
┌ ○ /                                    5.63 kB         107 kB
└ ○ /_not-found                            977 B         102 kB
+ First Load JS shared by all             101 kB
  ├ chunks/4bd1b696-67ee12fb04071d3b.js  53.2 kB
  ├ chunks/684-1726a0fd22fca363.js         46 kB
  └ other shared chunks (total)          1.89 kB


○  (Static)  prerendered as static content

次のセクションに進む前にまたここでDockerを一旦止めておいてください。

# 別ターミナルから実行
$ docker-compose stop

Next.jsビルドプレビュー環境構築

Next.jsでSSG生成されたファイルをプレビューで確認したいので、ビルドプレビュー用にもう一つコンテナを作っておきます。 基本「npm run dev」でSSRモードで表示を確認できますが、たまに「npm run build」して生成されたファイルをホスティングして確認するとコンソールに見たことないエラーが出てるぞってこともあるので、、、

# docker-compose.yml
services:
  ssg-blog-app:
    image: node:22
    volumes:
      - ./next-app:/usr/src/app
    working_dir: /usr/src/app
    environment:
      - WATCHPACK_POLLING=true
    command: ["/bin/sh", "-c", "npm install && npm run dev"]
    ports:
      - 80:3000

  # 追加
  ssg-blog-app-build-preview:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./next-app/out:/usr/share/nginx/html:ro

下記を実行してDockerを起動してください。

$ docker compose up

http://localhost:8888/もしくはhttp://127.0.0.1:8888/にアクセスし、また下記のような画面が表示されていれば成功です。 「Next.jsの表示画面

まとめ

とりあえずここまでがSSG環境構築の方法でした。
現状のままですと「npm run build」コマンドを実行した後は開発サーバーにアクセスしてもエラーが表示される問題はありますが、
これは想定された挙動なので、「npm run build」コマンドを実行した後は再度Dockerを立ち上げ直すという運用方法で問題ないかと思います。
これを解決しようと思うと少し複雑なDocker構成(SSGとSSRを共存させる)になりますので今回は割愛させていただきます。
それではまた。