Firebase, prect-cli を使って~50人向けのサービスを作っている話 (前編)

社員の人なら覚えている人もいると思うが、3年前に WebRTC を利用して顔のスナップショットを共有するサービスを社内ロンチしたことがあった。

当時、思っていた以上に多くの人に利用して頂けて嬉しかった事を覚えている。
が、紆余曲折解あり、今ではサーバは止まったままである。

あれから月日が立ち、当時よりも技術環境は良くなっていると感じており、どこかでもう一度フルスクラッチで書き直したいなぁと思っていた所、今回のアドベントカレンダーちょうどいいなと思ってやった。


# 当時のサービスについて

はっきり言って、[Sqwiggle](https://twitter.com/sqwiggle) の模倣だった。
あの当時、[naoyaさんが書いた記事](http://d.hatena.ne.jp/naoya/20140512/1399864629) で知り、『これは凄い』と直ぐに事業所の人を集めて語った記憶がある。

それから 1～2ヶ月、プライベート時間をかけて、 **HeadShot** というサービスを構築した。
コンセプトは **雰囲気の共有**。

※ ちなみに、[Sqwiggle は閉鎖が決まっている](https://twitter.com/sqwiggle/status/730383154742169600) 。素晴らしいサービスであったのに残念。


## サービス概要

<img src="" alt="ここに当時のキャプチャが出る予定"></img>

- Google OAuth でログイン
- ユーザの顔のスナップショット
- 全ユーザの会話の文字起こし
- 顔認識 とマスクがけ
- インスタ風・セピア・白黒等画像加工もあり
- 1 on 1 の動画チャット

## 環境 ( 当時 )

### バックエンド

WebRTC のシグナリングを考え、当時 WebSocket を最も簡潔に扱えた Node.js を選択。
Express + Socket.io で構築。

### クライアントサイド

当時自分は C# で WPF の開発に良く親しんでいたので、フロントエンドで気軽に使える MVVM フレームワークを探し、筋が良さそうな Vue.js を選択。

#### 認証

[Google API Client Libraries](https://developers.google.com/api-client-library/javascript/start/start-js) を使用しての普通の Google OAuth。

#### 画像取得

顔のスナップショット取得のために、 WebRTC を利用。
メインはスナップショットの共有だったので RTCDataChannel 経由での画像データ送受信を行った。

#### 動画チャット

どうせ WebRTC 使うなら、と動画チャットも追加。
しかし、これは雰囲気の共有というコンセプトから、おまけ程度に考えていた。

#### コンピュータビジョン

顔を直接出したくないという要望が多かったので、まずは Face Detection 機能として Haar-like face detection の実装を JavaScript で行った。当時、Open CV の JavaScript ポーティングもあったがパフォーマンスが足りず独自実装した。
これにより、顔の上にマスク画像をオーバーラップできるようになった。

また、自分の気持を表現するために、画像の雰囲気を変える Filter 機能も実装した。
当時世界中で人気だった Instagram を真似たトイカメラ風加工や、セピア・白黒の加工も実装した。
今であれば CSS である程度再現できる内容だが、当時はピクセル RGB を数値計算して行っていた。

#### 会話起こし

雰囲気の共有というコンセプトから重要と考えていた機能がこれ。
しかし技術的には大したこと無くて、Chrome の [Web Speech API](https://developer.mozilla.org/ja/docs/Web/API/Web_Speech_API) を使っていただけ。

## 今、振り返って

今見てみても、悪くない内容だったと思う。
スケールしないとか、ブラウザの仕様更新に追いつけないとか、色々問題はあったけど。

# HeadShot 改め、 atmosphere

ということで、今 **atmosphere** というサービスを作っている。
展開は今の所、前回と同じ社内ツール ( 約50人向け ) を考えているが、ソース自体は公開して誰でも使えるようにはしていきたい。

やりたい事は余り変えずに、技術のみ更新していく。
フィルタやマスク機能等、あまり本質的では無い部分は削っていく。

## 環境 ( 今 )

### バックエンド

**[Firebase](https://firebase.google.com/?hl=ja)** を選択。
まずはホスティングサーバとして利用。
また、Firebase が持っている Google の OAuth2 も利用。
データ交換には firestore を利用。P2P でのデータのやり取りが主であるため、シグナリング等の管理的なデータの共有に使う。

ああ、便利な時代になった。

対象ユーザ数としては ~ 50 人と小規模であるが、この程度であれば Firebase の無料枠でさばききれるとも思い、最適だとも思う。

### フロントエンド

**[preact-cli](https://github.com/developit/preact-cli)** × **[mobx](https://github.com/mobxjs/mobx)** を選択。

普段の開発では、React × Redux を使用しているので、個人開発ではちょっと違うものが使いたかった。

preact-cli は、[PRPL](https://developers.google.com/web/fundamentals/performance/prpl-pattern/) に則った SPA を勝手に作ってくれる優れもの。
Firebase は HTTPS 前提で、HTTP2 Server Push にも対応してるので相性最高。
Server Push 用の設定ファイルも作ってくれる。
**この辺、良く分かってないが、勝手にやってくれたものを解析しながら学べるので助かる**。

mobx は、Observable な Store をサクッと作れる。
そして簡単に React, Preact に connect できる。

### 画像取得

この辺は、進化している様でしていない感じ。
ライブラリ等は使わずに愚直に実装。

# 開発

- Windows 10
- Node v9.2.0
- firebase 
- preact-cli 2.1.0

## 準備

必要な環境は [scoop で用意する](https://qiita.com/kikuchi_kentaro/items/77793e4a21db6ffdb7cd)。

```ps1
scoop install nodejs yarn coreutils
yarn global add firebase-tools@latest preact-cli@latest
```

※ Yarn で入れたモジュールが上手く起動できない場合、scoop の参照が間違っている可能性がある。以下を修正すると動作する。

```batch:~\scoop\persist\yarn\bin\preact.cmd
@"%~dp0\..\..\..\..\..\AppData\Local\Yarn\config\global\node_modules\.bin\preact.cmd"   %*

rem ↑ この '..\' の数が合っていない事がある
```

### Firebase の登録・初期化

まずは [Firebase Console](https://console.firebase.google.com/?hl=ja) へ移動し、プロジェクトを作成する。

![rapture_20171217090243.jpg](https://qiita-image-store.s3.amazonaws.com/0/20198/ddb8b136-3839-a253-5616-0fd9f9fb0f10.jpeg)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/3ca3b7dc-2f52-a181-5b5f-ecb337674033.png)

これだけで Firebase プロジェクト自体の作成は完了。

次は、[gh コマンド](https://qiita.com/kikuchi_kentaro/items/f500f261d1292ebe2941) で Github プロジェクトを作る。

```ps1
PS> gh create

# create kentork/atmosphere project

PS> gh cd kentork/atmosphere
```

この atmosphere プロジェクトを firebase 化する。
まずは、以下でfirebase にログインする。

```ps1
firebase login
```

次に、初期化する。

```ps1
firebase init
```

![2017-12-17_09h15_23.png](https://qiita-image-store.s3.amazonaws.com/0/20198/4452f160-c3f8-85da-5e2e-1785b814ee02.png)

使用する機能を選択する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/eda40676-1d28-0a76-7613-aa6c201723a6.png)

プロジェクトには、先程作った物をデフォルトとして指定。

![2017-12-17_09h18_01.png](https://qiita-image-store.s3.amazonaws.com/0/20198/94c6b2e6-35ba-f8c9-9e9e-a7543a1ac387.png)

Firestore, Hosting の設定を行う。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/df2449af-9660-9c33-d26d-9ae8873cd6f1.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/29817b90-014b-f2af-66d3-4458d927fb27.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/a62b762f-c1d0-3100-1d50-ef19ca5a5926.png)

最後に、SPAの設定にするかどうか聞かれる。SPA にするなら Yes を。

これで、Firebase の初期設定が完了する。

```bash
.
├── LICENSE.txt
├── README.md
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
└── public
    └── index.html
```

ここで、 firebase.json をちょっと見てみる

```json:firebase.json
{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "hosting": {
    "public": "public",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}
```

firestore の方は聞かれた通りの設定。

hosting の方は、

- public : 公開フォルダ。 `public` 以下を公開する
- ignore : `firebase deploy` 時に無視するファイル
- rewrites : 全リクエスト "**" を `/index.html` に飛ばす設定。つまり SPA。

という設定。特に難しい点はない。

これで、Firebase の基本的な設定は完了。

### preact の初期化

と、このプロジェクトを preact 化する。
既存のフォルダに展開するには、 `--force` が必要。

`preact create <template name> <folder name>`

この `<< template name >>` の部分に、[元にしたいテンプレート](https://github.com/preactjs-templates) を指定する。

```ps1
PS> cd ..
PS> preact create default atmosphere --force --yarn # yarn 派は '--yarn' が必要
```

```bash
├── LICENSE.txt
├── README.md
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
├── package.json
├── src
│   ├── assets
│   │   ├── favicon.ico
│   │   └── icons
│   │       ├── android-chrome-192x192.png
│   │       ├── android-chrome-512x512.png
│   │       ├── apple-touch-icon.png
│   │       ├── favicon-16x16.png
│   │       ├── favicon-32x32.png
│   │       └── mstile-150x150.png
│   ├── components
│   │   ├── app.js
│   │   └── header
│   │       ├── index.js
│   │       └── style.css
│   ├── index.js
│   ├── manifest.json
│   ├── routes
│   │   ├── home
│   │   │   ├── index.js
│   │   │   └── style.css
│   │   └── profile
│   │       ├── index.js
│   │       └── style.css
│   └── style
│       └── index.css
└── yarn.lock
```

実際に増えたのは、`package.json`, `yarn.lock`, `src/*` である。
何故か、`public` フォルダが消し去られている。preact-cli がビルド時に `index.html` を生成するので、要らないといえば要らないんだけど。

`src` 以下の各フォルダの役割等の詳しいことは、以下を読めば大体分かる。
[Getting started with PreactJS — A Step By Step Guide](https://blog.codeinfuse.com/getting-started-with-preactjs-a-step-by-step-guide-f3197f871753)

### 開発は何でするべきか

開発サーバを用意する方法として、いくつかある

#### ● preact の開発サーバを使う

```ps1
PS> preact watch
```

これで多分 WebpackDevServer が動いている。開発中は基本はこれ。

#### ● preact のプロダクションサーバを使う

preact-cli は、[開発時に使える本番サーバ](https://github.com/developit/preact-cli#preact-serve) と言うものがある

```ps1
PS> preact serve
```

これで HTTP2 で HTTPS なサーバで動作確認ができる。便利。
だけど、[Windows では動かない](https://github.com/developit/preact-cli/issues/262)。くそぅ。

後ろで [simplehttp2server](https://github.com/GoogleChromeLabs/simplehttp2server) という HTTP2 サーバが動くはずだが、これが立ち上がる前に [devcert](https://github.com/davewasmer/devcert) が [鍵を生成している所で落ちる](https://github.com/developit/preact-cli/issues/262)。がっくり。

WSL で動くみたいな情報もあるので、そっちで頑張ろうかな。

#### ● firebase に deploy してから確認

毎度できる方法ではないが、preact でビルドした後に生成物を本番 firebase に deploy して確認する。HTTP2, HTTPS どちらも確認可能だけど、時間もかかるし公開されるので。その辺を考えて使わないといけない。

```ps1
PS> preact build
PS> firebase deloy
```


### preact の Server Push の設定を Firebase に設定

preact には、Firabase の Server Push の設定を生成する機能がある。

```ps1
PS> preact serve --dir "public" --server "config"
```

しかし、この機能は [`forebase.json` を丸ごと上書き](https://github.com/developit/preact-cli/issues/374) してしまい、database や firestore 等の設定と共存できないので、簡単なスクリプトを書いてマージする。

```js:cmd/mergeConfig.js
//  yarn add jsonfile

var jsonfile = require('jsonfile')

let base = jsonfile.readFileSync('./firebase.base.json'),
  preact = jsonfile.readFileSync('./firebase.preact.json')

const newOne = Object.assign({}, base, {
  hosting: preact
})

jsonfile.writeFileSync('./firebase.json', newOne)
```

```json:package.json
...
  "scripts": {
    ...
    "config":
      "preact serve --dir \"public\" --server \"config\" --dest \"./firebase.preact.json\" && node cmd/mergeConfig.js",
    ...
  },
...
```

```ps1
PS> yarn run config
```

# 前編まとめ

と、ここまでで開発できる環境ができた。
後編からは、実際の実装部分に入っていく。