React+Expressで本番環境へデプロイ

#はじめに

React＋ExpressでWEBサービスを作るにあたって、
本番環境へのデプロイ方法がわからなかったので調べてみました。

###対象読者
Reactを導入してみたいけど、Expressでデプロイする場合どうすりゃいいんだ、と悩んでいる方。



#方法

ずばり、
**ReactプロジェクトでビルドしたファイルをExpressプロジェクトのpublicにぶち込む、です。**

**手順**
①Reactプロジェクトで本番用ファイル群を生成
②生成された/build以下のファイルをExpressプロジェクトの/public以下にデプロイ
③Expressのルーター操作をrenderによる描画からsendfileに変更（index.htmlをクライアントに返すだけにする）


詳細は以下で説明します。

##①Reactプロジェクトで本番用ファイル群を生成

まずはプロジェクト作成

```
create-react-app client
```

作成したプロジェクトに移動してビルド

```
cd client
npm run build
```

ビルドするとbuild以下に本番用ファイル群が生成される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633470/990a9eb5-7253-85a6-26f4-e766b66f8dc4.png)


##②生成された/build以下のファイルをExpressの/public以下にデプロイ
①で生成したファイル群をExpressの公開フォルダ(public）に配置します。

まずはExpressのプロジェクト作成。
（Expressの導入などはhttps://qiita.com/etet-etet/items/1c65b934dbe7fc33490b）

```
express server
```

プロジェクトが作成されると以下のファイルが生成される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633470/66f1cf18-4a1c-b5ae-0736-fa54f45e430f.png)

これのpubilc配下に①で生成したファイルをぶち込む。（元々あったimages, javascripts, stylesheetsフォルダは不要なので削除してよい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/633470/39d718af-f096-e52a-8442-bc377d88c272.png)

##③Expressのルーター操作をrenderによる描画からsendfileに変更

Expressのルーターは、アクセスされたアドレスに基づいて予め定めた動作をします。
例えば、デフォルトだとルート（/）にアクセスされたら
viewsのindex.jadeでhtmlをレンダリングし、クライアントに返します。

```javascript:routes/index.js（オリジナル）
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
```

これを、
レンダリングは行わずにpubilcフォルダにあるindex.htmlを返すようにしてあげます。

```javascript:routes/index.js（改）
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  // res.render('index', { title: 'Express' }); //renderによる描画は不要
  res.sendfile('./public/index.html');  //クライアントにindex.htmlを返す
});

module.exports = router;
```

以上

参考
https://stackoverflow.com/questions/17911228/how-do-i-use-html-as-the-view-engine-in-express


#別の方法（同じプロジェクト内でReactとExpressを連携する方法）
今回ご紹介した方法は、React側でビルドしたファイルをExpressにデプロイする方法でしたが、
ReactとExpressを同じプロジェクト内で連携する方法もあるようです。
[Node.js, Express, sequelize, React で始めるモダン WEB アプリケーション入門（Express/sequelize編）](https://qiita.com/tatsurou313/items/2ba0387806b07f442b8c)

また、上記の記事で言及されてますが、express-react-viewsを使ってExpressでサーバーサイドレンダリングする方法もあるようです。ただし、クライアントサイドでの React レンダリングやイベント処理が出来ない仕様とのこと。
>views ディレクトリ配下に React コンポーネントを使った View を用意できるようになるが、SSR(Server Side Rendering) により static な HTML が出力される。そのため、クライアントサイドでレンダリングしたい場合には利用できない。express-react-view の公式ドキュメントにも下記のとおり、その旨が記述されている。

>>This is an Express view engine which renders React components on server. It renders static markup and does not support mounting those views on the client.


#本番環境はいいけど開発環境はどうすべき？
基本的にはそれぞれ別のプロジェクトで開発し、デプロイするときだけ上記の手順でファイルをExpress側に持ってきてあげるのが良さそうです。

クライアントからAPIを呼ぶ必要がある場合の開発環境については、下記のページが参考になります。
[React開発時には、APIサーバーとReactアプリサーバーを別にして、プロキシを使うというベスト・プラクティス](https://applingo.tokyo/article/1568)

#さいごに

今回はReactとExpressを別々のプロジェクトで用意し、
React側でビルドしたファイルをExpressにデプロイする方法をご紹介しました。

一方で、ReactとExpressを同じプロジェクトで管理する方法もあるようですが、
個人的な意見ですが、品質管理の観点から別々のプロジェクトにした方がいいと考えてます。

同じプロジェクトで管理すると、フロントエンドとバックエンドの動きの見通しがよくなるのがメリットですが、
フロントエンドとバックエンドの結合度が高くなり、品質の観点で継続的にメンテするには幾分やっかいな気がします。

1~3人とかの少人数開発ではいいですが、
人数が増えてくると全員がフロントエンドとバックエンドの知識を備えてなければならなく、
言ってしまえば知らなくていい情報やファイルがオープンになってしまってるとも言えます。

高凝集・疎結合はエンジニアリングの基本なので、
なるべく結合度を弱めたプロジェクトの単位がいいのかなーと思ってます。

アドバイスございましたら是非お願いいたします。