AWS Amplify × Next.js で Server Side RenderingのデプロイおよびCI/CD環境を構築する

Posted on May 19, 2021  -  15 min read

本記事では、AWS Amplify と Next.js を使って SSR 構成のデプロイ、および CI/CD の構築について考えていきたいと思います。

※2021/05/19 更新 Amplify Console が SSR ホスティングをサポートしたので記事の内容を更新しました

(参考) https://aws.amazon.com/jp/blogs/mobile/host-a-next-js-ssr-app-with-real-time-data-on-aws-amplify/

はじめに

AWS をある程度触ったことがあり、主要なサービスについては最低限理解されている前提で進めます。AWS を全く触ったことない方は、AWS 上で SSR 構築する際の課題や方針などについてなんとなく理解できれば良いなくらいの期待値で読んでいただけると幸いです。

SPA/SSR のおさらい

既に SPA,SSR の技術に精通されている方は、本章を読み飛ばしていただいても構いません。

SPA(Single Page Application)

spa

SPA では、初回アクセス時に、アプリケーションの動作に必要な Javascript ファイルや CSS といった Asset を端末に一括ダウンロードします。単一のページを Javascript 側の処理で表示を切り替えることで高速な画面遷移を実現します。また、画面の一部を非同期に描画、更新することができ、ダイナミックな画面表示を実現することができます。

一度画面を表示してしまえば、以降のページの切り替えやデータのやり取りは非常に高速に行うことができる反面、初回アクセス時にある程度まとまった Asset を一括で端末にダウンロードするという特性上、初回のページ表示が遅くなる、もしくは、画面のレンダリングが完了するまで未完成の状態のページが画面に表示される場合があります。こういった挙動が SEO や OGP の観点で不利になることが知られています。(注 1)

SSR(Server Side Rendering)

ssr

SPA の問題を解決するために出てきたアーキテクチャが Server Side Rendering(SSR) です。

SSR では、先ほどの初回表示の問題を解決するために、API サーバとクライアントの間に、ページを生成するためのレンダリングサーバを用意します。

SPA では実際に画面を構築する作業はクライアントで行なっていましたが、SSR ではこの作業をレンダリングサーバに移譲しています。これにより、クライアントは既に構築済みのページを受け取ることができるので、初回画面の表示が高速化される場合があります。(注 2) また、未完成の状態のページが画面に表示されることがなくなるため、SEO の観点からも SPA と比較して有利であると言われています。

SPA の時と同様にクライアントから直接 API を呼び出すといったことも可能ですので、一部の画面を SSR にして、他の画面を SPA のようにクライアントで画面を構築することも可能です。

SSR のデメリットとしては、SPA の時と比較して、レンダリングサーバというインフラのコンポーネントが一つ増えることになるので、インフラの実装、運用コストが増えます。また、レンダリングをクライアントサイドで行うのか、レンダリングサーバで行うのかという実装の住み分けが必要になるため、アプリケーションの実装コスト(設計の難易度)が増えることがあります。

これらのコストと比較して SPA、SSR のどちらで実装を行うのかを検討する必要があります。例えば SEO がそもそも必要ない場合(社内向けシステムなど)であれば、構成がシンプルな SPA が選択される場合が多いです。

AWS Amplify について

SSR に限らず、AWS 上に Web アプリケーションを構築する際に避けては通れないのが AWS Amplify(以下、Amplify)です。

Amplify は、1. サーバーレスなバックエンドをセットアップするための Amplify CLI、2.フロントエンドで利用できる UI コンポーネント、3.クライアントサイドから AWS のバックエンドに接続する際に直感的な IF を提供する Amplify Library、4.CI/CD やホスティングのためのコンソール、を含む Web およびモバイルアプリ開発のためのフレームワークです。

about amplify

Amplify を用いることで、AWS のバックエンドの構築、構築したバックエンドに接続するためのクライアントサイドの実装を非常に直感的に行うことが可能です。

実装の例を挙げると..

  • Amazon Cognito を用いた認証、認可、Social Provider を用いた OAuth 認証
  • GraphQL のマネージドサービスである AWS AppSync を用いたデータの CRUD 処理
  • きめ細やかな権限設定を伴うファイルのアップロード/ダウンロード
  • MQTT を用いた WebSocket 通信
  • AWS が提供する各種 AI サービスとのシームレスな統合(例えば、ネガポジ判定や画像アップロードして Object Detection の結果を返すといった API などが簡単に利用できます)

Amplify CLI を用いることで、これらの機能の実装をほぼゼロコストで構築することができます。専門のインフラエンジニア、バックエンドエンジニアを抱えることができない小規模な開発組織においては Amplify は非常に強力なツールになり得ます。

また、Web アプリケーションのホスティングも非常に簡単で、Amplify Console と呼ばれるホスティングサービスを用いることで、数クリックで Github や Bitbucket といったソースリポジトリの連携、CI/CD パイプラインを構築することができます。

amplify console

Amplify の各機能の詳細に関しては 2020/5 に公開された BlackBelt Online セミナーを見れば体系的に学べると思います。

Amplify と SSR

2020.09.15 に Amplify Library が Server Side Rendering(以下、SSR) に対応しました。これまでの Amplify Library では、セッション情報やクライアントの設定情報をサーバサイドに引き継ぐ仕組みが存在しなかったため、Amplify Library は事実上クライアントサイドのみの実装に限られてきました。

今回のアップデートにより、Next.js でいうところの getServerSideProps メソッド に Amplify のクライアントの情報を引き継ぐことができるようになります。

以下、AWS 公式 Blog の抜粋

import { withSSRContext } from 'aws-amplify';
import { listMovies } from '../src/graphql/queries';

export default function Movies(props) {
  const { movies } = props;
  return (
    <div>
      {movies &&
        movies.map((movie) => (
          <div key={movie.id}>
            <h3>{movie.name}</h3>
            <span>{movie.description}</span>
          </div>
        ))}
    </div>
  );
}

// ↓ Server Side で実行されるソース
export async function getServerSideProps(context) {
  // 今回新しく追加された、withSSRContext により、クライアントの情報をServer Sideで引き継ぐことが可能
  const { API } = withSSRContext(context);
  let movieData;
  try {
    // クライアントのセッション情報(この場合Cognitoのtoken情報など)を引き継いでAppSyncにクエリを発行
    movieData = await API.graphql({
      query: listMovies,
      authMode: 'AMAZON_COGNITO_USER_POOLS',
    });
    console.log('movieData: ', movieData);
  } catch (err) {
    console.log('error fetching movies: ', err);
  }
  return {
    props: {
      movies: movieData ? movieData.data.listMovies.items : null,
    },
  };
}

Amplify が SSR のホスティングをサポート

2021/05/19 に Amplify Console がNext.js の SSR ホスティングをサポートしました。

これまでServerless Frameworkと呼ばれる Amplify CLI とは別のコマンドラインツールを用いる必要がありましたが、Amplicy Console を用いて静的サイトの時と同じ手順でホスティングを行うことが可能です。

SSR のホスティングには特別な操作は必要しません。静的サイトとしてホスティングするのか、SSR としてホスティングを行うかは Amplify Console 側で自動判定してくれます。公式ドキュメント によると、どうやらpackage.jsonの中身から SSR でホスティングすべきかどうかを判断しているようです。

デプロイされる構成としては以下のようになります。

architecture

SPA の時と同様に静的アセットは ストレージサービスである Amazon S3 に配置され、CDN である Amazon CloudFront から高速に配信されます。動的コンテンツのレンダリングには Lambda@Edge が用いられており、上記コードの例では、Lambda@Edge から GraphQL のマネージドサービスである AWS AppSync に対してクエリが発行され、認証認可には Amazon Cognito が用いられています。

Amplify Console で SSR をホスティングした場合、CloudFront の設定を変更することが可能に

Amplify Console で静的サイトをホスティングした時のもう一つの変更点として、CloudFront が自アカウントで操作可能な点があります。

静的サイトのホスティングの場合、AWS サービスチームの CloudFront にホスティングされていたため、ユーザは CloudFront の管理を意識することはありませんでした。ユーザの管理対象が減るというメリットがある反面、CloudFront に対して独自の設定を行うことができないというデメリットもありました。

Amplify Console で SSR を実施する場合、CloudFront はご自身が使用されているアカウントに構築されます。これにより、例えば Amplicy Console でホスティングされたサイトに AWS WAF の設定を追加するといったことができるようになります。

Enterprise のお客様などで、自社ネットワークからのみサイトにアクセスさせたいといったユースケースにマッチしそうです。

なお、静的サイトに関しては引き続きサービスチームの CloudFront にホスティングされるためご注意ください。

ssr hosting

まとめ

Amplify の仕組みを用いて、AWS 上に Next.js × SSR を構築する一例についてご紹介しました。 Amplify Library が SSR に対応したことで、AWS 上で SSR のアプリケーションを構築する選択肢が取りやすくなったと思います。

以上です!


補足:

  • 注 1. 近年では多くのクローラが Javascript を実行するようになりました。そのため、数年前に問題視されていたほど、SEO の観点で SPA が不利になることは少なくなりました。ただし、クローラは全ての Javascript を実行することを保証していないため、高い SEO が求められる場合は SSR や JAMStack といった構成を検討する必要があります。

  • 注 2.* Web のパフォーマンスに関しては様々な指標が存在します。Web のパフォーマンス改善を議論する場合は、アプリケーションの特性を理解し、どの指標を改善すべきなのかを正確に把握する必要があります。一般的に、SSR にすることで SPA より描画速度が高速になると言われることが多いですが、ケースによっては SSR にすることでユーザ体験を損なう場合もあります。(Server Side Rendering の挙動を確認する を参照) 自社のアプリケーションの課題、特性、SPA や SSR の持つ特性を正しく理解し、技術選定を行うことが大切です。