Sails.js(0.10.x)でSocket.IOを使った超シンプルなチャットサービスを作る

# Socket.IOを使った超シンプルなチャットサービスを作る
Sails.jsはとても簡単にシステムが構築できて超便利なんですが、その中でも素晴らしいのがSocket.IOがすでにフレームワークに組み込まれているところ。Sailsを始めて１時間もあればSocet.IOを使ったリアルタイムなんちゃってWebサービスが作れてしまいます。

ただ難点がSails.jsのバージョンアップの速さ。巷にある情報はver0.9以下の情報が多く、Socket.IO以前にそもそも基礎的なアプリを作るのに時間がかかってしまう。というわけで、ver0.10系からSails.jsを始めた人向けに、Socket.IOを使ったリアルタイムチャットサービスの作り方を書きます。

今回作ったソースコードはこちらに置いています。
https://github.com/KeitaMoromizato/chatsample

## Socket.IOとは？
簡単に言うと、ブラウザに対してPush通知を行う仕組み。Facebookとかの通知が来るやつと同じですね。

## アプリの作成
まずはアプリを作成。名前は適当に「chatsample」にでも。ver0.10.x系からは`generate api`コマンドでController/Modelが同時に作れちゃいます。今回は超シンプルなチャットサービスなので、MessageController/ModelだけでOK。

```
$ sails new chatsample
$ cd chatsample
$ sails generate api message
```

## ちょっとした設定
たぶんv0.10から追加されたデータベースのマイグレーション設定。migrateの行のコメントアウトを消しましょう。

``` config/models.js
   migrate: 'alter'
```

## Controller/Modelの実装
今回はControllerとModelには__一切コードを書きません__。Controller/Modelさえ作ってしまえば、`GET /message`でMessageモデルを全取得、`POST /message`でMessageを新規作成という風に全自動でやってくれます。

## Viewの作成
こんな感じで、超適当にViewをつくります。`views/homepage.ejs`がデフォルトのトップページになるので、これをガッツリ書き換えちゃいます。要件はTextAreaと送信ボタン、チャットのタイムラインを表示するためのul要素だけ。こんなところに時間をかけてもしょうがないので適当にいきましょう！

![21.png](https://qiita-image-store.s3.amazonaws.com/0/52054/9a27c26d-af2b-649d-6c7f-b6a41ee4f461.png)

``` views/homepage.ejs
<div id="chat-main">
  <ul id="chat-timeline"></ul>
  <div id="chat-form">
    <textarea id="chat-textarea"></textarea>
    <div id="chat-send-button">送信</div>
  </div>
</div>
```

## フロントエンド実装
フロントエンドの実装。まずは結論から、ということで全体像は以下のような感じです。よく分からなくても下で説明していくので大丈夫です。説明を見るのが面倒という方はとりあえずコピペして動かして楽しんでください！

``` assets/js/main.js
(function() {

  // Socket.IOに接続
  var socket = window.io.connect();

  socket.on('connect', function() {
    // 以下の処理はSocket.IOのconnectメッセージ受信後(接続確立後)
    // に行わないと失敗する
    socket.get("/message", {}, function(messages) {
      for (var i = 0; i < messages.length; i++) {
        $("#chat-timeline").append('<li>' + messages[i].text + '</li>');
      }
    });

    socket.on('message', function(message) {
      if (message.verb == "created") {
        $("#chat-timeline").append('<li>' + message.data.text + '</li>');
      }
    });
  });

  $('#chat-send-button').on('click', function() {
    var $text = $('#chat-textarea');

    var msg = $text.val();

    socket.post("/message", {
      text: msg
    }, function(res) {
      $("#chat-timeline").append('<li>' + res.text + '</li>');
      $text.val('');
    });
  });
})();
```

### Socketの接続
リアルタイム通信のためにSocketを接続します。`window.io`は/assets/js/dependencies/sails.io.jsに定義が書かれていたりするのですが、まあ読まなくても大丈夫です。実装や関数が気になったり、業務で使うレベルになったら考えましょう。
とりあえず、`window.io.connect()`を呼ぶとSocketを接続して、完了すると'connect'イベントが呼ばれるということだけ覚えてたら大丈夫です。

```js
// Socket.IOに接続
var socket = window.io.connect();

socket.on('connect', function() {
  // 接続後の処理
});
```

### 保存済みメッセージのGET
まずはすでに保存されているメッセージを取得します。普通ならjQueryでも使って`$.get()`とか`$.ajax()`とかやりたいところですが、ここはsocketを使います。socket経由でGETをすると普通にajaxで情報を取得する＋「今取得した情報について何か通知があれば教えてね」とサーバーにお願いしにいきます。
ここを`$.get()`とかにしてしまうとリアルタイム通知が受け取れないのでお気をつけて！

``` js
socket.get("/message", {}, function(messages) {
  for (var i = 0; i < messages.length; i++) {
    $("#chat-timeline").append('<li>' + messages[i].text + '</li>');
  }
});
```

### 通知の受け取り
次は通知の受け取りかた。これも難しいことは無く、socketの`message`イベントを待ち受ければ良い。コールバックの引数.dataにモデルが入ってくるので、そのtextをリストに表示する。

``` js
socket.on('message', function(message) {
  $("#chat-timeline").append('<li>' + message.data.text + '</li>');
});
```

### メッセージの送信
送信ボタンのクリックイベントで実行する処理。ここもGETの時と同様、通常のajax関数ではなく`socekt.post`を使う。すると不思議な事に↑で`socket.get`した人全員に新しいメッセージが作成された通知が飛ぶ。素晴らしい！

``` js
socket.post("/message", {
  text: msg
}, function(res) {
  $("#chat-timeline").append('<li>' + res.text + '</li>');
});
```

## 動作確認
実装は本当にこれだけ。というわけで動作確認をしてみる。まずはsailsアプリケーションを起動

```
$ sails lift
```

ブラウザを２つ開いて`localhost:1337`にアクセス。この画像を見てもよくわからないが、片方のブラウザで送信した内容がすぐに別のブラウザにも現れる！

![22.png](https://qiita-image-store.s3.amazonaws.com/0/52054/2ff7373a-46b1-8362-d848-ab4c6ad9ceea.png)