【備忘録】Next.js開発環境をDockerで構築(WSL2)

# 備忘録です
WindowsでDockerを使ってNext.js開発環境を構築しようとすると、実はいくつかのトラップがあるのでその点も解決しつつ解説していこうと思います。

# 検証環境
Windows 22H2
VScode 1.78
Docker Desktop 4.19.0

# さっそくやりましょう
## コンテナ定義
VScodeで作業用のフォルダを開いて、Dockerfileを作成しましょう
```Dockerfile:Dockerfile
FROM node
```
え?こんだけ?となりますが、この辺はプロジェクトによって書き足すことが予想されるので最小限置いておくという感じです。続いてdocker-comope.ymlを作成します。
```yml:docker-compose.yml
version: '3.9'
volumes:
  node-modules:
services:
  next:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - node-modules:/app/node_modules
    tty: true
```
node_modulesを別の名前付きボリュームにすることで、コンパイルの時間をちょっとだけ短縮できます。作業ディレクトリに関しては後に使うDevcontainerがいい感じにしてくれるのでボリュームはこの一つで十分です。

現在のツリー構造は以下の通り。
```
.
|-- Dockerfile
`-- docker-compose.yml
```
## Devcontainerで開く
そうしたらVScodeのdevcontainer機能を使ってコンテナで開発していきましょう。
    `Ctrl+Shift+P`をおして、`reopen`と入力し、おそらく選択肢に出てくる`Dev Containers: Reopen in Container`を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034739/c3279253-fb4d-16b2-1b29-fb6b7f4b385a.png)
そして`'docker-compose.yml'から`を選択してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034739/9c23ab4e-b10a-9fdd-e0be-c7e4b6b4b22a.png)
機能の選択は何も選択せず`OK`(またはEnter)を押してください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034739/8bf0fb49-6775-84ce-9051-f53348d88541.png)
するとビルドが走ってコンテナ内で開発環境が開けるようになります。
`.devcontainer`フォルダが作成され、以下のようなツリーになっているはずです。
```
.
|-- .devcontainer
|   |-- devcontainer.json
|   `-- docker-compose.yml
|-- Dockerfile
`-- docker-compose.yml
```
`.devcontainer/docker-compose.yml`で作業ディレクトリのデータをボリュームマウントしてるみたいですね。それではアプリケーションを作っていきましょう。コンテナ内のターミナル(`Ctrl+@`)で`npx create-next-app`を実行して、指示に従ってプロジェクトを作成してください。ここでは`my-app`というプロジェクトを作成したとします。

## ホットリロード有効化
このままでは、Windowsファイルシステム上にプロジェクトが置かれているために変更を検知できず、ホットリロードが効きません。なのでここではWebpackのPollingを使います。

作成したプロジェクトの`Next.config.js`を編集します。私のプロジェクトでは`./my-app/Next.config.js`に存在しています。
```javascript:Next.config,js
/** @type {import('next').NextConfig} */
const nextConfig = {
    reactStrictMode: true,
    swcMinify: true,
    webpack: (config, context) => {
        config.watchOptions = {
            ignored: /node_modules/,
            poll: 1000,
            aggregateTimeout: 300
        }
        return config
    }
}

module.exports = nextConfig

```
ここでpollの値を変更すると、検知する間隔を設定できます。
`node_module`をignoreしないとCPUの負荷が上がって激遅になります。

# 動作確認
ではサーバーを立ち上げてみましょう。
```bash
root@337132d0c5ba:/workspaces/test-folder# cd my-app/
root@337132d0c5ba:/workspaces/test-folder/my-app# yarn dev
```
これでlocalhostの3000番でプロジェクトが立ち上がっているのが確認できます。ポートの転送も自動でしてくれるのがdevcontainerのいいところですね～

`my-app/app/page.tsx`なんかを編集してホットリロードが確認できたら完了です。お疲れ様でした。

# さらにいろいろ
パッケージ管理にyarnを使いましょう。一旦コンテナ内で`my-app`に入り、`yarn install`を実行しましょう。`yarn.lock`が生成されるので、不要になった`package-lock.json`を削除します。

Dockerfileを以下のように追記します
```dockerfile:Dockerfile
FROM node:latest

# 必要なパッケージのインストールとアップデート
RUN apt-get update \
    && apt-get install -y \
    git \
    # 追加のパッケージをここに列挙する
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*


# アプリケーションの依存関係をインストール
COPY /my-app/yarn.lock ./
RUN yarn install --frozen-lockfile
```

これによってある程度環境が整います。

.devcontainer/devcontainer.jsonに拡張機能の設定を追加します。こうすることで、開発環境を開いたときに自動的に拡張機能をインストールします。ここではCode Spell Checkerを追記してみます。
拡張機能を検索して、詳細画面に移動します。歯車のマークから`devcontainer.jsonに追加`を選択した後、拡張機能をインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034739/6ba2f8c8-38aa-f9bd-1695-36bc98510b3e.png)

すると以下のようにdevcontainer.jsonが変更されているのがわかります。便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034739/622ecff4-3be1-efd2-e0f9-37b102b08481.png)

# おわり
普段あまりフロントの開発をしないのですが、情報が散らばっていて毎回探すのが面倒なので記事として残しておきます。今度からはこの記事を見て自分で環境構築するぞ