MonacaとHerokuを使ってSocket.ioによる通信対戦アプリを作るための準備

# 前置き
この記事は、[Monaca Advent Calender](https://adventar.org/calendars/3073)の10日目の記事です。


# 目的
タイトルの通りです
前提知識はMonacaは触ったことあるけれどHerokuもNode.jsもSocket.ioも触ったこと無い人向けです

# 環境と前提条件
* Monacaのアカウント
* Herokuのアカウント
    * 無かったら[作りに行きましょう](https://signup.heroku.com/dc)
* ローカル環境でNode.jsが動いている
    * 無かったら[動かしましょう](https://devcenter.heroku.com/articles/getting-started-with-nodejs#set-up)

## 1.Monacaプロジェクト作成
ダッシュボードからOnsen UI付きの新しいプロジェクトを作成します
説明用にはOnsen UI V2 JS Minimumを使用します
![monacaproj.png](https://qiita-image-store.s3.amazonaws.com/0/99067/b7edebc8-dc75-8b72-6344-8aaba0d72755.png)

今回作成するHerokuのプロジェクト名を`sample-server`、Monacaのプロジェクト名を`sample`とします
以降出てくるこれらの文字は作成する時のプロジェクト名に読み替えてください


## 2.Herokuの準備
ローカル環境上で新規プロジェクトを作成します
Socket.ioの構築の仕方は検索すると色々出てくるのですが、最終的にはHerokuで動かすので[公式ドキュメント](https://devcenter.heroku.com/articles/node-websockets)を主に見ることにします

### プロジェクト作成
中身が空の`sample-server`ディレクトリを作成し、そこでnpmを使ってプロジェクトを作っていきます

```bash
// 今回作るHerokuのプロジェクト Monacaのアプリ名と同じか、アプリ名-serverとか
$ cd sample-server  
$ npm init --yes
```
ここでpackage.jsonが作成されるので以下のように編集します
//コメントが付いてるところです
編集内容は記事作成時点の物なので、[公式ドキュメント](https://devcenter.heroku.com/articles/node-websockets)と差異があるならばそちらを使用したほうが無難です
内容はNode.jsのバージョン指定と、起動時のコマンド指定です![image.png](https://qiita-image-store.s3.amazonaws.com/0/99067/6ca82742-6fae-4517-7193-9333bcf7782b.png)



```json:package.json
{
  "name": "sample-server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "engines": {        // 追加
    "node": "10.3.0"  // 追加
  },                  // 追加
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"  // 削除
    "start": "node server.js"                              // 追加
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

### Socket.ioの追加

`npm init --yes` を入力したその場所で以下を打ち込みます

```bash
npm install --save --save-exact express socket.io
```

### サーバサイドのソース作成
先程編集したpackage.jsonに"node server.js"と追加したので、このプロジェクトはserver.jsファイルを最初に実行して起動されます
なのでserver.jsファイルを作成し、編集します
具体的には[公式ドキュメントのリンク先](https://github.com/heroku-examples/node-socket.io/blob/master/server.js)からコピペします

この時点でプロジェクトのフォルダ内は以下のようになっているはずです

```bash
.
├── node_modules
├── package-lock.json
├── package.json
└── server.js

1 directory, 3 files

```

### Gitリポジトリ作成
作成したプロジェクトをHerokuに入れる準備をします

```bash
$ git init
$ git add *
$ git commit -am 'socket.io starting point'
```

### Herokuプロジェクト作成
HerokuのCLIを使用してプロジェクトの作成とGitリポジトリのpushを行います

```bash
$ heroku create sample-server
Creating ⬢ sample-server... done
https://sample-server.herokuapp.com/ | https://git.heroku.com/sample-server.git
```

Gitリポジトリの作成を既に行っている場合、この時点でRemoteに`https://git.heroku.com/sample-server.git`が追加されています
なのでそのままHerokuにpushします

```bash
$ git push heroku master
```



# 3.MonacaにSocket.ioを導入する
最初に作ったMonacaのプロジェクトを開き、index.htmlのsrcに先程のHerokuのURLを指定します
指定する際に、URLの末尾に`socket.io/socket.io.js`を追加します

```html:index.html
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>
  <!-- ↓追加  -->
  <script src="https://sample-server.herokuapp.com/socket.io/socket.io.js"></script>
  <!-- ↑追加 -->

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/style.css">

  <script>
    ons.ready(function() {
      console.log("Onsen UI is ready!");
    });
    // 追加
    var socket = io('https://sample-server.herokuapp.com');
  </script>
</head>

<body>
  This is a template for Onsen UI app.
</body>

</html>
```


これでSocket.ioがMonaca上で動作するようになります

# 4. 動作サンプル
これだけだと寂しいので、通信のサンプルを作成します
最初にログイン画面を表示し、入力したユーザ名をHerokuへ送信
返事を受信したら次のページへ遷移するだけです

まずはMonaca側

```html:index.html
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
  <script src="components/loader.js"></script>
  <script src="lib/onsenui/js/onsenui.min.js"></script>
  <script src="https://sample-server.herokuapp.com/socket.io/socket.io.js"></script>

  <link rel="stylesheet" href="components/loader.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsenui.css">
  <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css">
  <link rel="stylesheet" href="css/style.css">

  <script>
    ons.ready(function() {
      console.log("Onsen UI is ready!");
    });
    var socket = io('https://sample-server.herokuapp.com');
    // ↓追加
    // ログイン処理
    var login = () => {
      let username = document.getElementById('username').value;
      //ログイン結果取得時の処理
      socket.on('login', (data) => {
        let content = document.getElementById('navigator');
        content.resetToPage('home.html');
      });
      // 送信
      socket.emit('login', { name: username });
    };
    // ↑追加
  </script>
</head>

<body>
  <ons-navigator id="navigator" page="login.html"></ons-navigator>
  <ons-template id="login.html">
    <ons-page id="login">
      <div class="content" style="text-align: center;">
        <p>ログイン</p>
        <p><ons-input id="username" placeholder="User Name" float/></p>
        <br />
        <p><ons-button onclick="login();">ログイン</ons-button></p>
      </div>
    </ons-page>
  </ons-template>

  <ons-template id="home.html">
    <ons-page id="home">
      <div class="content" style="text-align: center;">
        <p>ようこそ</p>
      </div>
    </ons-page>
  </ons-template>
</body>

</html>

```


そしてHeroku側のserver.jsにログインイベントの受信部と返信部を追加します

```js:server.js
// ～前略～
io.on('connection', (socket) => {
  console.log('Client connected');
  socket.on('disconnect', () => console.log('Client disconnected'));
  // ↓追加
  socket.on('login', (user) => socket.emit('login', { username: user.name }));
});
// ～後略～
```

基本的にはこの2つがあれば通信している体裁が整います

* 送信部：`socket.emit(イベント名, パラメータ)`
* 受信部：`socket.on(イベント名, 処理)`

今回はあくまで準備なのでこの辺で