課題を解決してUXを継続的に改善する
さて今回で連載は最後です。アプリケーションは、はじめに考えていた機能を実装したら終わりではなく、そこからがスタートです。ユーザーの声を聞いたり、行動を分析したり、手を加えながら磨き上げていく必要があります。
今回は、実際のプロジェクトで発生した案件をK5 Playgroundのアプリとして作ってみて、課題を解決してUXを継続的に改善する方法を説明します。今回は「Twitterでの人気の観光スポットに関するツイートを地図上にリアルタイムに表示させて把握したい」といった要件があるとします。「人気の観光スポット」は実際の要件とは全く変えています。
1. これまでのようにK5 Playgroundにアクセスして、Mapテンプレートを選択します。
2. バックエンド編集画面を開いてAPIロジックを編集します。
SNSのメニューから"Twitter - Search Tweets"をドラッグ&ドロップします。 query
には検索ワードを入力します。
const query = '名所'; //Twitterの検索ワード
開発スピードのためにまずは任意のHTTPリクエストに対して固定ワードをTwitterで検索するAPIとします。
そして、Twitterからのレスポンスを整形するために、Customメニューから"Empty Logics"をドラッグ&ドロップします。Twitterのレスポンスで地理情報 coordinates
キーを持つものだけにフィルタリングします。
//地理情報(coordinates)付きのTweetオブジェクトにフィルタリング const tweets = results.filter(x => x.coordinates).map(x => { //Mapテンプレートが扱える形式にする return { name: x.tweet, lng: x.coordinates.coordinates[0], lat: x.coordinates.coordinates[1], original: x } }); next(tweets); //フロントエンドに渡して終了。
originalキーに元のツイートを付けてフロントエンドに改善の余地を持たせています。最適化は後回しにして、UXを改善する際に利用する可能性のある情報を渡します。K5 Playgroundで必要な手順は以上2点です。
3. アプリをダウンロードして解凍したらこれまでと同様に起動します。あらかじめTwitterのTokenを Twitter Appsから取得しておきます。
Twitterのレスポンスには地理情報がほとんど付いていない
アプリを起動させると、地図上が寂しいことに気づきます。たいていは何も表示されず、まれに1件表示される程度かと思います。原因を確かめるためにBFFのロジック backend/logics/tweetsGET.js
でTwitterのレスポンスをデバッグしてみます。
console.log("ツイート:", results.statuses.length); console.log("地理情報付きツイート:", results.statuses.filter(x=>x.coordinates).length);
ツイート: 100 地理情報付きツイート: 1
地理情報付きのツイートの比率は1%以下と極わずかでした。この結果を考慮すると、Twitterのレスポンスの coordinates
キーだけから地図系のアプリを作るには、課題があることがわかりました。アプリ開発を中断すべきでしょうか。
まだ解決策はあります。ツイート本文に注目します。ツイート本文から地理に関係する固有名詞とその緯度・経度を取得できれば解決です。緯度と経度があれば地図上に表示できるだけでなく、地理空間インデックスを持つMongoDBなど、地理情報と親和性の高いデータベースでも扱いやすくなり、データの再利用性も高まります。
これを実現するのが、富士通のAIサービス「Zinrai」です。
機械学習のAPIで課題を解決しよう
富士通は機械学習などをベースとしたAIサービスとして「Zinraiプラットフォームサービス(以下Zinrai)」を提供しています。ZinraiはWebAPIで自然言語処理・音声処理・画像処理を行う「基本API/目的別API」とGPUインスタンスでディープラーニングを行う「Zinraiディープラーニング」から構成されています。
ZinraiはFUJISTU Cloud Service K5 30日間無料トライアルから申し込むことにより、期間限定で無償利用できます。
文章から地理情報を抽出する
それでは実際に先ほどのアプリにZinraiを組み込んでいきましょう。
K5 Playgroundでは、ZinraiのAPIロジックを4種類用意しています。ここでは一般の文章の中から地理情報を抽出して住所や緯度や経度を返してくれる自然言語処理のAPIを使います。
1. 先ほどのURLにアクセスします。TwitterのレスポンスをZinraiで分析するために、右メニューのK5 Zinraiから「K5 Zinrai Natural Language Processing - Location Analysis」をドラッグしてTwitterの直下にドロップして、次のように2行修正します。
//ZinraiにTweet本文の配列を与えるようにします。 const texts = results.statuses.map(x => x.text); : //Zinraiの分析結果(body)とTwitterのレスポンスをマージして次に渡します。 next(results.statuses.map((tweet, i) => Object.assign(tweet, body[i])));
2. Empty Logicを次のように修正します。Zinraiで分析が成功したツイートのみを扱うようにします。
const tweets = results.filter(x => { return x.locations && x.locations.length > 0 }).map(x => { return { name: `${x.user.name} @${x.user.screen_name} ${x.text}`, lng: x.locations[0].longitude, lat: x.locations[0].latitude, original: x } }); next(tweets);
3. アプリをダウンロードして先ほどと同様に起動します。 config.js
にはZinraiのポータル画面で指定した id
と token
を記述します。
Zinra使用前とは異なり多くのツイートが地図上に表示されるようになったと思います。さてこれでアプリの機能自体の課題が解決しました。次からアプリのUXを改善していきます。
アプリのUXを改善する
ツイートの取得を改善する
まずはツイートを定期取得して、放置しておいても最新の検索結果が常に表示されるように改善します。そのためには、JavaScriptのタイマー処理関数 setInterval()
をFluxのActionCreatorに適用すればよいです。
//ファイル:frontrend/app/components/containers/MapContainer.js setInterval(() => { //10000ミリ秒ごとに繰り返す GeoLocationActionCreators.getLocations({ ...略... }); }, 10000);
最新ツイートだけでなく過去のツイートを全て表示するように改善します。そのためにはFluxのStoreの reduce()
のふるまいを変更します。
//ファイル:frontend/app/stores/***Store.js return { data: state.data.concat(action.data) //変更前はaction.data }
タイムライン用のReact ComponentをMaterial-UIで作る
タイムラインを表示させたいという要望があったため、タイムラインのReact ComponentをMaterial-UIを使ってさくっと作ります。
const timeline = <div> { cultural.data.map(x => <Card key={`timeline-${x.original.id}`}> <CardHeader title={x.original.user.name} subtitle={`@${x.original.user.screen_name}`} avatar={x.original.user.profile_image_url} /> <CardText>{x.original.text}</CardText> </Card>) } </div>;
作成したタイムラインは聖杯レイアウトのReact Componentで右メニューに表示させます。
<BaseLayout : right={timeline} : />
さらに、いくつかの修正をして次のようになりました。地図上にはマーカーのみを表示してすっきりさせ、マーカーにマウスオーバーした時にカードが表示されます。
ここまでAIを導入して、試行錯誤してきましたが、手の早い人であれば数10分でできるでしょう。今後はどう開発していけばよいでしょうか。
これまでトライ&エラーを繰り返しながら開発を進めてきました。「UXを改善する」と述べましたが、果たして本当に改善したと言えるのでしょうか。何を基準に「優れたUX」と判断できるのでしょうか。
優れたUXとは?
アプリケーションのUXは通常、特定の人が理論や経験に基づいて提案して、コンペやオーナーの判断などで決定されると思います。しかしながら理論や経験に基づいて提案された「優れたUX」は単なる仮説でしかありません。誰かが考えた「優れたUX」がユーザーにとっても「優れたUX」であるかどうかは一切検証されていません。検証するのに最適な手法はユーザーの行動を見ることです。
優れたUXかどうかはユーザーの行動が決める
優れたUXはユーザーの行動が指し示します。UXという仮説をユーザーに提示して、体験したユーザーの行動を分析して検証をすることで、仮説の正しさが検証されます。
その検証の道具の1つが、A/Bテストです。A/Bテストは、AとBの2変数(Variant)からなる対照実験(Controlled Experiments)のことです。
ユーザーを2つの比較群に分類し、片方のグループには部品Aを使ったアプリケーションを与え、残りのグループには部品Bを使ったアプリケーションを与えます。アプリケーションには、登録/広告クリック/詳細閲覧/SNS投稿といった目標行動が定められていて、部品Aのアプリケーションと部品Bのアプリケーションとで、目標行動に対してどのような差異が生じているかのデータを収集します。
収集したデータを分析した結果、部品Bのアプリケーションが優位に目標行動につながると検証された場合には、正式に部品Bを採用します。
こういったA/Bテストを日々繰り返してUXを改善するのが現代のサービスと言えます。
Netflix社では、製品を変更する前にA/Bテストを実施することが必須になっています。A/Bテストでよりよい結果が出たデザインや機能だけが採用されます。ビデオのタイトル画像の細微な差異までもがA/Bテストされているのです。NetflixのUXは社員の声や強い意見のみによって変更されることはなく、ユーザーの行動につながるとデータで検証された案だけが採用されます。
デザイナーや開発者が思いつきの改善を繰り返すだけでは、こうしたプロセスで開発されているサービスとは勝負になりません。同じ土俵にも立てないと言っていいでしょう。そこで次に、K5 Playgroundのアプリを使ってA/Bテストを実施する方法を紹介します。
A/Bテストと親和性の高いReact
A/Bテストでは特定箇所のみに差異があるページをユーザーに提示すると説明しました。Reactはコンポーネント志向なので、あらゆる箇所を柔軟に交換できます。
またコンポーネントが関数であることも利点です。関数としてのコンポーネントに対して、デザインや動作を外部から引数として注入できるため、1つのコンポーネントからA/Bテストのためのバリエーションを無数に作ることができます。関数型のコンポーネント志向であるReactは、A/Bテストにうってつけのフロントエンドライブラリと言えます。
さらにReact用のA/Bテストライブラリを使えば、2つのアプリケーションを作る必要はありません。1つのアプリケーションでユーザーによって部品A/Bをランダムに表示できます。
実際にK5 PlaygroundのReactのフロントエンドにA/Bテストを実装していきましょう。
ReactアプリにA/Bテストを導入する
A/Bテストを導入するためにReactのA/Bテストライブラリpushtell/react-ab-testを使います。
まずK5 Playgroundからダウンロードしたアプリの frontend
ディレクトリで react-ab-test
をインストールします。
npm install --save react-ab-test
A/Bテストを実装する
次にA/Bテストの内容を考えます。ここでは、レイアウトのA/Bテストを行うことにします。AとBは、それぞれ2ペインと3ペインのレイアウトとします。
テストしたいページを開いて react-ab-test
のモジュールを import
します。次に、AとBの表示比率を定義します。ここではA:B=50%:50%としていますが、実際は新しいUIは少数のユーザーにのみ提示します。
import { Experiment, Variant, emitter } from 'react-ab-test' //実験名称:Layout, 部品名:2pane,3pane、2paneと3paneの表示比率50:50と設定 emitter.defineVariants('Layout', ['2pane', '3pane'], [50, 50]);
AとBは次のように定義します。実験(Experiment)の中に2つの変数(Variant)を定義します。
//対照実験: 名称Layout <Experiment name='Layout'> //バリエーションA: 2ペインのレイアウト <Variant name='2pane'> <BaseLayout top={top} center={center} bottom={bottom} left={timeline} /> </Variant> //バリエーションB: 3ペインのレイアウト <Variant name='3pane'> <BaseLayout top={top} center={center} bottom={bottom} left={menu} right={timeline}/> </Variant> </Experiment>
目標行動はどう設定すればよいでしょうか。
アプリケーションをより多く使ってもらうことを目指して、ここでは目標行動を「地図上のマーカーにマウスを重ねて詳細を閲覧する」ことと定義します。レイアウトの変更で快適さが向上するとメインの機能がより多く使われるのではないか、といった仮説です。そのために、マーカー部品のマウスオーバーのイベントハンドラの中で目標達成の関数 emitter.emitWin()
を呼び出します。
: //マウスホバーのイベントハンドラ handleOnHoverMarker = (event) => { emitter.emitWin('Layout'); //Layout実験で、目標行動が達成されたことを宣言 this.setState({visible: true}); } render () { //マウスのホバーを検出 return <div onMouseOver={this.handleOnHoverMarker} ...>...</div> }
さてこれで、50%のユーザーには2ペインレイアウトが表示され、残り50%には3ペインレイアウトが表示されるようになります。
目標行動の達成はどのように検出するのでしょうか。 react-ab-test
には、A/Bテストの開始時や目標達成時に呼ばれる関数が用意されています。
//Experimentが表示された際に実行されるイベントです。 emitter.addPlayListener((experiment, variant) => { console.log(`A/Bテスト${experiment}が実行され${variant}が表示されました。`); }); //目標達成時(emitter.emitWin()実行後)に実行されます。 emitter.addWinListener((experiment, variant) => { console.log(`A/Bテスト${experiment}が実行され${variant}が表示されたユーザーが目標を達成しました。`); });
この中で、Expreimentの表示や目標行動の達成を検出できます。
A/Bテストの結果はどう分析する?
A/Bテストのデータはどのように収集して分析すればいでしょうか。
1.A/Bテストの分析環境を用意して結果を送信する
1つは自前で分析環境を用意することです。APIやDBを用意して、先ほど紹介したリスナーの中でA/Bテストのデータを送信します。
//Experimentが表示された際に実行されるイベントです。 emitter.addPlayListener((experiment, variant) => { console.log(`A/Bテスト${experiment}が実行され${variant}が表示されました。`); //ここで分析環境に送信 }); //目標達成時(emitter.emitWin()実行後)に実行されます。 emitter.addWinListener((experiment, variant) => { console.log(`A/Bテスト${experiment}が実行され${variant}が表示されたユーザーが目標を達成しました。`); //ここで分析環境に送信 });
K5 Playgroundのバックエンドに分析結果送信用のPOSTメソッドのAPIを追加して、DBのAPIロジックを使えばデータは格納できます。ただしデータを格納しても分析自体は別途自分で行わなければなりません。インフラの準備にも分析の開始にも時間がかかるようでは、十分なA/Bテストを行っている他のサービスに負けてしまうかもしれません。
2.専用のサービスで高速にA/Bテストをはじめる
そこでおすすめなのが専用のサービスを使う方法です。連載の冒頭で現代のWebアプリケーションはサービスを組み合わせて高速に作りあげることが大切であると説明しました。A/Bテストにおいてもこれは同様です。
A/Bテストが行えるサービスはいろいろあります。今回は react-ab-test
も対応しているMixpanelを使って説明します。まずは、Mixpanelにサインアップして、最初に表示されるスクリプトをコピーします。
コピーしたスクリプトを frontend/public/index.html
の <head></head>
内部にペーストします。
: <link rel="stylesheet" href="/css/style.css"> <!-- start Mixpanel -->略<!-- end Mixpanel --> //Mixpanelのスクリプト </head>
react-ab-test
のimport文に mixpanelHelper
を追加して、Mixpanelを有効化します。
//mixpanelHelperを追加 import { Experiment, Variant, emitter, mixpanelHelper } from 'react-ab-test'; mixpanelHelper.enable();
以上で完了です。3行を編集しただけです。今回は自分の環境にデータを送信しないため、先ほど追加した emitter
の addPlayListener()
や addWinListener()
は不要なので削除して構いません。
MixpanelのLive viewタブを開いてA/Bテストの状況を受信できているか確認しましょう。
受信を確認したらセグメント分析を行います。MixpanelダッシュボードのSegmentationをクリックします。
次のように「Experiment Win」を選択して、Byを「Experiment equal to」、値をフロントエンドのExperimentで設定した値とします。さらにByを追加して「Variant」と設定します。これでExpreimentを固定して、Variantを比較できるようになりました。
Showをクリックすると結果が表示されます。
2ペインレイアウトが左で、3ペインのレイアウトが右です。2ペインの時の方がアプリがよく操作されていることが判明しました。
ただし、これで2ペインがよいと即断していいわけでもありません。例えば3ペインの左メニューのUIが異なれば、違った結果になるかもしれません。
このような「大きい」A/Bの場合、まずは3ペインで左メニューのA/Bテストを十分実施して改善した上で、レイアウトのA/Bテストを実施する方が望ましいです。
次の例はマーカーの色をVariantとしたA/Bテストで、マーカーの色をAはレッド、Bはシアンとしています。このようなVariantが限定されたA/Bテストであれば、結果はより明確でしょう。
ふるまいのA/Bテスト
UIに対するA/Bテストを紹介しましたが、目に見えるUIだけがA/Bテストの対象ではありません。UIが同一でありながらイベントハンドラやAction Creatorの処理のみが異なるReact ComponentをVariantとすれば、UIには現れないサービスのふるまいに対するA/Bテストが実施できます。
登録してくれたユーザーの一部にはクーポン付きのメールを送信して、クーポンの効果を検証したいとします。まずUIが全く同一のSign Upボタンに対して、異なるイベントハンドラを割り当てます。
<Experiment name="Email-Coupon"> <Variant name="WithCoupon" > <Button title="Sign Up" onClick={this.handleSignUpA}/> </Variant> <Variant name="WithoutCoupon"> <Button title="Sign Up" onClick={this.handleSignUpB}/> </Variant> </Experiment>
イベントハンドラの片方はクーポン付きメールを送信して、もう片方はクーポン無しのメールを送信します。
handleSignUpA = () => { emitter.emitWin('Email-Coupon') ActionCreators.sendEmailWithCoupon() } handleSignUpB = () => { emitter.emitWin('Email-Coupon') ActionCreators.sendEmailWithoutCoupon() }
このようなA/Bテストの場合、結果をどのように取得すればよいでしょうか。
ユーザーの継続率を調査する
長期的な視点で分析します。同じダッシュボードで今度はRetentionタブを開きます。ここでは継続率/リピート率が分析できます。例えばある日に目標行動を一度達成したユーザーが、別の日に再び訪問しているかを表示できます。今回の場合はExperiment Winを選択してVariantを切り変えればよいでしょう。
するとクーポン有りのユーザーと無しのユーザーで継続率にどのような差異があるかを比較できます。
短時間の利用状況もわかります。左上のAddictionを選択すれば、特定の条件を満たしたユーザーが何時間アプリを使っているかが表示されます。先に紹介した2ペインのユーザーと3ペインのユーザーの、利用状況の比較は、このAddiction機能によって分析するのもよいでしょう。
専用のサービスを使うことで、高速に高機能なA/Bテストを実現できることがわかったと思います。今回はMixpanelの無償機能の範囲内で分析しましたが、有償機能を使うとさらに高度な分析が行えます。アプリケーションは作って終わりではなく、A/Bをはじめとした分析を導入し、データに基づいて継続的に改善していきましょう。
おわりに
「優れたUX」は、仮説をユーザーに提示して行動を分析することで、たどり着くものであることがわかりました。一目見て気に入ったUXが作れたとしてもそれはあくまで気に入ったデザインであって、データによる裏付けがない段階では優れたUXとまでは言えません。また、そこからUXを変更してよりよい結果が得られるのにもかかわらず、気に入ったデザインに安心して改善を怠っていれば、それは優れたUXではなくなるのです。
2015年にサンフランシスコで開催されたAPIのカンファレンスで、Programmable WebのファウンダーでAPIエコノミーの推進の立役者John Musserさんが、"An API is a journey, not a destination"(訳:APIは旅路であって、目的地ではない)とプレゼンテーションを終えていました。
UXについても同じことが言えるでしょう。さらに言うと本連載で紹介したフロントエンドのアーキテクチャや技術も目的地ではなく、長い旅路の通過点です。
さて、本連載は今回で最終回です。歴史を振り返って今現在作るべきWebアプリケーションを学び、そのスターターキットの1つとしてのK5 Playgroundの概要と、活用テクニックを紹介してきました。高速にプロトタイピングを繰り返し、A/Bテストという対照実験によって優れたUXを目指すスタート地点に立てたでしょうか。あとはみなさんが継続的に手を動かし続けるだけです。
終着点のないフロントエンドの長い旅路を、日々コードを書いてデータを分析して楽しんでみましょう。
Write the Code. Change Your Life!