Angularでの開発を快適に進めるために知っておきたいこと
この記事は Angular #2 Advent Calendar 2019 3日目の記事です。
こんにちは、奥野賢太郎( @okunokentaro )です。今回は同日に公開された記事『フロントエンドフレームワーク選定前に知っておくべき Angular の 6 つの問題点と、それでも Angular を選ぶ理由』のアンサー記事として、筆者の考える問題点の克服方法、または誤解の解消、および自分がAngularを選ぶ理由を述べようかと思います。
特定のAPIの便利な使い方…とかよりは、もうちょっと視座の高い話になっています。また本稿内にてReactの言及が多いことについては、アンサー元の記事がReactを推しておられることに由来しており、それらの節で言及していない各フレームワークに対する他意はございません。
要約
- 大規模になること自体なるべく避けるべきである
- 筆者はどうしてAngularを選んだのか、個人の利得よりチーム全体の利得を優先した結果だった
- Angularを使う際の、周辺技術や記述操作についての筆者の考え方と取り組みについて述べた
- 結論において、いずれのフレームワークに対しても、おすすめするかどうかの言及はしていない
誰?
まず最初に自分のポジションを明らかにしておきますと、筆者はAngular日本ユーザー会というコミュニティの副オーガナイザを務めています。過去4年間で3社のAngularの大規模案件を担当した他、Angular, Reactでの大小いくつかの案件を担当しました。
現在はフリーランスとしてAngularアプリケーションの設計や、フロントエンド・アプリケーション・アーキテクチャそのものについてコンサルティングやリードデベロッパーの立場をとっています。
そのため業務経験としてはAngularJS, Angular, Reactがある他、市場の調査の目的で趣味プロ程度ではありますが、Nuxt.js (Vue.js)を経験しています。
大規模に向いたフレームワークとは何か
大規模の是非
そもそもAngularが大規模に向いているのか向いていないのか、という話をする前に、フロントエンド・アプリケーションとして大規模になるのがよいのかという議論をすべきかなと思っています。
そして、その答えとして筆者は「そもそも、大規模になってしまうのはよくないことである」と結論付けています。
アンサー元の記事でもこの拙資料が引用されていたのですが、この資料でも述べたように、大規模になってしまうことによる弊害は大きいです。例えばCircleCIの処理の遅さ、コンパイルの待ち時間、lintツールの待ち時間…などなど、あらゆる処理時間がファイル数に比例して遅くなっていくのは間違いないです。
よく勘違いされるのですが、提供しようとしているユースケースの大きさとアプリケーションの規模の多さは、イコールで扱うべきではないです。例えば、極端な例ですが、200画面の1アプリケーションを作るという要件があったとき、慎重に精査したら5〜10画面持つアプリケーションを20個ほど作ればよかった、みたいなこともあります。
そのとき「じゃあ共通コードはどう管理するのだ」といった議論も頻繁に起こりますが、小規模のアプリケーションを複数抱えるときの問題は、解決方法の性質が異なるため一旦触れずに、大規模アプリケーションには弊害が伴うという点のみ述べます。
つまり「Angularは大規模に向いているかどうか」ではなく、AngularであってもReactであっても、大規模になりすぎない方がよりよいです。
なぜAngularは大規模に向いていると解釈されるのか
Angularに関する勉強会やカンファレンスイベントで、筆者は頻繁に「大規模アプリケーション」と「Angular」の語を何らかの組み合わせで足し合わせていますので、その事例から「大規模といえばAngular」という解釈をされた方々がおられた可能性は考えられます。
こちらが、この4年間で筆者が登壇してきた勉強会での、大規模Angular案件、あるいは大規模案件そのものについて言及している資料です。
- 2016
- ngUpgradeと移植戦略
- 2017
- 2018
- 2019
これらの登壇をご覧になった方々に「ああAngularは大規模に向いているんだな」と感じてもらえたのであれば、それは本意である一方、筆者は根本的に「大規模アプリケーション」になってしまうことを是としていません。なるべく小さくする方法があるのであれば、そうするべきと考えています。(これは記事執筆時点での見解です。過去の筆者の意見と異なっていた場合はご容赦ください。)
しかし世の中には大規模にせざるを得ない状況があります。正しさを追い求められない、政治的、金銭的な事情、外的要因、人的リソース…さまざまな要因で「大規模にしてくれスマン」といった状況に追い込まれることは、ままあります。そういった大規模化を覚悟せねばならない際に、なぜ自分であればAngularを選択するのか、それについて話します。
100点か50点か、それより全員80点を
前に、お手伝いしている会社でAngularに特化した社内勉強会を開いたことがあります。その時はAngularをご存じない方々のための内容だったので「そもそもなぜ御社がAngularを選択しているのか」といった点の理解を深めてもらう目的でした。
そこで筆者が述べたのが節題にもある言葉です。Reactを使えば100点の実装ができるかもしれません。ただ、Reactではライブラリをすべて自分で技術選定し、組み合わせてアプリケーション全体を構築していきます。ページ遷移や通信や、シングルトン・ストアのひとつひとつ、なんならそれらに付随するミドルウェアも、自分で選んでいく世界観です。ここに自信のあるデベロッパーは、ぜひその自信を活かして100点のReact世界観を構築するとよいと、筆者は思っています。
一方でAngularは全員80点です。100点からしたら下がっていますね。
ですが、Reactを使うと50点や20点になってしまうデベロッパーに対しても、全員を80点まで引き上げる力をAngularは持っています。Angularはフレームワークですので、まず技術選定というラインで均質に仕上げます。ページ遷移のためのルーターも、通信のためのHttpClient
も備わっています。シングルトン・ストアにはデファクトのNgRxがあります。これらの選定で悩む必要がありません。
これはメリットとも、デメリットとも捉えられます。先にも書いたように、ここに自信がある方はその自信を活かしてほしいと思います。しかし、いまから初めてウェブ・アプリケーションを開発してみたい、あるいは開発しなければならない…という方々にとってはどうでしょう。「さあここからすべて一人で選べ」という世界観は彼らを露頭に迷わせることになります。20点を叩き出すかもしれません。
それをAngularは80点まで引き上げてくれるのです。Angularを初めて触って、慣れたことで自信をつけたらReactのライブラリ選定に興味が湧くかもしれませんし、それでもよいのです。
チーム開発をする場合、20点や50点の不安があるデベロッパーが80点まで引き上げられる一方、100点のデベロッパーまで80点に下げられてしまいます。これは一見デメリットのようにも見えます。
しかし筆者はそうではないと思っています。チーム開発を長く続けてきた筆者としては、個々のスキルを最大限活かした上で、チーム全体の平均点をいかに上げるかが重要になってくると考えています。
Angularを使うことで技術選定の楽しみや利点は薄くなるかもしれません。Angularはフレームワークですので、基本的にはAngularが提供するAPIを使うことになります。しかしチーム全体の平均点を上げられるのであれば、それでよいかなと思います。
足かせとしてのAngular
もう一つ、足かせとしてのAngularの側面として、あまり凝ったことをしにくいようにできています。
例えば、Angularではhtmlとcssとtsを組み合わせた、シンプルなコンポーネントを毎回定義していきます。「Higher Order Componentはないのか」「html + cssよりtsx + CSS in JSのほうがよいのではないか」その意見は分かります。
ですが、Angularは他人に説明するとき「この表示についての実装は、このtsを見てください。それがブラウザ上でどうなるかは、このhtmlを見てください。そのときのデザインはcssに宣言があります」といったように、それぞれのファイルごとの責務に応じた説明が可能です。
この構造は、デベロッパー・デザイナーでの分業や、社内複数案件のコンテキストスイッチの切り替えの観点で特に役に立ちます。全社的にAngularを採用し、プロダクトを増やしていった場合に「同じ会社なのに別案件のことはすべてキャッチアップし直し」といったことが起こらないのです。即戦力、即80点です。
ReactのCSS in JSやtsxを非難する気は全くなく、自分もあれは便利だと思うので、趣味プロで思う存分書いた経験はあります。ですが、あらゆるデベロッパーのReact案件コードを読ませてもらって思ったものとして「この案件のReactではこうなっている。あっちの案件のReactでは別のああなっている」といった事象がとても多かった記憶があります。同じ社内のReact案件でも流行り廃りがあるとすら聞きました。
Angularはこれを防いでいます。Angular案件は、少なくともここ3年大きな変化がありません。あるとすればNgRxがデファクトになった、くらいでしょうか。なのでもちろんあまりにも古いAngularと現在のAngularに乖離がないのかといえばありますが、Angular動向そのものをキャッチアップすれば、それは変化について行けているということです。すべての些末なライブラリをすべてキャッチアップする必要はありません。
このようにAngularを選択する意義には、組織やチームのことを考えた結果といったこともあるのです。そして、チームが大きくなる案件ほどプロジェクト自体が大規模になりやすい傾向はありますので、そこからAngularは大規模に向いているという論説が導かれるのであれば、それは筆者は納得できます。
(191204追記)公開後に言及が多いので触れておきますが、20点や、もっというと0点かもしれない人たちが、何もせずにAngularを使えばいきなり80点になるという誤解がありそうなので釈明します。
Angularを使った場合「Angularの作法通りこのように書くとよい」というコードレビューが周知しやすく、それによって知識差があったとしてもリードデベロッパーがチーム全体の平均点を80点まで引き上げやすいという意図で書きました。残念ながらまわりに、Angularについて詳しい人が誰もいない場合にいきなり0点から80点になるかというと、そういうわけではなく、点数を積み重ねる学習は必要になってきます。これを学習コストの高さと捉えられることもあれば、ウェブ開発を進める上での必要最低限が揃っていると捉えることもできると思います。
ここは言葉足らずだったなと感じていますので、あえて追記させていただきました。
Angularの新陳代謝
バージョンアップは困難か?簡単か?
先に答えを言いましょう、簡単です。ただし、それは今のAngularの話です。
たしかにAngularJSからAngular v2.0の互換性は断ち切られました。AngularJSからAngularに移行せずVue.jsやReactに移行した人々をたくさん知っています。ここで互換性が断ち切られたことは事実ですし、断ち切られてすぐにマイグレーション用のライブラリの完成度が満点だったかというと、そんなことはなく、まだまだ使いにくいものでした。なので、悪印象を抱かれてもやむを得ないと思います。
Angular 2から4、4から5も大変でした。筆者も経験していますし、大変だった記憶が蘇ります。しかしAngular 6から導入されたAngular CLIの ng update
がすべてを変えました。Angular 9のリリースが間近な今、Angularをお使いであればAngular CLIを信用すればバージョンアップへの追従は心配要りません。
事例ですが、つい最近筆者はAngular 4案件を8に引き上げる仕事をしました。Angular CLIの ng update
が導入されるまでの4, 5, 6間のバージョンアップは、慣れてはいたものの作業数は多かったです。ところが ng update
導入後の6, 7, 8間のバージョンアップについては、ほぼ一瞬でした。
その内容についてはこちらの資料に詳しいです。
バージョンアップ・サイクル
バージョンアップのサイクルが6ヶ月である点、これは筆者は「ウェブの早さ」と捉えています。
Chromeは6週間ごと、Firefoxは4週間ごとにあたらしいバージョンがリリースされます。セキュリティの修正やブラウザ自体の機能改善だけでなく、あたらしいAPI Specの実装、言語処理エンジンの新構文への対応といった、各種WebのAPIについての仕様のドラフトに対する実装が、頻繁に繰り返されるためです。
Angularはこれらのサイクルに取り残されない、かつ、キリよくアナウンスのしやすい半年というリリースサイクルを選択しました。これはAngularの新陳代謝です。1年、2年変化のないフレームワークは、むしろこのウェブの早さに置いていかれてしまうのです。
そして、このウェブの早さに慣れることこそが、ウェブを生業にする者の宿命だと思っています。
Google か? Facebook か?
これもよくAngular, Reactの比較で論じられます。正直、これは起こり得る議論だと思います。そして「〇〇〇はすぐ破壊的変更やディスコンを入れる、いやいや△△△だってそうだろう」といったbikeshedな議論もこれまでに散々見てきました。
これについては事例を踏まえるしかないと思っています。Reactの事例についてはReactの専門家に任すとして、AngularがGoogleでどのように使われているかを紹介します。
こちらは2019年のAngular ConnectでのKeynoteの資料です。この例にもあるように、Google社内では1500件以上のプロジェクトでAngularが採用されています。これは公開先が社内外問わずです。有名な事例としては、Firebaseの管理画面はAngularで開発されています。
毎年数を増やし、Google社内のみで1500件以上の事例となったAngularを、Googleがどこかで急に辞めると言い出すかどうか?その判断を我々はせねばなりません。そこで筆者は「しない」あるいは「するとしても長いマイグレーションパスを用意する」と推測しています。
逆にこれが信用ならないから他へ行く、ということも有り得るとは思います。ただし、どんなフレームワーク、ライブラリも、遅かれ早かれいつかはなんらかの変化を迎えます。
余談ですが、最終的には若干宗教めいた議論になるのは避けられないかなと思います。それでいうと、私はGoogleが好きです。好みでフレームワークを選ぶことも、ひとつの選び方でしょう。
より快適に開発するために
IDEを使いこなす
筆者は、コードファイルはセマンティックに、かつ責務がファイルごとにひとつであるべきと確信しています。これはSOLID原則を筆頭とした歴史がすでに証明していますが、単純に行数の多いファイルを扱うと、人間の集中力では扱いきれません。これは10,000行を超えるjsファイルを業務で扱った経験からの反省です。
このことから、編集利便のためにファイルをひとつにまとめることを、筆者は賛同しません。ファイルは常に単一責務の粒度でセマンティックにあるべきで、コピペを楽したいのであればIDEのタブを増やし並べて表示させ、検索したいのであればマルチファイル検索の機能を習熟すべきと筆者は述べます。
先述の通り、Angularはチーム全体の平均点を80点に揃えるといいましたが、そのチームメンバーのエディタ選定やエディタ習熟については差が生じやすいところであると、経験的に思っています。
セマンティックさを破壊してファイルをひとつにまとめるより、コードとしてセマンティックであるほうが、将来的な保守性は高く、またエディタに習熟することによってあらゆる作業は高速化されます。エディタ論争を始めると戦争が起こってしまいますが、各人お気に入りのエディタをより効率よく扱う工夫をするのも、業務に役立つことと思います。
ここまでは抽象的すぎるので筆者の例を紹介しますと、筆者はWebStormを使い、かなりの数のAngularのためのスニペットを持っています。Angularは記述量が多い…という意見を聞くこともありますが、筆者からすれば数文字いれてtabキーを押すだけなので、なんの不満も持っていません。これは他のフレームワークにも言えることで、コードスニペットは積極的に活用し、ネタ帳のように温めておくとよいと思います。
また、WebStorm特有なのですがAngularのコンポーネントの一括的なリファクタリングにも対応しています。これはWebStormの開発チームのブログを読む限り、こういった機能への熱の入れ方が高く、これがVSCに対するセールスポイントなのだろうと思います。
また、 ng generate
というAngular CLIのコマンドによって、ある程度はscaffoldの生成が可能なので、この機能を活用したり、この機能を拡張するツールを自作するのもよいでしょう。
依存性注入とテスト
筆者と依存性注入との付き合いも5年になりました。いまやDIの概念なくテスト可用性の高いコードを書くのは困難とすら思っています。
Angularの世界観の大部分をつかさどる依存性注入、constructor injectionは大きな特徴でもあります。これはNgModule
, TestBed
への理解が必要になります。
Angularでは、NgModule
という単位で、アプリケーション内で使われるサービスやコンポーネントの登録をする必要があります。近年のAngularでは@Injectable(providedIn: 'root')
というAPIが実装されたことによって、ある程度簡略化できるようになりました。このNgModule
という粒度はSPAでのページ遷移時の遅延ローディング (lazy loading) のときの粒度になるほか、サードパーティライブラリの提供する粒度としても機能します。これはCommonJSやES Moduleの世界観と異なる概念で、ES ModuleとNgModule
は両立される概念です。
また、AngularのテストではTestBed
というAPIを扱います。この特徴としてテスト用のNgModule
をテストコード内で定義・作成することが可能となっています。ひとつのファイルだけプロダクションコードを使い、残りはすべてモックを使うといったテスト設計がしやすいのは、このTestBed
のおかげ、ひいてはNgModule
のおかげといえます。
NgModule
はまさに「DIコンテナー」を体現するAPIだと言えますが、テストコード自体を書きやすくするものではありません。テストの書きやすさはJasmineやJestといったテストフレームワークに担保されるべき部分で、その中でモックの宣言およびインスタンスの取得がやりやすくなるに過ぎないです。
周辺ライブラリや言語環境について
TypeScript
AngularはTypeScriptの最新版に追従しません。これはAngularのリリースサイクルが半年と定まっていることに起因します。ただしAngularのマイナーバージョンアップでTypeScriptの依存が引き上げられることは事例としてあります。
これについては技術選定と同じ話で、自由なバージョン選択をしたいか、Angularに委ねるかといった問題と同様になってきます。
新しすぎる言語も、一方で問題は起こりやすく、たとえばprettierやeslintの対応が即座になされないといった事実も確認されているため、一概に最新版に飛びつくのがよいかというと筆者はそうでもないと思っています。そのため「Angularを技術選択した以上、そのバージョンに追従する」というポリシーを持っていて、TypeScript 3.7が書きたい場合はAngular以外の案件(例えばNode.js案件やReact案件など)で導入します。
RxJS
RxJSについてもよく言及を見かけます。RxJSは難しい、と。たしかにそうかもしれませんし、Angularが必須ライブラリとしている以上、覚えなければならないという観念に迫られることも頷けます。ですが、これについては答えはシンプルで「使わなければいい」と思っています。
HttpClient
のレスポンスのObservable
はすでにhasCompleted
フラグがtrue
となっているため、.toPromise()
を付けてPromiseに変換することができます。ここから先、ずっとRxJSを使わずaxiosと同じようにPromiseを扱ってアプリケーションを開発することが可能です。ストリームを扱うことでチーム全体が混沌となりそうであれば、Promise主体にしていく方法もよいと思います。Angularはこれを拒みません。
一方でせっかくRxJS主体なのだから恩恵を受けたいが、やはりオペレータは多すぎる…といった意見もあるでしょう。そこで筆者がよく使うオペレータを紹介します。
以上です。多いですか?これ以外にも例えばキーボード操作やスクロールの制御でfromEvent()
#やdebounceTime()
#などを使うこともありますが、基本的に覚えるべきオペレータは何かと聞かれたら、この4つを答えます。筆者でも全オペレータの用途なんて覚えていません。あれは多すぎます。
RxJSを扱う上で必要な考え方としては「なんのオペレータが備わっているか」ではなく「RxJSは何のために開発され、何ができるようになるライブラリなのか」という思想を理解することです。それさえ分かれば、あとはドキュメントを斜め読みしながら、必要に応じての検索でよいです。
余談: Angular日本ユーザー会があるということ
この節は客観ではなく主観で述べます。私はAngularが好きです。そしてコミュニティ活動をしています。直接のフレームワーク選定基準とは異なりますが、コミュニティの居心地の良さ、話しやすさ、相談のしやすさも、ひとつの考慮に加えることはできるかもしれません。
Angular日本ユーザー会に私が参加したのは2015年、それから毎年、カンファレンスイベントng-japanを中心に数多くのイベント、ハンズオン、セミナーを運営してきました。ローカルコミュニティng-kyotoの代表を務めていたこともあります。
コミュニティ運営への参加のモチベーションは、実際のところこの4年で流動的に変化しています。しかし現在、もっとも支持できるコミュニティポリシーとしてひとつ掲げられたのが大きいです。
Make community where everyone feels welcome
これは、フレームワークAngularが掲げるValuesのひとつ "Community where everyone feels welcome" を実現するため、多様性を尊重し誰もが歓迎されるAngularコミュニティの輪を日本中に広げていくことを目標とする、というものです。
Angularが好き、Angularが嫌い、ほかのライブラリが好き、多様性です。なんだってよいのです。
そのなかで、もしAngularに興味を持ってくださる方がいれば、その方に自分のこれまでの経験を伝えたい、Angularへの理解を深めて欲しい、そう思って私はコミュニティの副オーガナイザを務めています。
私はこれまでに長い時間Angular案件に関わってきました。それなりに設計の精度や問題解決の早さには自負があります。ただ、運営に携わる理由はAngular案件をやってきただけではありません。コミュニティの思想、ひいてはAngularそのものの思想と私の思想が一致しているからなのです。
もしAngularを使っていて、悩み、相談があればコミュニティサイトを訪ねてください。
今、何を選ぶべきか
2019年も終わりに近づいた今、アプリケーションの開発にどのライブラリ、フレームワークを選ぶべきか、それは筆者がここで述べられるものではありません。読者であるあなたが学び、携わる案件の参加人数、納期、要件の多さなどを考慮しながら判断を下す必要があるのです。
その判断を下すためには、ウェブの早さに慣れ、ウェブの世界で今何が起こっているのか、何が生まれ、何が消えていったのかを学ぶ必要があります。そうして学んだ知識はAngularでも、他のライブラリでもきっと活かせるものですし、そこで得た自信は、他の何らかの記事を読んだ程度では揺らがない確固たるものとなります。
遠い将来、JavaScriptが無くなるかもしれません。ウェブブラウザがどのような姿になっているかも想像がつきません。もっと新しいプログラミング言語を生業としている可能性すらあります。それでも、今何を選ぶべきかという判断のスキルは、将来ずっと、その時々に何を選ぶべきかという決断に、役に立つのです。
Discussion