モダンなWebフロントエンドの技術とAMP

リクルートテクノロジーズ兼リクルートライフスタイルのASGチームに所属しているフロントエンドエンジニアの可児です。本記事は、リクルートライフスタイルアドベントカレンダー4日目の記事です。 本記事では、リクルートライフスタイルで取り組んでいる新規Webサービスのモダンなフロントエンド開発に関することとして、Next.jsやAMPについて紹介します。

目次

  1. はじめに
  2. モダンなWebフレームワーク
    • React/Next.js
    • TypeScript
    • styled-components
  3. AMP
    • AMPとは
    • AMPの開発パターン
    • Next.jsとAMP
    • AMPキャッシュ
    • AMP と 非AMP の共存
    • 検索結果カルーセルとSEO
  4. AMPの開発における注意点
  5. まとめ

はじめに

リクルートライフスタイルでは、じゃらんやホットペッパーグルメ、ホットペッパービューティーといった従来のWebサービスだけではなく、様々な新しい領域の開拓を狙ったサービスにもチャレンジしています。組織としては柔軟に技術選定することが可能な新規Webサービス開発を通して、最新の技術スタックを取り入れた開発を行い、有用性を確認した上でリクルートの様々なサービスに横展開していくことで、リクルート全体のWebサービス開発のDX(Developer eXperience)とUX(User eXperience)を高めていきたいと考えています。

ホテルや飲食店といったクライアント様の情報を、Webを介して様々な情報の中からより素早く適切にユーザにお届けするための1つの施策として、Webサービスを高速に表示することが求められています。それを実現するための技術として、最近ではGoogleが提唱するAMPという技術に注目が集まってきています。今回は、実際にリクルートライフスタイルの開発現場で利用しているモダンなWebフレームワークとしてNext.js、周辺技術としてTypeScript、styled-components、そして上述のAMP、それらの技術スタックでAMP対応のページを開発する方法と注意点、AMPのメリット・デメリットなどについて紹介します。

紹介技術ロゴ

モダンなWebフレームワーク

React / Next.js

Reactは近年のWebサービス開発において広く使われているUIフレームワークです。また先日の Chrome Dev Summit 2019 のキーノートやWebフレームワークの技術を紹介するセッションでは、Reactで容易に開発可能なWebフレームワークである Next.js が大々的に紹介されました。UIフレームワークとしてのReact、それを利用するWebフレームワークとしての Next.js が Webサービス開発の代表格となりつつあります。 Next.js はフロントエンド開発で一種の鬼門であるwebpackの設定であったり、BFFのWebサーバの構築やサーバサイドレンダリングを、zero config 、つまり何の設定もなくいきなり Reactで書きはじめることができます。周辺プラグインも充実し、公式のGitHub内には多数の example も用意され、後述する AMP のサポートももちろん行われています。リクルートでも Next.js を活用したWebサービス開発が多数進められています。

TypeScript

JavaScript には型がないため、これまでも様々なAltJSが世の中に出回ってきましたが、いまはTypeScriptが非常に強い印象を受けます。TypeScript は @DefinitelyTyped の充実さが光っており、有名なモジュールにはほとんど型定義が用意されてきています。今後のモジュール開発も TypeScriptが利用されるものも増え、より一層型で縛ることのできる世界に近づいていくものと思われます。上述の Next.js ではデフォルトでTypeScriptの開発が可能です。

styled-components

CSS in JS の代表格の1つとして、styled-components というライブラリがあります。近年では emotion なども人気が出てきており、いくつかの選択肢があるように思います。styled-components でも emotion でも js で変数値を style に利用することができます。

1
2
3
4
5
6
7
8
9
10
11
const Color = {
  Border: {
    Default: "#e4e4e4",
    Fixed: "#b8b8b8"
  }
  ...
}
const StyledItem = styled.div`
  border-top: 1px solid ${Color.Border.Default};
`;

Next.js では styled-components を活用した example も紹介されています。

AMP

AMP とは

最近のWebサービスはよりリッチなUI/UXを提供するようになってきています。それに伴って JavaScript, CSS が肥大化したり、表示する画像は高画質化したりしていて、結果的に表示したいWebページのサイズが増加しています。さらにWebサービスに対して改善を加え続けていくと、Webページ内に不要なコードが残ってしまったりすることでページのサイズが大きくなってしまい、ページのレンダリング速度が遅くなることで顕著にUXが悪くなることがあります。 それらの問題に対し、Google はパフォーマンスを低下させないための仕組みとして AMPを提唱しています。AMP は “Valid な状態” が明確に定義されています。例えば、

  • AMP ページには任意のJavaScriptは含めないこと
  • CSS は 50KB以内にすることなど

ルールを制約として設けることで AMP Valid な状態にしておくことでページサイズを抑えレンダリングの速度を遅くしないようにしています。 AMPは独自のAMP用の Validator を用意することで、AMP Valid な状態かどうかを確認することができます。AMP Valid な状態でWebページを公開すると、Google 側が検索結果に AMP Valid であることを示す稲妻マークを表示してくれるため、ユーザはそのページがAMPであり高速に表示されることがわかります。そして、Googleは自動的にAMP ValidなページをGoogleのCDNにキャッシュとしてのせてくれるため、より高速にユーザに検索結果を表示することができます。

AMP では任意のJavaScriptが記述することが許容されない代わりに、AMP用の UI コンポーネントが提供されています。Web Components として作られている AMP コンポーネントは、 <amp-xxx> といった形式のタグとして html に書くことで簡単に利用できます。 例えば、画像を表示する <amp-img> は従来の <img> タグと似たタグとなっています。しかし、 <img> が属性値で指定された src の画像を表示するだけのものであるのに対し、 <amp-img> はそれ単体で、レスポンシブに表示することを可能にするとともに、自動的に lazy load 機能が働き、画像がブラウザに表示され必要となったタイミングでダウンロードされるようになります。このように AMP コンポーネントは、単体でパフォーマンスに考慮された設計がなされているHTMLタグとして活用することが可能です。 そのほかにも、 <amp-sidebar><amp-carousel> といった、サイドバー、画像のカルーセル表示など、自前で開発しようとすると大変そうなコンポーネントもすでに用意されているので、十分に活用することができます。最近では amp-story と呼ばれる新たに大きな機能がリリースされ注目されています。

AMPの開発パターン

AMP の開発パターンには2種類あります。

  • Hybrid AMP
  • Full AMP(AMP First とも呼ばれる)

Hybrid AMP はオリジナルの AMP Valid ではない Webページに対して、 AMP用の AMP Valid なページをカノニカルとして用意するものです。実際には、既存のオリジナルWebサイトや記事がすでにリリースされている場合に、Google検索による流入増加を目的としてAMPページを用意する形で開発が進められることが多いため、 Hybrid AMP の開発パターンの方が現在は多いです。

しかし、Hybrid AMP でAMPのページを開発することだけが目的となってしまい、本来のオリジナルのページとはUXが異なる簡素なページを開発されるケースが見られます。AMPを開発しているGoogleは、基本的にはAMPとオリジナルのページの UX は大きく変えないことを推奨していますし、ユーザ体験としても同じページを見ているのに、AMPページを見る場合とオリジナルのページを見る場合で、機能やUIが異なってしまうのは体験上良いものとは言えないため、オリジナルのページとほぼ同等のAMPページを作り込むべきです。

新規でページを開発する場合には Hybrid AMP では、同じWebページをオリジナル、AMPの2ページ分開発してしまうことになり、二重開発が発生してしまいます。そのため、Full AMP で開発することで、はじめからAMPを利用して開発を行うことで、二重開発を避けられます。

ちなみに、AMPコンポーネント は必ずしも AMP Valid な状態で使う必要はありません。AMPコンポーネントは単なる Web Components として利用することもできます。AMPコンポーネントそのものとして有用なものも多いので、Webページの開発を行う際に部分的にAMPコンポーネントを一種のUIライブラリとして利用することで、パフォーマンスを担保しつつ、開発効率も高めることができます。

Next.js と AMP

Next.js ではpagesフォルダ配下のファイルが自動的にファイル名でルーティングされ、1つのページとして機能し表示されます。デフォルトの設定でサーバサイドレンダリング(SSR)されます。

AMP 化は容易で、pages のファイル内で export const config = { amp: true }; と書いておけば、AMP を有効化するために必要なスクリプト等を含んだ形でSSRを行ってくれます。ただし、それだけで自動的にAMP Valid になるわけではないので注意が必要です。

AMP Valid かどうかは Next.js がSSRするタイミングで、AMP用のValidationを通してくれるので、Next.jsの標準出力で確認することができます。AMP Validにするためには、ページやコンポーネント内にクライアントサイドで実行されるJavaScriptを書かず、 CSSも50KB以内に抑えつつ、<head> 内の1箇所にまとめてに書く必要があります。Chrome Extensionや、 AMP ValidatorがWebページになっているので、それらも活用できます。

AMPキャッシュ

AMP Validな状態だと、AMPページのキャッシュ(AMP キャッシュと呼ぶ)がGoogleのCDNにのります。GoogleのAMPキャッシュにのることで、Googleの検索結果でカルーセル的に表示されて検索結果表示に優遇されるようなことがあったり、AMPキャッシュにのっているページは検索結果ページが表示されている状態であらかじめ(ユーザがクリックする前に)HTMLドキュメントをダウンロードしておき、ユーザが実際にクリックしたら高速に表示することができるようにしたりします。

しかし、AMPキャッシュが返されるということは最新のHTMLドキュメントがSSRされるわけではなく、前回キャッシュにのったタイミングのものがAMPキャッシュからユーザに届けられることになります。そのため例えば、飲食店の予約情報などといった変化の激しいデータがAMPキャッシュにのってしまうと、ユーザに正しくないデータが届けられてしまう可能性があることに注意が必要です。ただAMPキャッシュは Stale-While-Revalidate モデルに従ったものになっています。つまりユーザの誰かがキャッシュにアクセスしたタイミングで、GoogleがオリジナルのサーバにアクセスしAMPキャッシュを更新させることが可能になりますので、問題になるケースは絞られるのではないかと思います。

また、AMPキャッシュはGoogleのCDNにのっているためGoogleのドメインとして表示されてしまうことにも注意が必要です。基本的にページの表示そのものに問題はありませんが、ユーザがURLバーを見てどこのサイトのものなのかわかりづらかったり、モニタリング分析系の通信で思ったようなデータを収集できないケースがあります。AMP用の分析タグとして amp-analytics が用意されているものの、例えばITP2.3の影響で Safari では別ドメインによるCookieの書き込みやLink Decorationであると判断されてしまうことで、これまで取得できていたような分析データが収集できないかもしれないので注意が必要です。

ちなみにChromeブラウザのみを対象にすれば、Signed HTTP Exchange (SXG) という仕組みを適用することができます。これはWebページへのリクエストとレスポンスに対して署名を行いAMPキャッシュとしてGoogle側が保管しておき、Chromeブラウザがその署名が正しいものであると検証できた場合に、Googleから配信された署名つきAMPキャッシュを本来のオリジナルのドメインとして扱うことができるものです。そのため、Googleから配信された AMPキャッシュをブラウザで表示しているにも関わらず、 URLバーの表示はオリジナルのURLが正しく表記されるようになります。CDNサービスを提供しているCloudflareは簡単にSXG化させることができます。しかしながらSXGの仕組みは Chromeブラウザでしか実装されていないため、現状Safariでは変わらずGoogleドメインとして扱われてしまいます。

AMP と 非AMP の共存

Full AMPで開発を進めていたときにユーザ固有のログインページなど、AMP Valid である必要がないページも同じサービス内に作る場合でも、Next.jsで開発していればpagesで定義するページに対し、 amp: true の config を用意しなければそのままReactのページとしてレンダリングされることになります。つまり、開発体系を大きく変えることなくコンポーネントも使い回しながら開発を進めることができるため、AMP ValidとAMP Validではないページを共存させることは難しくありません。

検索結果カルーセルとSEO

Googleの検索結果でカルーセル表示になるページは、ニュース記事や飲食店情報といった限定された分類のページに限られています。商品情報や店舗情報といったその他の内容のものはAMP Validなものにしても検索結果として優遇されるカルーセルになるとは限らないことに注意が必要です。

また、SEOとしてAMPページが優遇され検索結果が上位になるといったことがGoogleから明言されているわけではありません。しかし、Googleはページの表示パフォーマンスはSEOに影響します。AMP Validにすれば必ず検索結果が上位になるわけではありませんが、ページのレンダリング速度は一定の高速化が可能になり、これは間接的にSEOへの効果が寄与されるものと言えます。

AMPの開発における注意点

AMP では任意のJavaScriptを記述して動的な処理をページ内に含められない代わりに、AMPコンポーネントに対して動的な処理を記述することが可能です。AMPコンポーネントに対しても制約上JavaScriptを記述できないので、HTML属性にスクリプトライクな文字列を記述して、動的な処理を定義します。例えば <amp-sidebar> でサイドバーを利用する際は、サイドバーの開閉処理をボタンなどに対して on 属性で定義することができます。

1
2
3
4
5
6
7
8
9
  // サイドバー本体
  <amp-sidebar id="sidebar">
    <div>
      サイドバーの中身
    </div>
  </amp-sidebar>

  // idがsidebarのサイドバーを開くトグルボタン
  <button on="tap:sidebar.toggle">開く/閉じる</button>

この時、開発時にNext.jsでTypeScriptを利用している場合、AMP独自のWeb ComponentsはReactのJSXとして型が用意されているわけではありません。

型違反

現時点ではAMP公式の型定義が公開されているわけではありませんので、開発者がAMPコンポーネント向けに型定義をそれぞれ用意する必要があります。また、on 属性自体ももともとbuttonタグには存在しないものなので、型違反になるためこちらも同様にHTMLAttributesの型定義の拡張が必要です。

amp-sidebarの型の例

また styled-components と組み合わせる場合には、 const StyledSidebar = styled["amp-sidebar"] のようにAMPコンポーネントの style を直接修飾することはできず、 <amp-sidebar> をラップしたReactコンポーネントを定義し、styled関数で囲うことでスタイルを修飾する必要があります。

このようにAMPが既存のHTMLにないタグ名だったり属性値を使用しているために、TypeScriptやstyled-componentsといった他のライブラリで一般的な仕様上のHTMLを想定されたものになっているときに不具合が生じることがあるので注意が必要です。

一方で、styled-componentsではもともとAMP用の on 属性が付与されたHTMLコンポーネントにスタイルを修飾すると on 属性の情報が消えてしまうバグがありましたが、今は解消されています。このようにだんだんとAMPとモダンなライブラリとの親和性が高くなってきていることも事実です。

AMPそのものが発展途上であるがゆえに、AMPそのものにもバグが入り込んでしまうことがあります。バグが入り込んでしまうこと自体はライブラリとして利用している以上逃れることができません。しかし、npm などで配信されるモジュールに対しては、package.jsonでモジュールのバージョンを指定しておき、必要に応じてバージョンを上げたりビルドし直すことで異常を確認できますが、 AMPではAMPコンポーネント向けのスクリプトがCDN経由で配信されているため、パッチがあたった瞬間にCDN側が更新されることに伴い、リリースされているプロダクションで参照しているスクリプトが急に新しくなることがあります。ここにバグや想定外の仕様変更が入り込んでいる場合、いきなりプロダクションのサービスに直に影響が出ることがあり得ます。つまり、AMPコンポーネントやAMP用のスクリプトはある一定の依存度を保ったり、エラーハンドリングをきめ細やかにしておくなどの細心の注意が必要になります。

また、DX的にはAMPは上述の通り、スクリプトライクな文字列でロジックを管理しなければなりません。これはAMP独自のスクリプトであるため、はじめてAMPを触る場合には学習コストがかかります。それに加えて、今のところこのAMP 独自のスクリプトに対して適切なシンタックスハイライトが行われるエディタであったり、lint機能は提供されていません。せっかくTypeScriptを利用していても、このままでは型を効かせることもできません。そのため、もしきちんとAMP独自のスクリプトを管理するためには、AMP用のスクリプトビルダーを内部で開発するなどして、適切な管理が必要になります。

まとめ

本記事ではWebフレームワークとしてのNext.jsやGoogleの提唱しているAMPについて紹介しました。

これまでの内容を踏まえ、私が考えるAMPのメリットは、以下になると思います。 - Validな状態にするための制約に従うことで、パフォーマンス低下を防ぐことができる点 - AMPキャッシュにのせることによる検索結果のカルーセルと表示高速化 - AMPコンポーネントそのものの有用性

一方でAMPによるデメリットは以下だと思っています。 - AMP Validにする厳しい制約による機能開発の難しさ - AMPを扱うためのスクリプト更新への追従 - AMPキャッシュやAMPの制約下で分析、広告タグが利用しづらいこと

もちろんメリット・デメリットは上記に挙げた点だけではありません。AMPを導入することによる学習コストの増加や制約による開発が難しくなることはあります。しかしながら、AMPの根本的な考え方である、必要最低限のスクリプトやCSSでWebページの高度なパフォーマンスを担保しながらユーザ体験が良いものを提供していくことはどのような開発を行うにも参考にすべきことであると思います。例えば、AMPコンポーネントをWeb Componentsとして利用しながら、パフォーマンスバジェットをlighthouse CIで管理し、パフォーマンス低下を防ぎながらサービス開発を行ったとしても一定の同様の効果を得られると思っています。すべての技術に言えることですが、開発するサービスや開発チームにとってどのような技術を組み合わせることがベストなのかどうか判断しながら先進的なフレームワークを取り入れて開発を行なっていくべきかなと私は思っています。

最後になりますが、 リクルートライフスタイルの開発チームリクルートテクノロジーズの開発チームではフロントエンドエンジニア・モバイルアプリエンジニア・サーバサイドエンジニア・インフラエンジニア・機械学習エンジニア等々幅広く募集しています!!!