backbone.jsでtodoアプリを少しずつ作る(1)
まえおき
backbone.jsのtodoサンプルなんて、いまどきどこにでも転がっています。
でも、そのサンプルを参考に作ってみたけど、いまいち理解が深まらない。。。
という「自分」のために一歩ずつ理解を深めながら、もう一度todoサンプルを作っていこうと思います。
完成系
やってる内容はtodoアプリそのままですが、登録するものを「食べ物の名前」と「カロリー」にしてます。
下準備
backbone.jsはjqueryとunderscore.jsに依存します。
また、今回はデータをlocalStorageに保存します。これにはbackbone.localStorageプラグインを使用します。
まずはgithubより、backbone.js関連のファイルを取得します。
% git clone https://github.com/documentcloud/backbone.git
test/venderフォルダ内のライブラリから必要なものを取得し、以下のファイル構成で配置します。
ファイル構成
index.html (自分で作る) js - page-index.js (自分で作る) - lib - backbone.js - backbone.localStorage.js - jquery.js - underscore.js
index.htmlとpage-index.jsは以下の内容。
index.html
page-index.js
これで下準備は完了です。
モデルの設計と実装
まずはどんなデータを扱うかを考えます。
今回は食べ物とカロリーのセットなので、入力データ1つの構造はシンプルにこんな感じ
Food: { name: "名前" calory: "カロリー" }
これをModel化して、さらにCollectionを作ってみます。
※超ざっくり説明ですが、DBに例えるとModelはテーブル構造の定義で、Collectionが実際のテーブルみたいなものです。
page-index.js note02Foodモデルでは、デフォルト値の設定と初期化処理を定義し、これをベースにFoodCollectionコレクションを定義しています。
また、最後の2行でテストデータを登録しています。実際にlocalStorageの中を見るとこんな感じです。
※backbone.localStorage.jsは読み込むだけで、データの保存・読込を勝手にlocalStorageに向けてくれます。べんり
ビューの設計
次に登録したデータを表示するViewを作っていきます。
がその前にビューを設計していきます。
ここでいうビューの設計は、「どのようにビューを分割するかを決めること」です。
以下のような単位で、Viewを分割していきます。
・ページ内の構成による分割(header,footer等で分ける)
・機能的な分割(検索フォームと検索結果で分ける)
・よく繰り返すパーツを部分的に分割(検索結果内の1項目を別Viewにする)
結果こんな感じに分割してみました。
AppView : ページ全体を制御 - NewView : 食べ物の新規登録部分 - FoodListView : 登録された食べ物のリスト表示部分 - FoodView : リスト内の食べ物1件
データを表示
ではまず、既に登録されたデータを表示するだけのViewを書いてみます。
page-index.js note03先程のテストデータ登録はコメントアウトして、その下にViewを追加します。
各Viewの中身は以下の通り
FoodListView
このビューで扱うDOM要素をelに指定します。(ビューとDOMを紐つける感じ)
初期化の際に、ul要素をDOMに追加します。
また、食べ物が1件追加されたときに、FoodViewインスタンスを生成し、ul内に1件DOMを追加します。
addAllが呼ばれた際には、コレクション全てを走査して、それぞれについてaddを行います。
FoodView
elの変わりに、tagNameとclassNameを指定します。指定したtagNameとclassNameのタグがelに設定されます。
初期化時に自身に割り当てられたModelからDOMを生成し、elに設定します。
※新規登録用のView(NewView)は今回はまだ手を付けてません。
結果はこちら
localStorageのデータがリスト表示されます。
データを手動で登録してみる
ここで以下を「var App = new AppView;」の下に追加してみましょう。
// test window.fc = fc;
そして、consoleに「fc.create({name:'cakes', calory:300});」と入力してEnter。
結果はこちら
データが追加されたと同時に、リストに情報が追加されました。
AppView内で、Collectionへデータを登録した際に、foodlist.addを呼び出しているので当たり前といえば当たり前です。
ただ、ここで重要なのは、「データを登録するだけで、リストの更新ができる」ことが確認できたことです。
逆に言えば、「新規登録部分は、データを登録さえすれば良く、リストの更新は考えなくて良い」ので、
新規登録部分とリスト表示部分を完全に切り離して考えることができます。
このように、各Viewが疎結合になる様に設計していくことで、複数人による分散開発がより容易になります。
新規登録部分を作る
新規登録用のNewViewを実装していきます。とこんな感じ
page-index.js note047-9行:#newfood-buttonなbuttonがclickされたらaddFoodを呼び出すようにしてます。
このようにBackbone.View.eventsプロパティに対して、'イベント名 要素(セレクタ)':'関数名'と指定することで、特定のイベントに動作を割り当てることができます。
16-19行:データを登録した後に、自分自身を再描画しています。
30行:AppViewの初期化時にNewViewのインスタンスを生成します。
結果はこちら
FoodViewやFoodListViewと完全に切り離して開発することができました。
編集・削除機能を追加する
FoodViewにロジックを追加していきます。
page-index.js note057-8行:FoodViewに紐付けられたmodelに変更があった場合に呼び出す関数を指定しています。(Backbone.View.removeはデフォルトで定義されています)
11-15行:要素に対するイベントに対して、動作を割り当てています。
26-34行:編集用に要素を再設定します。食べ物の名前がダブルクリックされた際に呼ばれます。
35-40行:Enterが押されたときに、modelを更新します。
41-44行:modelを破棄します。
結果はこちら(ページの最初のものと同じ)
これで、一応todoアプリ的な物が完成しました。
まとめ
途中から感じたかもですが、Viewを細かく切り離した結果、機能を追加するときに、他のViewを意識しなくて良くなりました。
また、他のViewの変更は基本的にmodelの変更を介して行われるため、各Viewを編集するときにはmodelとのやり取りだけ気にすれば大丈夫です。
モジュールとして分割できるため、複数人での開発もより容易になりますね。
次へ進む
より、MVCぽく修正した感じがこちら↓
backbone.jsでtodoアプリを少しずつ作る(2) - 1010realのブログ ver2
RequireJSでモジュール管理してみるサンプル(2)
前回の記事で
「RequireJSには、最適化ツール(r.js)を用いて、各モジュールをminifyし結合することができます」
といったので、それについてさくっと書きます。
セットアップ ~ 最適化の実行
前回のサンプルの構成をそのまま最適化していきます。
1.まず、最適化ツール(r.js)の使用にはnode.js環境が必要なため、以下のサイトからインストールします。
Node.js
インストール後、nodeとnpmのバージョンを確認します。
ターミナルから以下のコマンドを実行
node -v npm -v
2.requirejsパッケージをインストールします。
ターミナルから以下のコマンドを実行
npm install -g requirejs
3.実際に最適化を適用します。
ターミナルから以下のコマンドを実行
r.js -o baseUrl=. name=page out=./page-built.js
- baseUrl: jsファイルが含まれているフォルダ、今回はhtmlと同階層
- name: 最終的に読み込むjs(data-mainに指定したjs)を指定
- out: 出力するjsファイル名
4.正常に出力されたら、index.htmlのrequire.jsを読み込むスクリプトタグのdata-mainの指定を出力されたファイルに変更
<script src="require.js" data-main="page-built.js" async></script>
上記で対応は完了です。
実行結果
実行結果は変わりません。
ネットワーク状態をみてみます。
読み込むファイルが1つだけになっています。
モジュールをどんなに分割してもリクエストは増えないのが嬉しいですね。
さくっと書きました。
RequireJSでモジュール管理してみるサンプル
RequireJSとは
Javascriptモジュールローダの1つです。
モジュールの依存管理だけではなく、パフォーマンス面も考慮されたライブラリで、
開発効率とパフォーマンスを両立したJavascript開発を実現できるかも。。。
RequireJSを使わないとこう成り得る...というサンプル
ページのロードが完了したら、タイトルと説明文をページに表示するというサンプルを用意してみました。
敢えて、色々良くない書き方をした部分もありますので、参考にはしないでください。
動作イメージ(in plunker)
ファイル構成
- index.html - メインHTML
- site-common.js - サイト内共通で使う定数や関数をここで定義
- addon.js - 上記のsite-common.jsの機能を拡張するようなモジュール
- page.js - ページのメインコード
各ソース
index.html
site-common.js
addon.js
page.js
あと、このサンプルですが、以下のシナリオで作られたと想定してみてください。
- 最初はシンプルに、ページロード完了時にタイトルと説明を表示していた。
- 後になって、別の開発者がaddon.jsを追加し、page.jsも書き換えて、タイトルに鍵カッコを付けた。
問題点
複数開発者で開発することを考慮すると、とたんに色々な問題点が出てきます。
- ユーザがライブラリの読み込み順を知らないといけない
addon.jsとsite-common.jsの読み込み順を入れ替えると鍵カッコがundefinedになってしまいます。
読み込みの順番が変わると正常動作しないってことはそれを全て把握して読み込む必要が出てきます。めんどい。
- グローバル汚染
定義している変数・関数が、全てグローバル領域に...汚染がひどいです。constantという名前で他の人が変数を定義したら色々死亡します。
- 意図と外れた設計
そもそもconstantへのアクセスは、site-common.jsを見る限り、setConstant,getConstantを用いて行って欲しいという意図が汲み取れますが、addon.jsの開発者がそのことを知らないのか、意図しないconstantへの直接アクセスが行われています。
という訳で、早速RequireJSさんにおでましいただきます。
RequireJSを適用してみる
適用してみた
動作イメージ(in plunker)
動作は同じですね
各ソース
index.html - 修正後
まずは、サイトからrequirejsをDLして設置。
スクリプト読み込みは、このrequire.jsのみに変更します。
data-main属性には、このページのメインコードのみ指定します。
また、scriptタグにasync属性を追加すると、対応ブラウザにおいて非同期でjsを読み込むことができます。
site-common.js - 修正後
まず全体をdefine(function(){ ・・・ })でくくります。
次に関数の戻り値として、このモジュールで公開したいオブジェクトのみを記述します。(return文)
addon.js - 修正後
全体をdefineでくくる際に、第一引数に依存モジュール(ここでは'site-common.js')を指定します。
また、site-common.jsモジュールのアクセスするための引数(名前は何でも良い。ここではCommon)を指定します。
その他は、site-common.jsと同じです。
page.js - 修正後
defineではなくrequireでくくります。
このrequire内部が、ロードが完了した際に実行されますので、window.onloadの内部処理をそのまま書けば大丈夫です。
また、define同様に依存モジュールとその引数も定義します。
考察
RequireJSを適用した事で、どんなメリットが生まれたかを列挙してみます。
- 読み込み時に、順番を意識しなくて良い
読み込みの順番はRequireJSが勝手に解釈してくれるので、各モジュールの依存関係さえ間違っていなければ、意識する必要はありません。
複数人での開発時にも、自分がそのページでやりたいことを書いたファイル(ここではpage.js)をロードすれば問題ありません。
- グローバルが汚染されていない
RequireJSを使うと、defineでくくり、公開する内容を自分で指定できるので、自然とグローバル領域は自然と汚染されない書き方になります。
(グローバルへの定義はRequireJSの提供するrequire関数、define関数のみとなっています)
- constantがプライベートに
addon.jsでのconstantへの直接アクセスもエラーとなり、公開されたsetConstantメソッドで書き換えるしかなくなりました。
これにより、site-common.js設計者の意図が守られます。
- レンダリング速度の向上
RequireJS使用前と使用後のネットワーク状態を比較してみます。
使用前
使用後
スクリプトのローディングを非同期で行うことにより、DOMの描画が並行で行われています。
結果的にDOMContentLoadedのタイミングはかなり早くなりました。
スクリプトの読み込みが終わらなくても、ユーザの目にサイトが表示されるので、待ち時間がより少ないと感じるはずです。
これらのメリットは、開発規模が大きければ大きいほど、開発人数が増えれば増えるほど、大きな効果をもたらすと思います。
さらに
RequireJSには、最適化ツール(r.js)を用いて、各モジュールをminifyし結合することができます。
これにより、さらなるパフォーマンス向上も見込めます。
そこについては、またその内。。。
公式サンプルから学ぶAngularJS (2)
ということで、前回の記事の続きで、AngularJS公式サイトのサンプル3つ目を作っていきます。
の前に、今回のサンプルはFirebaseというサービスを利用しているので、事前に登録を済ませておく必要があります。
さくっとできるのでぜひ試してみてください→以前にFirebaseに登録してみた記事
ではさっそく
サンプルその3「Wire up a Backend」
ブックマークの登録・編集・削除ができるサンプルです。
Firebaseと連携し、リアルタイムに他ユーザの変更も反映するようになっています。
イメージが沸きやすい様に、先に動作サンプル
今回のサンプルでやっていることは、大きく次の3つ
- ngRouteモジュールを使った、URLによるページ内表示内容の切り分け
- AngularFireを使ったFirebaseサービスとの連携
- 動的なフォームバリデーション
また、ソースの構成は以下
- index.html (メインHTML)
- project.js (メインJS)
- list.html (テンプレートとして読み込む)
- detail.html(テンプレートとして読み込む)
ここから、各ソースについて、自分なりの解説をしていきます。
※公式ページのサンプルソースとは、若干相違点もあります。
ソース
index.html
[a] AngularJS本体に加えて、ngRouteモジュールを読み込んでいます。ngRouteモジュールは、ng-view属性を指定した要素([d]の部分)に表示する内容をURL毎に設定することができる機能です。
[b] Firebaseサービスを使うためにAngularJS用Firebaseモジュールを読み込みます。
[c] さらに、見た目を少しよくしたいので、bootstrap.cssを読み込んでいます。(bootstrapはcssフレームワークです。簡単かつ迅速にサイトレイアウトを実現することが可能になります。ここでは詳細は省略します)
project.js
[a] まず、html全体に対するモジュール(project)を定義する際に、依存モジュールとしてngRouteおよびfirebaseを追加しています。これにより、このモジュール内にて、依存モジュールで新たに提供される要素にアクセスすることが可能になります。
[b] 'fbURL'という名前で、FirebaseサービスのベースURLを登録しています。
※value()は定数の定義ではなく、オブジェクトインスタンスの登録なので、文字列だけではなく数値、オブジェクト、関数等も登録できます。
[c] ここでは'Projects'という名前でFirebaseを利用するためのインスタンスを取得する関数を登録しています。
同じURLのサービスインスタンスを何度も作成する場合には、このようにfactory()を用いて登録しておきます。
[d] config()はモジュールの設定を行う関数です。ここではngModuleの設定を行っています。
URLに対して、対応するコントローラとテンプレート用HTMLを指定しています。パラメータとして値を渡したい場合には'/edit/:projectId'の用に記述します(20行目)
[e] 各コントローラの挙動を定義しています。先程パラメータとして渡した変数は、$routeParamからアクセスが可能です。(48,49行目)
list.html
[a] aタグのhrefには、最初に#をつけてページ外に飛ばないようにします。ngRouteモジュールを使って、ページ内の表示切替をする場合のテクニックです。
[b] ng-repeatには、filterやsearchを指定することで、表示する内容を制御することができます。ちなみに、orderByPriorityはFirebaseが返すオブジェクトを配列に変換するため、AngularFireが提供している機能です。Firebaseから取得したデータをng-repeatする際のおまじないだと考えて間違いないかと。
detail.html
[a1-a5] ここではフォームのバリデーションをしています。各フォームの部品に対して、以下のような属性を適宜追加することで、ユーザに優しい動的なフォームバリデーションが可能となります。
- ng-class="{クラス名:条件}"で、指定した条件が満たされたときに、この要素に追加されるクラス名を指定
- ng-show="条件" 条件が満たされたときに表示(満たされていないときには非表示になる)
- ng-disabled="条件" 条件が満たされたときに無効
また上記に設定する条件は、基本的なものは既に提供されています→バリデーションに関する説明とか(英語)
今回使用しているものは以下
- .$invalidで、入力値が無効かどうか(フォームの場合は、フォーム内の項目で1つでも無効なものがあれば無効判定)
- .$pristineで、未だユーザが値を入力したことがない(一度でも何かを入力したらその後はずっとfalse)
- .$error.xxxxで、xxxxに指定された形式でのフォーマットチェックを行います。urlでurl形式かどうか、invalidは必須判定
[b] このdetail.htmlテンプレートは、ブックマークの新規作成時と編集時に呼び出されますが、$scope.projectにFirebaseサービスインスタンスが設定されているのは編集時のみです。なので、project.$removeで削除ボタンの表示・非表示の制御を行っています。
大筋としては、こんな感じだと思います。解釈がおかしい点も多々あると思うので、鵜呑みにはしないでください。
ソースの説明が飛び飛びで分かりづらいと思うので、Firebaseとの連携、ngRouteモジュールの使用についてだけ、ざっくり以下に纏めました。
Firebaseとの連携部分について超ざっくり説明
1.firebase.js、angularfire.jsを読込
<script src="https://cdn.firebase.com/v0/firebase.js"></script> <script src="https://cdn.firebase.com/libs/angularfire/0.7.1/angularfire.min.js"></script>
2.依存モジュールとして、'firebase'を追加
var mainModule = angular.module('project', ['firebase']);
3.取得したいデータに対するサービスインスタンスを取得
var instance = $firebase(new Firebase('https://intense-fire-xxxx.firebaseio.com/angularSample3/[キー]/[キー]/...'));
上記でFirebase内のid(複数指定でその階層までさかのぼる)に対応したデータに対応するオブジェクトを取得できる
4.取得したサービスインスタンスを使って、データの取得や変更を行う。
主なプロパティとメソッドとしては
- instance.[キー]でその内部データにアクセス
- instance.$add(オブジェクト, コールバック関数):その階層に新たにオブジェクトを追加
- instance.$save():DOMの内容とFirebase内の情報を同期
- instance.$remove()、対象のデータをFirebaseから削除
ngRouteモジュールの使用部分について超ざっくり説明
1.angular-route.jsを読込
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-route.min.js"></script>
2.表示を切り替えたい要素に対して、ng-view属性を追加
<div ng-view></div>
3.依存モジュールとして、'ngRoute'を追加
var mainModule = angular.module('project', ['ngRoute']);
4.angular.module.config()で、ngRouteモジュールの設定(=urlとコントローラ、テンプレートの紐付け)を行う
※各コントローラの挙動の定義も行う必要があります。
mainModule.config(function($routeProvider) { $routeProvider .when('/', { controller:'ListCtrl', templateUrl:'list.html' }) .when('/edit/:projectId', { controller:'EditCtrl', templateUrl:'detail.html' }) .when('/new', { controller:'CreateCtrl', templateUrl:'detail.html' }) .otherwise({ redirectTo:'/' }); })
5.aタグでのページ遷移時には、#を最初に指定する
<a href="#/" class="btn">Cancel</a>
やってみて...
書き始めて10分後ぐらいに思ったことですが、1つのサンプルで3つのことを盛り込むよりも、個別にもっとシンプルなソースで、1つずつ説明したほうがきっといいんだろうなと思いました。。。
公式サンプルは、チュートリアルではなくて、こんなことができますよ程度に捉えたほうがいいのかも(いまさら)
次の公式サンプル4は、いずれまた。。。
リアルタイムのバックエンドサービスFirebaseに感動
Firebaseが「何これやべぇ」ってお話。
対話的リアルタイムアプリケーションのためのバックエンドサービスなんですが、
とりあえず、アカウント登録して、サンプルを動かすだけで、そのリアルタイム感に感動できます。
Firebase - Build Realtime Apps
アカウント登録の手順
1. 右上の「SIGN UP FREE」を押す
2. 登録するメルアドとパスワードを入力して、「Create My Account」
これで登録自体は完了です。
この後は、チュートリアル的な感じです。
3.登録後の画面で、「View Firebase」を押すと、ストレージの中がGUIで見れる
4. 赤い矢印の辺りがデータをツリー構造で表示してるGUIで、「新しいデータ登録してみ」とか「サンプルデータ入れてみ」とか聴かれるので、そのとおりにやってるといきなりポップアップでチャットウインドウが表示される。
5. このチャットアプリを複数ウインドウで立ち上げて、エア友達とチャットしている気になると楽しいw
データも更新される度に、リアルタイムでストレージのGUI表示も更新されるので、これを観ながらチャットすると尚楽しい。
このサンプルチャットアプリも5分で作れるというから驚き。(手順は以下)
Tutorial | Firebase
「何これやべぇ」
2014/5/8 追記
このFirebaseを使ったAngularJS公式サンプルを作りながら自分なりに解説してみた記事はこちら↓
公式サンプルから学ぶAngularJS (2) - 1010realのブログ ver2
JSFiddleでAngularJSを使う
前回の記事でのAngularJSのサンプルはJSFiddle内で動作確認してみたのですが、AngularJSをJSFiddle内で使うのに、ちょっとだけテクニックが必要だったのでまとめてみました。
1. まず、AngularJSを使いたいfiddleのFrameworks & Extensions設定を以下の設定にします。
- No-Library(pure JS)
- No wrap - in
2. css内に以下のコードを貼り付けます。
3. html内ではbodyタグは使えないので、bodyにng-appを追加している場合はdivに置き換える。
これで、AngularJSが利用できます。
Frameworks & Extensions設定の中でAngularJSの項目もありますが、選べるバージョンが少ないのと、設定によってうまく動かなかったりしたのでこちらの方法で動作確認しました。
この方法だと、自分が利用したいバージョンでAngularJSが読み込めます。
しかし、JSFiddle便利。。。
公式サンプルから学ぶAngularJS (1)
大層なタイトルをつけちゃいましたが、やってることは以下。
AngularJSの公式サイト(以下)のトップに乗っているサンプルを作りながら、AngularJSのチュートリアル的なことをしてみる。
英語:AngularJS — Superheroic JavaScript MVW Framework
日本語訳:AngularJS入門 | AngularJS 1.2 日本語リファレンス | js STUDIO
AngularJSとは?
間違ったことを書いてもアレなので、大部分を抜粋させていただきました。
- AngularJSは動的Webアプリケーション構築のためのフレームワーク
- データバインディングと依存注入を通して大部分の余計なコードを排除できる。分かりやすく言うと、動的アプリケーションを作るための様々な苦労(=JavaScriptでの大量なコード記述)を請け負ってくれるので、素早く機能開発を開始できる
- CRUDアプリケーションとかに特に向いている。逆に少し複雑な事(AngularJSのルールから外れること)をしようとするととたんにコストがあがるため、ゲーム等のバリバリのアニメーションとかには向かない
では早速サンプルを作ってきます。
サンプルその1「The Basics」
フォームに入力した値を、リアルタイムにDOM要素へ反映するサンプルです。
ソース
angular_1.html
- a. まずは、AngularJSを読み込みます
- b. AngularJSを適用する要素に、ng-app属性を追加します。(今回は文書全体に適用)
- c. テキストボックスに対し、ng-model="変数名"な属性を追加します。
- d. {{変数名}}でモデル内の変数を出力します。
動作イメージ
テキストボックスの値を変更すると、同時に出力されているDOM要素が更新されます。
上記の指定だけで、AngularJSが
「フォームの部品(=UI)と表示データ(=モデル)の紐付けと、どちらかが変更された際の同期処理」
をしてくれます。
これは「双方向データバインディング」と呼ばれ、AngularJSの大きな特徴の1つです。
また、AngularJSはブラウザに様々な記法を提供します(ngApp、ngModel等)
これを提供する仕組みを「ディレクティブ」と呼びます。
この仕組みを使って、自分で新たに記法を定義することもできます。
※ 他のモジュールを追加で読み込むことで使用できるようになる記法もあります
→ AngularJSディレクティブ一覧 (ngRouteより下)
サンプルその2「Add Some Control」
TODO管理を通して、要素の追加、読込、更新、削除を試すサンプルです。(バックエンドとの連携はなし)
ソース
angular_2.html
- a. todo.jsを読み込みます。todo.jsには、コントローラ(TodoCtrl関数)が定義されています。
- b. コントローラにより制御を行う要素に、ng-controller属性を追加します。
- c1-4. コントローラ内で$scopeに定義したプロパティやメソッドを参照して、各挙動を定義していきます。
todo.js
- a1-4. $scopeにプロパティやメソッドを定義します。これらは、ng-controller属性を追加した要素内から参照できます。
todo.css
動作イメージ
各UIからtodoの管理が行えます。
ここで大切なのは、todo.js内では、「データ(=モデル)をどうするか」しか定義していません。
「変更したデータをどう表示するか」については、AngularJSが全て請け負ってくれました。
という訳で、あっという間に要素の追加、読込、更新、削除を実現できてしまいました。
AngularJSが得意な分野の動的アプリケーションを作れば、相当な開発効率アップが見込める事が体感できたかと思います。
AngularJSの公式サイトにはあと2つのサンプルが紹介されています。
それらのサンプルについては、また今度。。。
2014/5/8 追記
公式サンプル3についても、作ってみました↓
公式サンプルから学ぶAngularJS (2) - 1010realのブログ ver2