リアルタイムWebアプリケーションフレームワークMeteorについて

  • 176
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

初めてAdventCalenderに参加させていただきます。
先月頭に初めてMeteorに触り、1ヶ月しか触っていないですが、
その素晴らしさに感動し、DiscoverMeteorの翻訳作業に参加させて頂いたりしてます。

その感動したポイントをお伝えしたいと思い今回Meteor AdventCalenderに記事を投稿することにしました。

目標は、「ちょっと触ってみるか」と思っていただくことです。

Meteorに出会う経緯

もともとiOSアプリ開発をやりたくて組み込み系エンジニアからWeb系エンジニアに転向しました。
その状態でWebアプリ開発に触れ、ネイティブアプリと比較して 違和感というか納得いかない感を感じており、
それを解決してくれたのがMeteorでした。

納得いかない点を説明させていただくところから始めさせてください。

Web開発の違和感(SPA以前)

Web開発で納得いかない部分は以下です。

  • POSTなどのデータ更新処理でURLが変わる挙動
  • ページ移動によりメモリがリセットされる問題

POST処理は

post処理
<form method='post'  action='/hogehoge' >
//各種input要素
</form>

通常ドキュメント修正後においてPOST処理を行うとactionに指定された/hogehogeにページに移動します。
ですが、通常のWebアプリなら処理中にバリデーション処理(ドキュメントの内容チェック等)を行います。
バリデーションエラーの場合でもPOSTによりhogehogeにページ移動をしているため、前のページに戻る必要があります。

戻ってみるとドキュメントの編集データは消えています。ですので、POST実行直後に編集情報をセッションに退避する対応が必要です。
最初にネイティブアプリに触ってる身としては、なんだか面倒くさいと思ってしまいました。

これを解決してくれたのがJSのMVCフレームワークでした。

バリデーション処理が二つ?

ですが、JSのMVCフレームワークでも問題になりそうなことがあります。
AngularJSなどで構築したクライアントサイドとPHPによるサーバーサイド。
それぞれでバリデーションが必要になります。

クライアントサイドがネイティブアプリと同じだと考えればいいんでしょうが、
バリデーションのコードを共通化できないものでしょうか?

SPA以前より全体として複雑化しています。

SPA以前より学習コストが増加

実はAngularJSの勉強をしようと思ったことがありました。ですが、チュートリアルを触っただけで諦めてしまいました。
クライアントだけのために勉強してもアプリはできません。サーバサイドも作らないと。
ここを面倒くさいと思ってしまったためです。

でもサーバサイドを楽にする手段としてBaasを使うという手がありました。
特に便利そうだと感じたのがfirebaseでした。
ちゃんと設定してあげれば、クライアントサイドのオブジェクトが勝手に永続化されサーバサイドと同期していく感じです。しかもリアルタイムに!( WebSocketを使っている)

リアルタイムWEBアプリすごいと感じたのもこの時でした。

これなら、AngularJSのアプリも作れそう!しかもfirebaseには本命のiOS用のSDKも用意されていました。

ところが、、、、iOSのPush通知機能がありませんでした。 (Parseにはあるのに・・)
サーバサイドもカスタマイズできないと将来は辛いかも・・・・。

どっかにfirebaseのように クライアントサイドでも簡単に永続化されたオブジェクトにアクセスできるような仕組みのオープンソースなリアルタイムWeb開発フレームワークないんだろうか・・・

そして Meteorに出会いました。

Meteorで解決する点

Meteorでは上記の悩んでいるポイントが解決されています。

  • SPAなWebアプリ前提のフレームワークです。(なので画面遷移ごとにメモリがリセットされません)
  • バリデーションの定義がサーバーサイドとクライアントサイドで共通化できます。
  • クライアント側からDBの呼び出しが簡単にできます。
  • AngularJSなどと違ってサーバサイドとクライアントサイドを別々に勉強するのではなく、同時に作っていくイメージで開発&勉強できます。
  • リアルタイムWebアプリケーションが気軽に開発できます。

技術的に素晴らしいポイント

さらに勉強を進めていってMeteorが素晴らしいと思った点をまとめてみます。

リアクティブフレームワークである

Qiitaにおいて技術的に勉強になる点がいくつもあり

リアクティブプログラミングの記事をよく目にすることが多くなりました。リアクティブプログラミングとは
「計算を実行するトリガーを計算で参照している変数に置く」ということです。

リアクティブプログライングはよくエクセルに例えられます。エクセルのセルの中には数値を直接書く場合もありますが、「あるセルの数値を参照する計算」を書く場合もあります。

たとえばA1を書き換えたら、A1に関係する計算の値がほぼ同時に書き換えたところを目撃できるはずです。これの意味するところはA1との依存関係をプログラマではなくシステムが認識してくれるという点で、書き換えのタイミング制御をシステムが担ってくれるという点です。

こちらの記事がより詳細に説明されています。

Meteorにおけるリアクティブプログラミング

Meteorにおいては画面が書き変わるトリガーはユーザの入力イベントとは限りません。
ユーザによって値が書き換えられる場合や、サーバサイドのDBの値が書き換わったタイミングでそれをトリガーとして書き換えが実行されます。

サーバサイドのDBの値を書き換えた際に画面に対応する値が表示されていた場合は、それが透過的に反映されることを意味します。
ユーザがリロードを押すようなタイミングではなく、DBを書き換えたタイミングで実行されるのです。これはすごいですよね。

実際に
DBの内容を更新した場合に、関連するログを出力するコードサンプルを書いてみました。

tracker_test.js

Counts = new Mongo.Collection("counts");
//一部省略
if (Meteor.isClient) {
  num = function(name){
    return Counts.findOne({name:name}).num;
  }

  set_num = function(name,num){
    var fo = Counts.findOne({name:name});
    if (fo === undefined) {
      return;
    }
    Counts.update(fo._id,{$set:{num:num}});
  }

  Template.trackerTest.rendered = function(){
    //自動実行される様子を観察ください
    Tracker.autorun(function(){
      console.log("log(1) c1=" + num("c1"));
    });

    Tracker.autorun(function(){
      console.log("log(2) c2=" + num("c2"));
    });

    Tracker.autorun(function(){
      console.log("log(3) c3=" + num("c3"));
    });

    Tracker.autorun(function(){
      console.log("log(4) c1=" + num("c1") +",c2=" + num("c2"));
    });

  }
//一部省略
}

ブラウザのコンソールから動作を確認すると以下のように出力されます。

set_num("c2",1)
tracker_test.js:39 log(2) c2=1
tracker_test.js:47 log(4) c1=12,c2=1

set_num("c3",1)
tracker_test.js:43 log(3) c3=1

set_num("c1",1)
tracker_test.js:35 log(1) c1=1
tracker_test.js:47 log(4) c1=1,c2=1

各ログはログ出力対象の変数の変化に応じて出しわけされることがわかるかと思います。
例えばc1の内容を書き換えると、実際にc1の内容をログ出力しようとしている
log(1)log(4)が実行されます。

クライアントからのDBアクセスが容易にできる理由

実はMeteorにはクライアントサイドで動作するDBとしてminiMongoという実装がフレームワーク内に取り込まれています。

これによってDBアクセスを行うことができるコードが共通エリア(サーバサイドでもクライアントサイドでもコードとして取り込まれる領域)に置くことができます。

ここに置きMeteor Methodという特殊なメソッドとして定義すると、
RPC(リモートプロシージャコール)としてクライアントサイドからサーバサイドのコードを実行できるようになります。

ここで、「RPCだったらサーバサイドだけで定義すればいいし、miniMingoいらなくね?」と思われるかもしれません。

クライアントサイドのDBはシミュレート用として動く。全てはリアルタイムWEBのために。

実はMeteor MethodにおけるminiMongoの値は直接使われることはなく、シミュレーション用として実行でされます。
なんでこんなことをするのか?全てはリアルタイムWEBのためです。

リアルタイムWEBにおいてはあらゆる動作がリアルタイムで行なわれます。
その度にDBアクセスを行い、応答を待ってから画面の書き換えを行なっていたら、UXとしてイマイチじゃないでしょうか?
そういった問題点をMeteorはデフォルトで回答を用意していたわけです。
つまり画面上はリアルタイムWeb用にシミュレートによって計算した値を表示し、
実際の計算はサーバサイドで行い、なんらかの要因によってサーバサイドとクライアントサイドで値に差異があった場合は、DBからの応答を持ってこっそり値を書き換えます。

リアルタイムWEBアプリのためのプロトコルがある

WebSocketがあってもWebAPIのようにプロトコルが必要です。
それをMeteorとして用意しています。DDPです。

通信内容は

  • RPC呼び出し
  • コレクションの追加、削除、変更
  • DBの公開範囲の設定
  • 死活監視

です。

基本的にMeteorは最初にSPAのアプリ部分を配信し、その後はDDPによるコレクション差分情報をやり取りすることで動きます。

JSのMVCフレームワークではWebAPI設計を考えなくてはいけないですが、
Meteorなら勝手にDDPをつかってクライアントサイドのDB内容を更新するので、
便利です。

そしてその仕様が公開され、様々な言語で実装されており、
Meteorで作成されたアプリ以外からでもアクセスできるようになります。

パブリッシュとサブスクライブという概念

開発時点では実はセキュリティを無視したパッケージが組み込まれており、
サーバサイドのDBとクライアントサイドのDBは完全同期されています。
プロトタイピングとしてはいいですが、DBの肥大化とともに現実的ではなくなってきます。

そのためにパブリッシュという概念を用いて各クライアントごとのDBの共有範囲を指定できます。
またパブリッシュ設定は引数を取るようにもできるので、
クライアント側から渡された情報に従って、共有範囲を規定できます。
共有範囲に合わせてDDPが必要な情報を配信したりクライアントに渡すときに不要と判断したフィールドの削除を行ったりします。

ちなみにサブスクライブはパブリッシュ設定の呼び出しになります。
引数を取るパブリッシュ設定に対して引数を渡すこともサブスクライブで行います。

この概念を用いて、ユーザごとの公開範囲を指定し、セキュリテイのリスクを抑えるわけです。

開発環境

作っている最中の画面が見える

ファイル保存を自動認識してコンパイルするのはまぁまかなと思ったのですが、
Web画面がファイル変更に合わせて、リアルタイムに書き換わっていくのはすごく新鮮でした。
iPadで今作っている画面を表示しっぱなしにして、ゴリゴリいじっていくとみるみる変わっていく様は未来のWeb開発と感じます。
CSSファイルの内容もほぼ瞬時に変わるのでデザイナーさんも触りやすいんじゃないでしょうか?

デプロイ環境が最初から用意されている

テスト用に簡単にデプロイする環境が用意されています。
コマンド一発なのですごく楽しいです。

環境があんまり汚れません

インストールも簡単ですが、アンインストールも

rm -rf ~/.meteor/
sudo rm /usr/local/bin/meteor

とするだけです。
作り手の良心を感じます。

必要なNodeJSとmongoDBも上記に含まれています。

最後に

実は基本的にこの内容は全てDiscoverMeteorの受け売りだったりします。
DiscoverMeteorはJS初心者でも読めるように作られておりますので是非見てみてください。

参考

この投稿は Meteor Advent Calendar 20141日目の記事です。