[Windows対応]Next.js+Firebase Hostingでサーバーサイドレンダリング

## はじめに
この記事は、[HUITアドベントカレンダー2018](https://qiita.com/advent-calendar/2018/huit) 8日目の記事です。

## この記事について
Next.jsとFirebaseを用いて、サーバーサイドレンダリング(SSR)を行うWebサイトをデプロイするまでのチュートリアルです。

主に**React+Firebaseでの開発経験のある方**をターゲットとしています。
事前に[Next.js公式チュートリアル](https://nextjs.org/learn)のUsing Shared Componentあたりまで学んでおくと良いと思いますが、この記事で扱う範囲ではやらなくても問題ありません。
なにしろ**コードは一切書きません**。

なお、僕自身はReact歴4カ月程度の雑魚です。

## なぜNext.js?
Next.jsは、Reactでサーバーサイドレンダリング(SSR)を行うことのできるフレームワークです。Vue.jsでいうところのNuxt.jsに近い立ち位置だと思います。(名前もよく似ています)
SSRはReactのようなjsだけで動くページを、事前にサーバー側でhtmlとして生成しておく手法です。主に以下の恩恵が得られます。

- 初回読み込みの高速化
- 動的に生成されるページのOGP対策
- 動的に生成されるページのSEO対策

どれもReact単体では実現できませんので、NextはReactの書き方でこれらを実現したい方にオススメです。

## Next.jsプロジェクトを作成する
さっそくNextプロジェクトを作っていきます。ありがたいことに、Next公式で[Firebase Hostingと連携するサンプル](https://github.com/zeit/next.js/tree/master/examples/with-firebase-hosting)が用意されています。
これをクローンして使うのが最も手っ取り早いです。
### サンプルのクローン
以下のコマンドでnext.jsリポジトリを丸々クローンしましょう。

```
$ git clone https://github.com/zeit/next.js.git
```

その後、examplesフォルダ内の**with-firebase-hosting**フォルダのみを抜き出します。残りは不要なので削除してしまいましょう。
with-firebase-authentication、 with-firebase-hosting-and-typescriptといった似たような名前のサンプルがあるので注意してください。

### firebaseプロジェクトのセットアップ
クローンが完了したら、README内のSet up firebaseに従い、Firebaseプロジェクトを用意します。
firebase consoleにアクセスし、新しいプロジェクトを作成しましょう。
プロジェクトを作成した後は、クローンしたフォルダ内の.firebasercファイルに、以下のようにfirebaseプロジェクトidを書き込みます。

```javascript:.firebaserc
{
  "projects": {
    "default": "<firebaseプロジェクトid>"
  }
}
```

firebaseの設定ファイルが既に存在しているため、```firebase init```をする必要はありません。

ここまで終わったら、最後にwith-firebase-hostingフォルダ内で

```
$ npm i
```

します。

## デバッグしてみる
セットアップが終わったので、READMEに従って以下のコマンドでデバッグしてみます。

```
$ npm run dev
```

```Ready on http://localhost:3000```と言われるはずなので、アクセスします。
以下のように表示されれば成功です。

![dev.png](https://qiita-image-store.s3.amazonaws.com/0/274982/9464a726-81f5-2289-6e3e-2b7026af7443.png)

HomeページとAboutページを行ったり来たりできるはず。
問題ありませんね。

## デプロイしてみる
以下のコマンドを実行してみます。

```
$ npm run deploy
```

**以下のようなエラーが出てデプロイに失敗するはずです。**
デプロイ前のビルド段階でエラーが起きています。

```
ReferenceError: Unknown option: .caller. Check out http://babeljs.io/docs/usage/options/ for more information about options.
```

babel/coreがインストールされていないのが問題です。
また、デプロイを成功させるためにはfirebase-toolsを最新にしておく必要もあります。
以下のコマンドを実行して各種モジュールをインストールし直します。

```
$ npm un @babel/cli firebase-tools
$ npm i --save @babel/core 
$ npm i --save-dev @babel/core @babel/cli firebase-tools
```

完了したら、再度以下のコマンドを実行します。

```
$ npm run deploy
```

```Deploy complete!```と表示されたらOKです。
さっそくデプロイ先のURLにアクセスしてみましょう。
以下のように、デバッグ時と同じ画面が表示されれば成功です。

![deploy.png](https://qiita-image-store.s3.amazonaws.com/0/274982/8ca43cb4-8629-d892-d0be-8e0923fefcd3.png)

なおWindowsでデプロイした場合、**Internal Server Error**とだけ表示されるはずです。
この問題は次の項目で解決していきます。

Windowsユーザー以外の方はお疲れさまでした。

## (Windows環境)デプロイしたページでInternal Server Errorと表示される
Windows環境でない方は読み飛ばしてOKです。

さて、エラーの原因をたどってみると、firebase cloud functions側のログに```Cannot find module ...```と出力されているのがわかります。
このエラーはwindows環境でビルドした際に、パスの区切り文字が「/」ではなく「\」として出力されることが原因です。
windowsはデフォルトの区切り文字が「\」なので、これがビルド時に使用されるわけです。厄介ですね。
これは、ビルド後にすべてのファイルに含まれる「\」を「/」に置換することで解決できます。
まず、以下のコマンドでreplace-in-fileモジュールをインストールします。

```
$ npm i -g replace-in-file
```

その後、nextプロジェクト内のpackage.jsonにおいて、predeployを以下のように書き換えます。

``` package.json
<省略>
"scripts": {
　　<省略>
    "predeploy": "npm run build-public && npm run build-funcs && npm run build-app && npm run copy-deps && replace-in-file /\\\\\\\\/g / dist/functions/next/server/**/**.json --isRegex",
　　<省略>
  }
<省略>
```

再度デプロイすると成功するはずです。
お疲れさまでした。

## さいごに
ここから先は自分の好きなようにReactコンポーネントを実装していくだけです。
Nextの書き方に慣れていない方はまず[公式チュートリアル](https://nextjs.org/learn/)に取り掛かるとよいと思います。特にPagesでのルーティング方法、initialPropsの使い方はぜひ覚えておきたいですね。
ルーティングをきれいにしたいときはnext-routesモジュールをインストールするのもオススメです。
Firebase Hostingでのnext-routesの使い方や、OGP対応のSNSでurlプレビューする方法について、気が向いたら記事にするかもしれません。
それでは！

続きあります↓
[Next.js+Firebase HostingにMaterial-UIを導入する](https://qiita.com/sono8/items/6fcd9d30c9b7073ed4a0)

## 参考

- ReferenceErrorについて
    - [create-next-appのdeployで困ったエラー『ReferenceError: Unknown option: .configFile.』 | 煩悩八百万クリエイター戦記](https://blog.f-arts.work/archives/999)
- Windows環境でビルドしたときのエラーについて
    - [Paths when build on Windows and deploying on Linux · Issue #4920 · zeit/next.js](https://github.com/zeit/next.js/issues/4920)
- 公式サンプルを使わずにNext.js+Firebase Hostingを実現したい人向け
    - [Next.js on Cloud Functions for Firebase with Firebase Hosting](https://codeburst.io/next-js-on-cloud-functions-for-firebase-with-firebase-hosting-7911465298f2)