10分で作るNext.jsとDockerを使ったSSR+SPA環境

# はじめに
今更ながらNext.jsを触ったところ、とても便利でした。
手っ取り早く`docker-compose up`で動かせるところまでを記載します。

# 前提
以下の環境が整っているものとします。

* Node.jsのstableバージョンがインストールされている
* Dockerがインストールされている

# ワークスペースの作成

ディレクトリを作成します。
※yarnで書いてますがnpmでも問題ありません

```shell
$ mkdir next-docker //　名前は任意で
$ cd next-docker
$ yarn init --yes
``` 

今回は最小構成で必要なモジュールをインストールします。

```shell
$ yarn add next react react-dom -S
```

# `/pages`ディレクトリの作成・コンポーネントの編集
`next.js`は`/pages`に存在するファイルを見てルーティングを行います。
`/pages`の中に`Index.js`と`About.js`を作成します。

```shell
$ mkdir pages
$ cd pages
$ touch Index.js About.js
```

```text:Files
/pages
  - Index.js
  - About.js
...
```

`Index.js`と`About.js`は以下のように書きます。
`Index.js`では`getInitialProps()`を使用してサーバーサイドとクライアントサイドどちらでレンダリングされたのかを`isServer`プロパティでわかるようにしています。
`Link`コンポーネントは`next.js`が提供しているもので、SPAとしてブラウザの履歴を含めたページ遷移を実行してくれる大変便利なラッパーコンポーネントです。

```jsx:./pages/index.js
import Link from 'next/link';

const Index = (props) => (
    <div>
        <h1>Index page.</h1>
        <p>This page was rendered on the server side: {props.isServer.toString()}</p>
        <Link href="/about">
            <a>About</a>
        </Link>
    </div>
);

Index.getInitialProps = async function () {
    return {
        isServer: typeof window === 'undefined'
    }
};

export default Index;
```

```jsx:./pages/about.js
import Link from 'next/link';

export default () => (
    <div>
       <h1>About page.</h1>
       <Link href="/">
           <a>HOME</a>
       </Link>
    </div>
);
```
# 表示確認

`package.json`には`scripts`を書いておきます。

```json:package.json
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  },
...
```

実際に実行してみましょう。
（ターミナルはプロジェクトのルートに戻しておいてください

```shell
$ yarn run dev
```

ローカルサーバーが立ち上がりますので、`http://localhost:3000`にアクセスします。
無事にページが表示されていたら成功です。
ページ遷移もためしてエラーがなければ次に進みましょう。

# docker-compose
サンプルアプリケーションはできたため、実行環境を`docker`で作ります。
`ctrl + c`などして`next.js`で立てたサーバーは一度ストップしておいてください。

`docker-compose.yml`を作ります。

```shell
$ touch docker-compose.yml
```

作成したファイルを開いて以下のように編集します。
今回書き方は割愛しますが、`nginx`でwebサーバーを立てて`next.js`で立てたアプリケーションサーバーにリバースプロキシで接続します。

```yml:docker-compose.yml
version: '2'
services:
  nginx:
    image: nginx:stable
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - "./conf.d:/etc/nginx/conf.d"
    links:
      - express

  express:
    image: node:8.9.0-alpine
    container_name: express
    hostname: express
    volumes:
      - ".:/src"
    working_dir: /src
    command: [sh, -c, npm install && npm run build && npm run start]
    ports:
      - "3000:3000"
```

`nginx`の設定ファイルを作成します。

```shell
$ mkdir conf.d
$ cd conf.d
$ touch default.conf
```

`nginx`も最小構成で書きます。

```conf:default.conf
server {
    listen 80;
    server_name localhost;
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://express:3000/;
    }
}
```

# 表示確認

`docker-compose`でコンテナを起動します。

```shell
$ docker-compose up
```

少し待つとnext.jsのサーバーが起動したことが分かるかと思います。
`http:localhost/`にアクセスしましょう。`nginx`にアクセスするためポートは不要です。
同じようにページが表示されていれば成功です。

トップページのテキスト`This page was rendered on the server side:`の箇所に注目してください。ページ遷移したときと、ブラウザでリロードなどをしたときで`true`と`false`に切り替わっているのが分かるかと思います。
簡単ですね！！
isomorphicなアプリケーションが既にできています...！！

# おわりに
10分でできましたか...？
もし少しあふれてしまっていたら申し訳ありません。誇張したタイトルでした。
しかし、フルスクラッチするよりかなり簡単だったのではないでしょうか。
環境構築で消耗しないでコンテンツやサービスに注力したいですね。