Node.js+Socket.io+StreamingAPIで実況支援Webアプリを作る

# 目的
　自分の成果物について文書化することで、今後似た問題にぶつかった時の解決までの時間を早めることが目的です。

# 結論
　**Twitter StreamingAPIとNode.js(socket.io)およびJavaScriptを用いた、ツイート速度**（以下、TPS:Tweets Per Second）**リアルタイム測定Webアプリケーション**を開発しました。
　後述する"query.json"を追記・修正することで、どんなキーワードのTPSも測定可能です。今回は自分の趣味が高じ、**プロ野球12球団のツイート**に設定しています。
　動作は以下の通りです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">いいねー！！　楽しい。 <a href="https://t.co/9bO1sole98">pic.twitter.com/9bO1sole98</a></p>&mdash; すいばり@&#39;16年度9試合2勝2敗 (@Suibari_cha) <a href="https://twitter.com/Suibari_cha/status/831497717901729792">2017年2月14日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

# 内容
　本アプリケーションの動作の流れは以下の図の通りです。（手書きで失礼）

<img src="https://qiita-image-store.s3.amazonaws.com/0/110901/caf596cc-af86-ba6d-c395-4e312601254b.jpeg" width=400px>

## 処理解説
　図中で振った丸付き番号部分の処理について、以下に詳しく記載します。

|No|動作|処理概要|処理説明|
|--:|:--|:----|:-------------------|
|1|サーバ<br>(Node.js)|**検索用キーワード読み込み**|サーバ上に置いた検索キーワード設定用JSON"query.json"を読み込みます。|
|2|サーバ<br>(Node.js+twit)|**StreamingAPI接続**|1の検索ワードでStreamingAPIに接続します。これで**リアルタイムに野球関連のツイートを受信**できます。|
|3|サーバ<br>(Node.js+socket.io)|**ツイートをクライアントに送信**|ツイート受信時に、そのツイートをクライアントに送信します。以降、4～6は受信の度に繰り返されます。|
|4|クライアント<br>(JavaScript)|**応援チーム識別**|"query.json"をクライアント側でも読み込み、**「ツイートがどのチームを応援しているのか？（複数チーム認識可）」**を識別します。|
|5|同上|**TPSn算出**|4で識別した**応援チームにおけるツイート間の時間差から、TPSnを算出**します。|
|6|同上|**TPS移動平均算出**|5の結果のままであるとTPSの分散が大きく、指標として参考になり難いです。よって**任意の配列長で移動平均TPSを算出**します。|
|7|同上|**DOM反映**|6で得られたTPSと、ツイート本文を**DOMに反映**します。|

### 「5 TPSn算出」のコード
　本記事では「TPSnの算出方法」関数のみ紹介します。他処理部のコードは、機会があれば別の記事とします。

　計算過程の変数をグローバル変数にしたくなかったことや、引数を与えて処理をさせたかったため、**クロージャ**設計に挑戦しました。クロージャの動作については、以前、自身の学習のために記事を書きましたので、ご参考にどうぞ。

* [Qiita: JS初心者がクロージャ（closure）を理解する](http://qiita.com/Suibari_cha/items/6ca74b57baed68fde4fb)

```client.js
function calcTPSn() {
  const MAX_L = 10;
  var tps_t   = {};
  tps_t.tmp   = {}; 
  
  return function(team) {
    for (var i = 0; i < team.length; i++) {
      if (tps_t[team[i]] != undefined) {
        var now = new Date();
        tps_t[team[i]].push(1 / secondDistance(tps_t.tmp[team[i]], now)); //配列末尾に計測時間を追加
        if (tps_t[team[i]].length > MAX_L) { //配列長が一定以上になっていたら削除
          tps_t[team[i]].shift();
        }
        tps_t['tmp'][team[i]] = now; //現在時刻をtmpに更新
      } else {
        // TPS連想配列にキーがない（初めて計測するチーム）
        tps_t[team[i]]     = [];
        tps_t.tmp[team[i]] = new Date();
      }
    }
    console.log(tps_t);
    return tps_t;
  }
  
  // 時間差測定用関数
  function secondDistance(src, dst) {
    var deltaMillsecond = dst.getTime() - src.getTime();
    return deltaMillsecond / 1000;
  }
}
```

## サーバディレクトリ構成
　参考に、サーバのディレクトリ構成を以下に示します。

```
root/
　├ app.js
　├ index.html
　├ js/
　│　└ client.js
　├ css/
　│　└ style.css
　├ etc/
　│　└ query.json
　└ node_module
　 　├ twit
 　　├ socket.io
　 　└ ...
```

# 最後に
　**「こういう機能が欲しい」という要望や、質問などありましたら、ぜひコメント欄へ記入をお願いします。**自分のモチベーションが上がります。
　[前回の記事](http://qiita.com/Suibari_cha/items/6ca74b57baed68fde4fb)がたくさんのいいねを頂き、嬉しかったです。

## 懸念点
1. 初めてサーバサイドとフロントエンド両方をコーディングしたことで、**「この処理はサーバorブラウザ、どちらで行うべきなのだろうか？」という視点を持てました**。今回はクライアントに殆どの処理を任せてしまっていますが、定石ではどうなのでしょうか？　その点考えてみたいです。
2. 日本語のツイート以外も拾うため、MLBのDetroit "Tigers"やNFL(アメフト)のPhiladelphia "Eagles"も測定対象となっている。[本家ドキュメント](https://dev.twitter.com/streaming/overview/request-parameters#language)を参考に追記したい。(17/2/17追記)

## 展望
1. **TPSを各球団の線グラフで示して可視化する**のは、より情報を見る側に伝えやすくなるうえ、時間経過を見る事もでき、有効だと考えています。
2. **形態素解析によるHOTワードの表示機能**は、以前作ったJava版(後述)では、満足いく出来にはなりませんでした。*「今行われている6ゲームのうち、どこのどの選手が熱いか？」*を分析できる機能を追加したい。
3. Twitterで野球実況を見ていて、*「話題になっているが、よく知らない選手」についてSTATSやキャリアなど検索するのが手間だな*とよく感じます。これを解消するために、3.の形態素解析と合わせて、**ツイートに含まれる選手名などを識別して、情報を検索して教えてくれる機能**を追加したい。
4. **デザイン**。CSS学習中です。

## 所見
　以前(約1年前)はJavaをやっており、以下のようなアプリケーションを作りました。今回やったことはこれのリメイクになります。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">完璧！！ <a href="https://t.co/zB0KRyrEyW">pic.twitter.com/zB0KRyrEyW</a></p>&mdash; すいばり@&#39;16年度9試合2勝2敗 (@Suibari_cha) <a href="https://twitter.com/Suibari_cha/status/719886454357254144">2016年4月12日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

　ご覧のとおり、Javaの時の方が多機能でした。しかし、Javaが私の初のオブジェクト指向言語だったことや、後から機能を追加しすぎたために、**メンテナンスが行えなくなりました**。(どこを弄ってもエラーが出てしまい、なぜ正常に動いているかを把握できない状態でした)

　今回はその反省を生かして、**最初に自分の決めた要件通りにまずシンプルに仕上げ、こうして公開しました。**

## 参考文献
* [Qiita: "node.jsのtwitterモジュールを使ってみた作業メモ (2)"](http://qiita.com/rarewin/items/7662b12db9995b5e61fb) @rarewin氏
* [Qiita: "Node.js + Socket.io + StreamingAPIでツイートを取得してWebサイトにリアルタイム描画する"](http://qiita.com/n0bisuke/items/327e10da3eac09dd6985) @n0bisuke氏
* [新JavaScript例文辞典: "JSONデータを解析して読み込む"](http://www.openspc2.org/reibun/javascript2/JSON/parse/0001/)
* [ほんっとにはじめてのHTML5とCSS3](http://honttoni.blog74.fc2.com/)
* [書籍(amazon): "JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック"](https://www.amazon.co.jp/dp/4883379930?tag=text2musiccom-22&camp=243&creative=1615&linkCode=as1&creativeASIN=4883379930&adid=0CXXG6FA2D5GTY6BS4Y0&)
* [書籍(amazon): "改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで"](https://www.amazon.co.jp/%E6%94%B9%E8%A8%82%E6%96%B0%E7%89%88JavaScript%E6%9C%AC%E6%A0%BC%E5%85%A5%E9%96%80-~%E3%83%A2%E3%83%80%E3%83%B3%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%81%AB%E3%82%88%E3%82%8B%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E7%8F%BE%E5%A0%B4%E3%81%A7%E3%81%AE%E5%BF%9C%E7%94%A8%E3%81%BE%E3%81%A7-%E5%B1%B1%E7%94%B0-%E7%A5%A5%E5%AF%9B/dp/477418411X)