2020年6月21日(日)「チャリティカンファレンス沖縄2020 Vol.1 Frontend編」というイベントで、「フロントエンド開発における課題を問い直す」というタイトルで登壇させていただきました。
今回発表資料の準備をするにあたって、まず話したいことをテキストでひたすら書いていき、全て書ききったらそれをもとにパワポのスライドに情報を圧縮するというアプローチで作っていたのですが、せっかく書いたテキストをそのまま捨てるのはもったいないので多少加筆修正した上でこちらのブログにて公開することにしました。
このイベントに参加出来なかった人にも登壇内容が届いて、なにかの参考になれば幸いです。
フロントエンド開発における課題を問い直す〜2020年6月版〜
なぜこのテーマを選んだか?
登壇すること自体は1ヶ月以上前から決まっていたのですが、登壇テーマがなかなか決まらずグズグズしていると当日のスケジュールが発表されました。 それを見てみると自分がトップバッターの一人になっていて、かつKey noteという立て付けになっているので、これはやばいぞと思いました笑
せっかくKey noteという立場をいただけたので、僕の方では特定の技術トピックについてお話するのではなく、現在のフロントエンド開発の全体像が見えるようなお話をして、それから他のセッションを聞いて深堀りをできるような形にしていきたいと思います。
では全体像をどういう視点で見ていくかという話なんですが、現在のフロントエンド開発は様々な技術が出てきており、それらはすべて開発における課題を解決するために生まれています。
僕もエンジニアとして技術を学ぶときに「この技術がおもしろそう!!→この技術はこの課題を解決するために生まれたのか〜」というテクノロジーファーストなきっかけで学ぶ事が多いのですが、本質的にはまず課題があり、その課題に対して技術でどう解決するかという、イシューファーストなアプローチを取るべきです。
そのため本セッションでは、まずフロントエンド開発における課題を改めて問い直して整理し、その課題に対していまどういう技術が解として存在するのかという話をしていきたいと思います。
課題の定義
ではフロントエンド開発における課題はこれです!という話をする前に、まず課題というのがどこから生まれるのかという話をさらっとしておきたいです。
国語辞典で「課題」と調べると以下のようにでます。
1 与える、または、与えられる題目や主題。
2 解決しなければならない問題。果たすべき仕事。
「解決しなければならない問題」というのはわかりやすい定義ですね。じゃあフロントエンド開発における解決しなければならない問題っていうのはなぜ解決しないといけないのか?
これを僕は、 「進みたい方向に進みたい速度で進みたいが、それが出来ない障害があるので、解決しないといけない」と考えています。
そう考えるとまず課題を知るにはそもそもの進みたい方向というのを知る必要があることがわかります。
フロントエンド開発の目的はユーザーにより良いUXを届けること
進みたい方向というのは、フロントエンド開発における目的になります。 フロントエンドというのは、システムとユーザーが接する部分のUIであり、その目的は良いUXを提供することになります。 じゃあ良いUXってなにか?という話になるんですが、UXの品質評価方法のひとつとして、UXハニカムという手法があります。
これはプロダクトやサービスが提供するUXの評価軸を、useful、desirable、accessible、credible、findable、usableの6つの項目に分け、それぞれの項目を5段階評価をし、その総合点でUXを評価するという手法です。
各項目は以下の様に定義されています。
1. Useful – 役に立つ 提供されるプロダクトやサービスがユーザーの役に立っているか。彼らのニーズを満たしているか。もしそれがユーザーの目的を達成していなければ、ユーザー体験としてはレベルが低い。
2. Desirable – 好ましい プロダクトの見た目や雰囲気がユーザーにとって好ましいかどうか。ここに評価軸においては”デザイン要素”はなるべく少ない方が優れているとされる。
3. Accessible – アクセスしやすい 体の不自由な方や、異なる制限のあるユーザーにとっても使いやすい体験がデザインされているかどうか。色盲の方でも認識しやすいサインなどもその例の一つ。
4. Credible – 信頼できる 企業やプロダクトが信頼できるものであるかどうか。例えば無名な企業よりも、著名なブランドの製品であれば、最初からユーザーの心理的ハードルが下がり、自ずと利用体験がよくなりがち。
5. Findable – 探しやすい 情報やコンテンツが見つけやすい。短期間でユーザーが求める情報にたどり着ければ、利用している際のストレスが下がる。サイトであればページの構造、駅や公共の建物であれば、目的の場所に辿り着きやすいなど。
6. Usable – 使いやすい そしてユーザビリティの高さ。利用していて必要以上に複雑で使いにくい場合はユーザー体験の価値が下がってしまう。例えば家電製品であれば、説明書を読まなければ使い方がわからない時点で減点対象になるであろう。
つまり、UXを最大化させることがUI/フロントエンド開発における目的であるならば、これら6つを高めようとすると発生する課題がフロントエンド開発における課題となります。
では、やっと本題です。フロントエンド開発をすると発生する課題とはなんでしょうか?
正解がわからない
最も大きな課題の一つはUXの6つの指標を高めるためにどうすれば良いという正解が誰にもわからないことです。自分たちがリリースしたプロダクトが本当にユーザーの役に立つのか、使いやすいのか、ユーザーに好んでもらえるのかということは、実際にリリースして反応を見てみるまでわかりません。
マルチデバイスへの対応
ソフトウェアのUIは、10数年前まではPCでの利用のみを対象にしていればよかったのですが、現代ではPCだけでなく、スマートフォン、タブレットの利用まで対象にしないといけません。そしてそれぞれのデバイスのOSの違いや、スペックの違い、利用シーンでのネットワーク環境の違いなど様々な状況を考慮に入れた開発をしていく必要があります。
ユーザーの期待値の増加
スマートフォンの普及によって、一般の人もアプリという形でソフトウェアを日常で使うことが普通なことになってきました。そしてみんなが使っているものといえば、instagram、Tiktok、Netflixなど世界No1を取ったものばかりです。その世界トップレベルに洗練されたプロダクトを日常で使い目が肥えたユーザーに、それよりも劣るUXを提供してしまってはなかなか使ってもらう、使い続けてもらうに至るのは難しいでしょう。 つまりUXにおいては、資本力においてはるかに劣っていながらも、必然的に世界トップレベルのサービスの基準と戦うことが宿命付けられています。 またスマホアプリと接する時間が増えることで、スマホでWebアプリを使う時に関してもスマホアプリと同等のUXが求められるようになってきました。
正解がわからないという課題に立ち向かう手段
正解がわからないという課題に対して立ち向かうには基本的には「早く小さくリリースして、フィードバックを得て、そのフィードバックをもとにまた早く小さくリリースする」を繰り返すしかありません。これがいま良く言われているアジャイル開発で行われていることでもあります。 では、みんながアジャイル開発をしている時代においてチーム力、開発力として差がでるところは以下の点になります。
- 単位時間あたりの試行回数を増やせるか?
- データドリブンな意思決定を行う
単位時間あたりの試行回数を増やす
- 限られた人数での開発効率を上げる
- コード量が増えても開発効率を落とさないようにする
- 人数が増えても開発効率を落とさないようにする
データドリブンな意思決定を行う
- ユーザーの利用動向に関するデータ収集
- データの可視化
では、上記を行うためにいまどのような技術がソリューションとして存在するのか?
Firebaseはフロントエンドエンジニアの必修項目のひとつ
「単位時間あたりの試行回数を増やす」、「データドリブンな意思決定を行う」のための手段を総合的に、かつ非常に低価格、そして簡単な方法で提供しているサービスは僕の知る限りFirebaseにおいてありません。
Firebaseのすべての機能を必ず使う必要はありませんが、Firebaseで何が出来て何ができないのかを把握して戦略を持って利用することができれば大きな武器になる、というよりもむしろ戦略を持って利用できないために不要なコストを書けて開発しなくてもよいものに開発リソースをかけてしまい競合に遅れを取るということにことになりかねません。
単位時間あたりの試行回数を増やすことにどう貢献するのか?
「単位時間あたりの試行回数を増やす」ために、限られた人数での開発効率をあげる、コード量が増えても開発効率を落とさない、人数が増えても開発効率を落とさない、という3つの点が重要です。
この3つの課題を同時に解消するための手段は、「自分たちで作らない部分を増やすこと」です。
Firebase AuthenticationというFirebaseが提供する認証サービスを例に考えてみましょう。
ユーザー認証の部分を自分たちでスクラッチで作らずにFirebase Authenticationを使うという戦略を取れば、その開発コストが大幅に減少し、その分他のアプリ固有の価値を生む機能や改善に開発リソースを使うことができ、それによって開発効率が向上します。
認証機能を自分たちで全て書くよりもFirebase Authenticationを使う方がコード量ははるかに小さくなるため、開発効率を落とさないで済むことができます。
人数が増えると仕様のキャッチアップをするためのコミュケーションコスト増加によって開発効率が落ちていくのですが、自前で作らずFirebase Authenticationを使う選択をしたことで仕様のキャッチアップはFirebase自体のドキュメントを読むだけで完了します。
もちろん外部サービスを利用する場合はそのサービスで出来ないことは出来ないこと、そしてそのサービスの提供が終了してしまうリスクなども考慮した上で利用を検討するべきですが、自前で作らずに既存のサービスを利用することで作らないモノを増やすことは開発効率を上げるための強力な手段になります。
Firebase Authenticationは認証というアプリ必要な単機能を提供してくれるものですが、もっと大胆に「作らない部分を増やす」ことに貢献するFirebaseのサービスがCloud Firestoreです。
Cloud Firestoreはクラウド上でホスティングされたNoSQLデータベースですが、iOSアプリ、Androidアプリ、WebのフロントエンドなどのクライアントからSDK経由で直接アクセスできるという特徴があります。
さらにリアルタイムアップデートという機能によって各デバイスから更新されたデータがリアルタイムで各デバイスに共有される機能、オフラインサポート、スケーラビリティの高さという特徴もかねそ兼ね備えています。
コレをうまく活用すれば、いままで単純なデータの読み書きだけのために行っていたサーバーサイドのAPI開発やデータ同期のためのプログラム、そしてスケーラビリティ確保のためのインフラ運用などの大部分を省く事が可能になります。
こちらも利用することによるトレードオフを見越した上で戦略を立てるべきですので、開発効率を上げるための自分の手札として何が出来て何ができないのかをしっかりとキャッチアップしておくべきでしょう。
データドリブンな意思決定を行うことにいかに貢献するのか?
開発効率をどれだけ高める事ができても、提供した機能がユーザーの体験価値をちゃんと高めたのか、またいま提供しているプロダクトのどこがユーザーの体験価値を毀損しているのかを把握することが出来なければ、正しい方向にプロダクトを改善していくことができません。
正しい方向にプロダクトを改善していくには、「ユーザーの利用動向をデータとして取得する」「取得したデータを可視化する」という2つが非常に重要です。
この点においてもFirebaseはGoogle Analyticsという強力なツールを提供しています。
Google AnalyticsのSDKを入れるだけで、自分たちのアプリをどれくらいのユーザーが利用しているのか、どこで利用しているのか、どれくらいの時間利用しているのかなどがわかります。
より詳細なことが知りたい、たとえばどの画面を開いたのか、どのボタンを押したかなどを知りたければイベントを発火するコードを書くだけでクラウド上に記録されていきます。
そしてそれらのデータがGoogle Anaticsのダッシュボード上で可視化されるので、そのダッシュボード上で自分たちのアプリが正しく改善されているのかを客観的な事実であるデータで知る事ができます。
同じ事ができるツールは他にもありますがそれらは有償であることや、Firebaseの場合他にもパフォーマンス監視やクラッシュレポートに関する機能なども提供されかつ無料であることを考えても、デフォルトで取る選択肢としてFirebaseのGoogle Analyticsを利用することが多いでしょう。
「単位時間あたりの試行回数を増やす」、「データドリブンな意思決定をする」をするための強力な技術の一つとしてFirebaseがあるのは間違いないため、これからフロントエンド開発に関わる上でほぼ必須科目に近いぐらい身に付けないといけないモノだと考えています。
マルチデバイスへの対応という課題に立ち向かう手段
マルチデバイスへの対応というのはまず2つに分類することができます。
今回は「Webのフロントエンドをマルチデバイス対応すること」は、僕の理解だとどこもそこまで大きなインパクトのある差異はなくて「レスポンシブ対応していこうな」というところだと思っています。
各デバイスのネイティブアプリを提供するかどうかという点は、今回はわかりやすくするため以下の3つのプラットフォーム上に、それぞれ用のフロントエンド(モバイルの場合はネイティブアプリ)の提供をどのようにするのか?という風に考えます。
これは「それぞれのプラットフォーム上のアプリ同士でどのくらいコードを共有するのか?」を考えることです。
単純に考えると、すべてのデバイスにそれぞれのプラットフォーム用のプログラミング言語(AndroidをJava、iOSをSwift、WebをJS)でフロントエンドを書くことにした場合、すべてのプラットフォーム用に共通コードを使うよりも開発リソースが必要になります。
しかし、コードをすべて共通化した場合には各プラットフォーム固有の処理を書かなければならない場合にはコードを分離して書かなければならないためコードの複雑性は高くなる傾向があります。またプラットフォーム間のコード共通化を支援しているフレームワークが、各プラットフォームのアップデートにどれだけついていけるのか?という課題もあります。
これらのトレードオフを見極めた上で技術選択をしていく必要がありますが、ではこの課題に向き合うための技術はなにがあるでしょうか?
Flutter、iOSとAndroidのコード共通化の解のひとつ
FlutterはGoogleが提供しているiOSとAndroidアプリをDartという言語で、共通のプログラムで書く事ができる技術の一つです。
ただし共通化できるのはあくまでUIとビジネスロジックなどプラットフォームに依存しないコードの部分であり、カメラなどの機能はプラットフォーム固有のコードをプラグインという形で書く必要があります。ただ良く使う機能のほとんどはOSSのプラグインが提供されているので凝ったことをしないのであればそれをインストールして使うことで解決するでしょう。
今回のカンファレンスでは登壇者がいませんが、iOSとAndroidでコードを共通化するというアプローチを取っている技術はFlutter以外にもあります。
・Xamarin ・React Native ・ionic(cordova)
Xamarin
XamarinはMicrosoftが提供するフレームワークで、C#という言語を使ってiOSとAndroidアプリを開発することができます。先日公開されたCOVID-19の接触確認アプリも、実はXamarinで作られています。もしインストールしていない方がいればぜひインストールしてみてください。
XamarinとFlutterの違いとして特徴的なのが、Flutterは「UIとロジックの共通化が目的」なのに対して、Xamarinは「ロジックの共通化が目的、だけどUIも共通化をしたければできる」という違いです。
Xamarinは、AnroidやiOSの各プラットフォーム固有のAPIとC#のAPIで1対1のマッピングを行っているので、C#から直接プラットフォーム固有の機能を使うことができます。 Xamarin.Formというライブラリを使うとiOSとAndroidで共通コードを使ってUIを作ることもできますが、全く別々のコードを使いつつロジックだけ共通化するというアプローチも取れます。
React Native
こちらはReact.jsというWeb用のViewライブラリをiOSとAndroidでも同じ思想で使えるようにしたものです。こちらはJSを使ってiOSとAndroidのUIとロジックを共通化して作ることができます。
JSでUIを書くにも関わらず実際に実行されているのはネイティブのUIコンポーネントなので、ネイティブアプリ水準のパフォーマンスが期待できます。
FlutterやXamarinとの違いは、すでにウェブのフロントエンドを作るのと同じReact.jsとJavaScriptをネイティブアプリの開発利用できるようにすることで、Webのフロントエンド開発者を、学習コストを減らした形でモバイルのネイティブアプリの開発者として転用可能にしている点です。
ionic(cordova)
ionicはFlutter、Xamarin、React Nativeと違い、JS/HTML/CSSというWebのフロントエンド技術を使ってアプリを作り、それをモバイルアプリのWebView上で動かすというアプローチを取っています。
これはiOS、Androidでコードを共通化するのはもちろんのこと、Webフロントエンドともコードを共通化することが技術的には可能になります。(Webと共通化する場合ネイティブの機能を提供するPluginをどううまく扱うか、またWebとモバイルアプリで製品のライフサイクルが違うなどの課題はある)
そのトレードオフとして、パフォーマンスやUXの面でネイティブアプリ同等のものを提供する難易度が高い、プラットフォーム固有の機能を使う場合はpluginという形で別でコードを書かなければならないなどの課題も出てきます。
このようにAndroidとiOSのコードを共通化してマルチデバイス対応を開発効率を上げながら行うための技術はいくつかあります。 各ツールのメリットデメリットを理解しつつ、共通化するのか別々に作るのか、また共通化するならどの部分を共通化するためにどのツールを使うのかを考えて選択していく必要があります。
ネイティブアプリを使わずにWebアプリをモバイルアプリのように提供する方法としてのPWA
FlutterやXamarinなどはあくまでAndroidとiOSでコードを共有してモバイルアプリを開発・提供するための技術ですが、あくまでウェブアプリとは別のコードを書く必要があります。
そこに対して「ウェブアプリ自体をモバイルアプリのように動かせるようにしたらいいじゃないか」というアプローチを取っているのがPWA(Progressive Web App)の技術になります。
PWAとはReactやFlutterのような特定のフレームワーク、ライブラリのことではなく、ブラウザに搭載されている機能の組み合わせによってモバイルアプリと同等の機能を実現するためのデザインパターンのようなものです。
ここでいう「モバイルアプリ同等の機能」とは、以下になります。
- インストール可能(ホーム画面にアイコン設置、そのアイコンをタップして起動できる)
- ネットワーク非依存(ネットがなくてもローカルにデータ保存して動かすことができる)
- 再エンゲージ可能(プッシュ通知など)
モバイルデバイスに大きく依存した機能(カメラやGPS、ARなど)が必要なアプリについてはPWAのアプローチは取れないですが、Webで提供している機能をモバイルでも提供したいだけの場合、PWAのアプローチを取ることでモバイルアプリの開発リソースを全く使わずに上記3つの要件を実現することが可能になります。
ユーザーの期待値の増加に立ち向かう手段として生まれたSPA
スマホネイティブアプリと同等のUXをWebのフロントエンドでも実現するために考え出されたのがSPA(Single Page Application)です。
皆さんご存知だと思いますが、SPAは1ページのHTMLをJSで動的に書きながら画面描画をする技術のことです。
いままでは画面遷移やユーザーのアクションのたびに画面を全部リロードしないといけなかったものを、必要なデータだけをサーバーと通信し画面描画はリロードせずに行えるようにすることでスマホアプリのようなUXを実現可能にしました。
SPAによってネイティブアプリとUXで戦える土台を手に入れましたが、それによってまた新たな多くの課題を生み出しました。それが以下になります。
- JSでより多くを担当することによるプログラムの複雑性が増加
- 初回表示パフォーマンス悪化とGoogleインデックスが遅い問題
プログラムの複雑性の増加
画面描画処理の複雑性とパフォーマンス改善
SPA以前は入力フォームのバリデーションや少しのAJaxやHTMLの動的変更程度だったJavaScriptの役割が、SPAによって非常に多くのことを担当せざる得なくなり、それによってコード量の増加、複雑性の増加が激しくなりました。
そのうち最も大きな比重を占めるのが、アプリケーションの現在の状態をもとに画面を正しく描画する、DOMを変更するというプログラムを書くことです。
この処理を、メンテナンスしやすく、簡単に、かつパフォーマンス高く解決しようと生まれたのがReact.jsであり、Vue.jsであり、Angular(js)になります。
不具合発生率を言語レベルで減らすための策としてのTypeScript
SPAにすることで画面描画処理の他にも非常に多くのロジックがプログラムに入ってきてコード量がふえてきます。コード量が増えてくると適切に設計されていなければ依存がどんどん増えていき、一つの変更によって想定外の場所に影響が及びバグが発生してしまう可能性も高くなります。
バグをリリース前に見つけるにはテストが必要ですが、手動テストでは時間が非常にかかってしまう上に漏れの可能性もあります。そのため本来的には自動テストを書くべきではあるのですが、UIに関するテストはテストプログラムを書く難易度が高い上にUIの変更頻度も多いため書いたプログラムが無駄になることも多々あります。
このトレードオフに関するリーズナブルな解としてTypeScriptが利用されるケースが増えています。
TypeScriptはJavaScriptにトランスパイルすることができる言語ですが、JavaScriptに型という言語仕様をつけているのが一番の特徴です。
関数や変数で受け取れる値の型を明示することで、間違った型の値が入った場合にはコンパイル時にエラーを検出してくれます。
これにより、値の形式(型)違いによるバグの発生を事前に防ぐ事ができる上に、不要になったコードの削除や簡単なプログラムの変更などをコンパイルが通ればある程度は動くという安心感を得られるようになりました。
もちろんテストを置き換えるものではありませんが、コードや開発に関わる人が増えていったときにも開発効率を下げないための手段としてTypeScriptはJavaScriptをそのまま使うよりは支持されているように思えます。
初回表示パフォーマンス悪化とGoogleインデックスが遅い問題
SPAによって、Webアプリを利用中のUX/パフォーマンスは以前と比べて大きく改善可能になりましたが、逆に初回表示時のサーバーからレスポンスを受け取ってから画面が描画されアプリを利用できるようになるまでのパフォーマンスの悪化が問題視されるようになりました。
SPA以前の場合はサーバー側でHTMLを描画しているため、そのレスポンスをブラウザが受け取って描画するまでの時間は微微たるものでした。
しかしSPAになるとサーバーからのレスポンスは早くても、そのレスポンスをブラウザが受け取って画面を描画するためにはまずJavaScriptをすべて読みこんで解釈し、それを実行し、それからAjaxで画面描画のためのデータを取得とし、最終的に画面が描画されるため初回表示時の待ち時間が明らかに悪化していました。
また、Newsなどのメディアサイトの場合Google検索からの流入が非常に多いため、Googleに正しくそして早くインデックスされることがビジネス上非常に重要なファクターになるのですが、GoogleのクローラーはJSの解釈は行うためSPAで作ったサイトでもインデックス自体はされるものの、JSの解釈をしてインデックスをされるまでの時間が通常のサイトよりも遅くなる傾向があるため、この問題も解決する必要がありました。
初回表示パフォーマンス悪化とGoogleインデックス遅い問題を両方解決するためのアプローチとして生まれたのがSSR(サーバーサイドレンダリング)という技術です。
SSRとは、SPAの場合はクライアントサイドで描画していたHTMLを、初回表示の場合のみサーバー側で描画しつつ、クライアントでJSが実行された際には現在の状態をHTMLから読み取れるようにデータを埋め込んでおく手法です。
これにより初回表示の場合はJSの実行またずに画面描画されつつ次のユーザーのアクションからはSPAとしてJSで画面描画を行うことを可能にしました。 初回表示をサーバーサイドでHTML描画できるようにしたことで、GoogleクローラもJSの実行なしにコンテンツを読み取れ、早くインデックスされることを可能にしました。
しかしこのSSR、フロントエンド用に書いてあるJSをサーバーサイドのnodejsで実行できるようにしつつ状態の受け渡しをスムーズに行う必要があるため、0からコレを作るのは非常に手間のかかるものでした。
それをSPAでよく使われるViewライブラリ毎にフレームワークとして解決したのが、Vue.jsのNuxt.jsであり、React.jsのNext.jsです。ちなみにAngularの方もAngular UniveralというAngularに同梱されているライブラリでSSRの導入をできるようにしています。
さいごに
この7〜8年のフロントエンドに関わる技術の発展は凄まじく、いろんな技術が生まれては消えていきます。しかし、それらの技術はいずれも特定の課題に対する解決策として生まれています。
ただいま流行っている技術を追うだけでなく、そもそもいまフロントエンド開発に起こっているトップレベルの課題からブレイクダウンして課題を具体化し、それらの解決策としていま注目されている技術はどのようにマッピングされているのかを見ていくと、自分たちのプロダクトや組織にとって必要な技術がなんなのかが見えてくると思います。
この発表で、いまのフロントエンド開発のなんとなく全体像が見えてくる、かつ、課題から考えるということの重要性を理解することが出来たら良いなと思っています。
長々とお付き合いいただきありがとうございました。