7839

雑草魂エンジニアブログ

Client Side Rendering(SPA)・SSR・SSG を整理してみた

Next.js で開発しているときに、Client Side Rendering(SPA)・SSR・SSG についてきちんと整理できておらず、自分なりの整理をしてみたので、備忘録として残しておく。(React ベースのフレームワークSSG といえば、Next.js 。ただし、v9.3から SSG の機能が本格的に導入されたので、Next.js は SSR はもちろんのこと、SSG も対応可能となった。) SSR を理解する上で、Client Side Rendering または SPA と対比しながら整理を行った。

Client Side Rendering

Client Side Rendering は、SPA(= Single Page Application)とほぼ同意義である。

ブラウザからリクエストされると、サーバーは、JS のビルドされたファイルと必要最小限のHTML要素しか含まれないHTMLファイルを返却する。HTMLファイルの中身はほぼないので、初期表示は何も表示されない。それから、ブラウザ上でAPIなどを使い、初期データを取得して、HTML 要素をレンダリングする。

<html>
  <head>
    <title>sample</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/js/bundle.js"></script>
  </body>
</html>

f:id:serip39:20200823202016j:plain

  • メリット
    • ユーザーに優れた UX 提供が可能
    • (ページ遷移ごとにリクエストしないため、)ページ遷移が高速
    • ネイティブアプリの代わりとして提供可能
    • HTMLとJSファイルのみがホスティングサーバーにあれば、ページ配信が可能
  • デメリット
    • 初期ローディングにかかる時間が増える
    • SEOで不利な場合がある(クローラーがJSを実行して中身を解析できるようにはなっているようではある。)
    • 動的なOGP対応ができない場合がある

SSR

「Server Side Rendering」の略である。上記の Client Side Rendering と対比して、ブラウザ上で初期データをレンダリングするのではなく、サーバー側でデータ取得、レンダリングまでを行い、HTMLファイルを配信する。ただし、初期データ以外のデータはAPIなどを用いて取得を行い、SPA同様に、ブラウザ上でレンダリングを行う。

f:id:serip39:20200823202030j:plain

  • メリット
    • (ブラウザ上でレンダリングしないので)コンテンツ表示までの時間を短縮できる
    • (SPAと比較して)SEOが向上する
    • (SPAと比較して)動的なOGP対応が可能
  • デメリット
    • SSRするためのWebサーバーが必要になる

SSG

「Static Site Generator」の略であり、静的サイトジェネレーターである。

アプリケーションのビルド時に、APIなどからデータを取得し、HTMLを最初に生成しておく。サーバーへのリクエストがあった場合には、この生成されたHTMLファイルを返却する。このように、ブラウザではなく、サーバーで先にビルド時にデータを取得してレンダリングを行っておくことを「プリレンダリング」という。また、生成された各 HTML はそのページに必要な最小限の JavaScript コードと関連づけられる。ブラウザによってページが読み込まれると、そのページの JavaScript コードが実行される。(このプロセスは ハイドレーション と呼ばれる。)

一番の特徴は、アプリケーションをビルドした際に、データベースなどからデータを取得することである。ブラウザからのリクエストがあった場合ではない部分が大きな違いである。そのため、利用されるアプリケーションとしては、更新頻度の少ないサイトやブログなどが挙げられる。

f:id:serip39:20200823222603j:plain

  • メリット
    • 静的サイトを配信するので、レスポンスが高速
    • (SPAと比較して)SEOが向上する
    • (SPAと比較して)動的なOGP対応が可能
    • HTMLとJSファイルのみがホスティングサーバーにあれば、ページ配信が可能
  • デメリット
    • ページ量が多いWebサイトには向かない
    • ページ数が多くなればなるほど、ビルド時間が遅くなる
    • 頻繁にデータ更新があるサイトには向かない(データ更新の度に、再ビルドが必要。)
    • APIを頻繁にリクエストするサイトには向かない

まとめ

各種構成の違いに関して、整理をしてみた。実際に、Next.jsで開発を行う際には、構成の違いに応じて、実装の仕方が異なる。特に、getInitialProps・getStaticProps・getStaticPaths・getServerSideProps をいつ、どのように使うべきか把握しておく必要がある。この違いに関しては、また別の記事で整理したいと思う。

それでは、ステキな開発ライフを。

【Next】Next.jsの中で Firebase Admin SDK を使った処理をどのように実装すべきか

最近、Next.js に触れる機会があり、試行錯誤しながらコーディングを行っている。その中で、Next.js の中で Firebase Admin SDK をどのように実装すべきかという課題にぶち当たり、とりあえず実装はできたものの、最適解ではないと思っている。もし最善の策があれば教えて欲しい。切実に。

WEBアプリケーション構成

今回のWEBアプリケーションでは、Firebase Authentication でユーザー管理、ユーザー認証機能を実装している。簡単に書くと、以下のような構成になっている。

f:id:serip39:20200822185854j:plain

フロントエンドの実装で、Next.jsを用いている。SSRで実装しており、Custom Server として、Express を用いている。

Firebase Authentication

firebase.google.com

Firebase Authentication を使うことで、アプリケーションに簡単に認証機能を追加することができる。パスワード認証や OAuth2 に対応した主要なフェデレーション ID を使った認証などが用意されている。フェデレーション認証で、GoogleFacebookGitHubTwitter などのサードパーティの認証情報を使用して、アプリケーションにログインさせることも簡単に実装できる。簡単に実装できると書いたのは、使いやすい SDK、アプリでのユーザー認証に使用できる UI ライブラリまでも用意されているからである。簡単に、確実に導入ができるというメリットは本当に大きい。さすがGoogle様だ。

今回の Node.js の WEBアプリケーションの場合、以下のSDKを用いることができる。(SDKには、Authenticationの認証機能だけでなく、Firebaseの様々な機能を用いることができる。今回は、認証機能に絞って記載する。)

  • Firebase JavaScript SDK(フロントエンド用)

    • firebase.auth() で Authentication のメソッドを使うことができる
    • 新規ユーザー登録、ログイン、ログアウト、ユーザーデータの取得・更新が可能(ただし、ユーザー管理などは、サーバーからのみしかできない。)
  • Firebase Admin SDK(サーバーサイド用)

導入方法などは、公式を参照して欲しい。

firebase.google.com

firebase.google.com

Firebase Admin SDK を用いた ID トークン検証

Firebase JavaScript SDK を用いることで、基本的にはフロントエンドで実装したい機能は対実現できる。ただ、ログイン後に、IDトークンを Cookie などに保持し、リロードされた場合にもこの Cookie で保持した IDトークン を使用して、再度認証する場合を考える。

その場合には、サーバーサイドから確認をする必要がある。

firebase.google.com

そのため、今回の場合、Express の Custom Server から Firebase Admin SDK を用いて検証を行い、フロントエンドにユーザー情報を返却してあげる必要がある。

Next.js 内での実装方法

Next.js でどのように実装すべきかを検討するにあたり、色々な実装方法があった。

Next.js の API Routes を利用する

公式の example に実装例が用意されている。

next.js/examples/with-firebase-authentication at canary · vercel/next.js · GitHub

API Routes はサーバーサイド側で実行されるので、Firebase Admin SDK を用いることができる。

getServerSideProps と HOC を利用して実装する

github.com

直接的に、Firebase Authentication を実装されていないが、上記と同じようにすることで実装が可能そうであった。

Express の Custom Server に実装する

今回、私はこちらで実装を行ったので、実装例として紹介する。

Server側に関しては、Express に IDトークンを検証する機能を追加するだけであるが、問題はどのようにして、フロント側に認証情報を返却してあげるべきかということであった。

処理の経路は以下のようになる。

  1. ユーザーがブラウザからアクセスする。
  2. サーバー側にリクエストが来て、Expressで受け取る。
  3. Cookieを確認して、IDトークンを Firebase に確認して検証する。カスタムクレームを使用して、ユーザー権限に応じたページをレンダリングする。
  4. getInitialProps(getServerSideProps)の処理が実行される。
  5. propsでフロントにデータを受け渡す

IDトークンで認証した際に得られたユーザー情報をフロントに渡さずに、ユーザー情報をGETできる API を用意することもできるが、今回はAPIを使わずに、フロントにユーザー情報(カスタムクレーム)を返却する方法を考えた。

カスタムサーバー側では、以下の処理でレンダリングを行うことができる。

app.render(req, res, pathname, query, parsedUrl)

すなわち、レンダリングする前に、リクエストやクエリに情報を付加することができるのである。headerに情報を付加しようとして、 setHeader などを試したがうまくいかず、リクエストのクエリにデータを付加することにした。

app
  .prepare()
  .then(() => {
    const server = express()
    server.use(cookieParser())
    server.all('*', async (req: Request, res: Response) => {
      authenticateAccess(req)
        .then((newReq) => {
          if (newReq) {
            return app.render(newReq, res, req.url)
          }
          return handle(req, res)
        })
        .catch((err) => {
          res.writeHead(301, { Location: '/signin' })
          res.end()
        })
    })

    server.listen(port, (err?: any) => {
      if (err) throw err
      console.log(`> Ready on localhost:${port} - env ${process.env.NODE_ENV}`)
    })
  })
  .catch((ex) => {
    console.error(ex.stack)
    process.exit(1)
  })

そして、ページ側では以下のようにして、getInitialPropsのreqのクエリデータから、ユーザー情報を受け取ることができた。

const AppComponent = ({ Component, pageProps }: AppProps) => {
  const { claims } = pageProps
  return <Component {...pageProps} />
}

AppComponent.getInitialProps = async (appContext) => {
  const appProps = await App.getInitialProps(appContext)
  const { ctx } = appContext
  const isServer = !!ctx.req
  if (isServer) {
    const { claims } = ctx.req.query
    appProps.pageProps.claims = claims
  }
  return { ...appProps }
}

export default AppComponent

かなり無理矢理で実装することができた。。。

まとめ

今回は、Next.js + Firebase Authentication に関して、色々と試行錯誤した結果の実装例を紹介した。是非、もっとこうしたらいいのではないか等、ご意見やアドバイスがあれば、是非教えてほしい。

それでは、ステキな開発ライフを。

【VSCode】公式版の設定同期機能 Settings Sync を早速使ってみた

前回の記事で、VS Codeの開発版(β版)である VS Code Insiders を紹介した。そして、v1.48 (2020/07) のアップデートがついに安定版にリリースされた。

様々な機能が追加されているが、今回は VS Code の設定同期機能「Settings Sync」がプレビュー版という扱いであるが、安定版で使えるようになったので使用感を確認してみた。

Settings Sync

「Settings Sync」とは、VS Codeの設定同期機能である。今回のアップデート v1.48 で追加された新機能である。

code.visualstudio.com

これまでは、設定を同期するには、2つの手段があった。

  • 設定ファイルをコピーする
  • 拡張機能「Settings Sync(OSS)」を使う

設定ファイルをコピーする

VS Code の設定に関しては、User / Workspace の2種類の設定が存在する。Workspace ごとに使用する言語も違うので、使用言語に合わせた設定が可能となっている。 設定は、 settings.json ファイルに記載されているので、このファイルをコピーすることで同じ設定で使うことができる。

settingsの画面の左上のファイルアイコンのマークから、settings.json を確認することができる。

f:id:serip39:20200818153447p:plain

また、拡張機能に関しては、以下のパスから、インストール済みのファイルを確認することができるので、コピーすることで同じ拡張機能を使用することができる。

C:\Users\ユーザー名\.vscode\extensions

この方法はかなりアナログであり、面倒であるが、ネットワーク環境がなくてどうしても同じ設定で使いたい場合などには使える。(ネットワーク環境がない時点で開発が厳しいので、ほぼそんなことはないw)

拡張機能「Settings Sync(OSS)」を使う

marketplace.visualstudio.com

VS Code拡張機能として Settings Sync をインストールして、GitHub にログインすることで設定を保存することができた。 設定ファイルに関しては、GitHub の Gist で cloudSettings というファイル名で保存されていた。ショートカットコマンドで設定のアップロードやダウンロードもできて快適に使用することができた。

v1.48 ではこの拡張機能と同様の機能が VS Code の公式からリリースされた。以下で公式版の設定方法などを確認していく。

設定方法

設定ボタンから、「設定の同期がオン」を選択する

f:id:serip39:20200818161108j:plain

現在、まだプレビュー版であるため、同意の上で、設定する。

f:id:serip39:20200818161405j:plain

設定の同期ができるのは、以下の5個の項目である。(必要な設定のみを選択することができる。)

f:id:serip39:20200818161421j:plain

次に、ユーザー認証をするのためのアカウントを以下の2つから選択する。

f:id:serip39:20200818161608j:plain

ブラウザが開いて、認証が成功すれば、設定は完了である。

一度、VS Codeを再起動して、設定ボタンから、「設定の同期がオン」を選択することで、操作を選択することができる。

f:id:serip39:20200818163254j:plain

「同期されたデータを表示する」を選択すると、サイドバーに同期のアイコンが表示されて、現在の状況などを確認することができる。リモートとローカルでの差分なども確認することができる。また、変更履歴もすべて残されているので、いつでも過去の状態に戻すことができて、とてもわかりやすい構成になっていると思えた。

f:id:serip39:20200818164110j:plain

まとめ

公式版はUIもとても見やすく、使いやすい構成になっているように思えた。まだプレビュー版ということであるが、正式版が待ち遠しい。複数デバイスで開発作業をする場合に、どのデバイスでも同じ作業環境で作業ができることは本当に助かる。ちょっと設定が違うだけで、あれ?となってしまい作業効率などに影響するからである。

是非、みなさんも v1.48 の Settings Sync を使って、いつでも、どのデバイスでも、同じ開発環境で最適な開発を行ってほしい。VS Code最高ですね。

それでは、ステキな開発ライフを。

【VSCode】Visual Studio Code Insiders でリリース前の新機能を使ってみよう

前回の記事で、VSCode + Drow.io で、図形入りドキュメントを作成する方法 を紹介した。私は現在、「VSCode」(Visual Studio Code)を愛用している。そして、VS Code は毎月のようにアップデートされている。

code.visualstudio.com

7月のアップデートでも、様々な機能が追加されている。新機能に関して、安定版にリリースされる前に、先行的に使える「Visual Studio Code Insiders」があることを最近知ったので、紹介する。

Visual Studio Code Insiders  とは

f:id:serip39:20200816155234p:plain

Visual Studio Code Insiders」は新機能をいち早くチェックしたり、新機能を利用した拡張機能をテストしたいというアーリーアダプターのためのVSCodeの開発版(β版)である。以前までは、通常版の設定ファイルを書き換える必要があったようだが、「Visual Studio Code Insiders」は通常版とは別にインストールすることができる。通常版の VS Code とは独立して、拡張機能の設定なども各々で保持しており、必要なものだけをインストールして使うことができる。

ただし、通常版とは異なり、開発版(β版)であるため、新機能を追加したことによるバグなどが発生することも考えられる。しかしながら、以下のように日々アップデートが行われ、BugFix されているので、開発版(β版)であることを理解した上で使う分には問題ないと思われる。

github.com

インストール方法

以下にアクセスして、「Download」ボタンからインストールしてみて欲しい。

code.visualstudio.com

インストール後は、解凍するだけで、すぐに使うことができる。

サブエディタとしての利用

開発版の使用用途としては、上記でも述べた新機能の先行利用が主であるが、サブエディタとしての利用用途もあるようだ。

通常版の VS Codeを、JavaScript/TypeScript、PythonRubyなど様々な言語で利用し、様々な拡張機能をインストールした場合に VS Code の動作が重くなることがあるらしい。

その場合に、通常版とは別で拡張機能をインストールでき、設定も個別に可能な VSCode Insiders をサブエディタとして使うことで、作業効率を上げることができる。用途としては、有用であると思えた。

参照:VSCodeの調子が悪い時はVSCode Insidersに避難しよう - mottox2 blog

まとめ

今回は、VS Code の開発版(β版)を紹介した。これまで通常版しか使ってこなかったが、β版で新しいリリース前の新機能が使えることは新しい体験であった。7月のアップデートで、「Setting Sync」の機能が VSCode Insiders でのみ現在公開されている。VS Code を複数マシンで使う場合に、毎回設定するのは面倒であるが、この「Setting Sync」を使うことでその問題を解決することができる、まさに待望の機能である。使用感などに関してはまた紹介したいと思う。みなさんも、是非、試してみてほしい。

それでは、素敵な開発ライフを。

Bot と一緒にインタラクティブに学ぶ Python 入門 〜 サイコロゲーム編

前回、「GitHub Learning Lab」のPython入門コースの紹介記事を書いた。入門編はあまりに簡単な方も多かったかもしれない。「GitHub Learning Lab」には、もうひとつPythonのコース「Intermediate Python」があったので、受講してみた。

lab.github.com

このカリキュラムでは、サイコロゲームを実際にPythonで作ることができる。実装する中で、forifなどの重要な構文を実践的に学びながら、体感することができるので非常にいいように思えた。サイコロゲームといったイメージがしやすいものを実際にPythonのコードで実装するのはわかりやすく、とてもいい入門コースであるように思えた。

Intermediate Python

「Intermediate Python」は「GitHub Learning Lab」にあるPythonコースのひとつである。現在はまだ日本語に翻訳されておらず、英語のみであるが、Botインタラクティブにステップアップで学習を進めることができる。英語が少し読めるレベルであれば、問題なくチャレンジできるので、是非挑戦してみて欲しい。

  • Step1. Set up your project
  • Step2. Running a Python program
  • Step3. Using Variables
  • Step4. Rolling multiple Dice
  • Step5. Adding Conditionals
  • Step6. Adding User Input

GitHub Learning Lab」の教材は、すべて Issue や Pull Request など GitHub の機能を活用した構成になっており、GitHubの使い方に慣れる面でも非常に有用だ。プログラミング入門で、PythonGitHubの使い方を実践を通してマスターできるのは一石二鳥であると思う。

Step0. セットアップ

まず「Start free course」ボタンを押してセットアップを行う。本コースで使う GitHub リポジトリの公開範囲を選択する必要があり、私の場合は Public しか選択できなかったので、そのままで進めた。すると、自動的に intermediate-python-course が作られる。

f:id:serip39:20200814125614p:plain

これからは、bot から 次に何をすべきか、丁寧に説明が書かれた issue がリポジトリに追加されているので、それに従って進めていく。

Step1. Set up your project

ここでは、開発環境の構築を行う。Python3、GitがローカルのPCにインストールされているかの確認、および本カリキュラムのレポジトリのクローンを行う。(Python2 しかインストールしていない方は、Python3 をインストールする必要がある。)私が実施したローカル環境は以下のバージョンであった。

$ python -V
Python 3.7.7
$ git --version
git version 2.27.0

確認後、お気に入りのサイコロゲームを issue のコメント欄に記載して Step. 1 は終了となる。

Step2. Running a Python program

ここでは、実際にPythonコードの動作確認、そして関数の使い方を学ぶ。 コメントアウトされている部分を解除して、表示確認ができれば OK である。

結果をcommitして、pushすれば、Step. 2 は終了となる。 git に不慣れな方でも大丈夫なように丁寧なコマンドが載っているので、git 操作に関してはコピペでもいけるので安心して欲しい。

Step3. Using Variables

ここでは、変数の使い方を学ぶ。 サイコロの出る目はランダムに変わるので、乱数の作り方、さらには変数をどのように表示すればいいか、 random f-string を使って実装する方法を学ぶ。

乱数で表示された結果を issue のコメント欄に貼ることで、Step. 3 は終了となる。

Step4. Rolling multiple Dice

先ほどは、一つのサイコロを乱数で表示させたが、ここでは複数のサイコロの目を表示する方法を考える。 反復処理を行う際に用いられる for の使い方を実践を通して学ぶことができる。

dice_rolls = 2
dice_sum = 0
for i in range(0,dice_rolls):
  roll = random.randint(1,6)
  dice_sum += roll
  print(f'You rolled a {roll}')
print(f'You have rolled a total of {dice_sum}')

反復処理をしながら、サイコロの出た目の合計値を計算する方法も詳しく説明してあり、非常にわかりやすかった。

結果をcommitして、pushすれば、Step. 4 は終了となる。

Step5. Adding Conditionals

ここでは、条件分岐 if の使い方を実践を通して学ぶ。 サイコロの出た目に応じて、表示を切り替えるように実装を行う。if の使い方に関しても、詳細に書かれており、わかりやすかった。実際のコードは、for文の中に含まれて、少し複雑に見えるが、ステップアップで実装してきたので、理解しやすいように思う。

dice_rolls = 2
dice_sum = 0
for i in range(0,dice_rolls):
  roll = random.randint(1,6)
  dice_sum += roll
  if roll == 1:
    print(f'You rolled a {roll}! Critical Fail')
  elif roll == 6:
    print(f'You rolled a {roll}! Critical Success!')
  else:
    print(f'You rolled a {roll}')
print(f'You have rolled a total of {dice_sum}')

issue のコメント欄に「Critical Success」を表示させるためのサイコロの目の数を記載して、Step. 5 は終了となる。

Step6. Adding User Input

最後に、サイコロの数とサイコロの面数をユーザーの入力で自由に決定できるようにする。これまでは、サイコロの数は2個、サイコロの面は6面としていたが、この変数をユーザーの入力を受け付けて、結果を出力するようにする。

dice_rolls = int(input('How many dice would you like to roll? '))
dice_size = int(input('How many sides are the dice? '))
for i in range(0,dice_rolls):
 roll = random.randint(1,dice_size)
 ~ 省略 ~

動作確認まで完了したら、結果をcommitして、pushすれば、本カリキュラムは終了となる。 発展としては、以下が挙げられている。

  • プレイヤーやチーム名を追加するにはどうすればいいか
  • 配列で各プレイヤーのサイコロの出た目を保存するにはどうすればいいか
  • 本当のサイコロゲームを実際にPythonで実装してみよう

まとめ

Pythonの入門コースのひとつの選択肢として、「GitHub Learning Lab」の「Intermediate Python」は非常に有用だと思えた。サイコロゲームという誰でも知っているゲームを実際に自分の手で簡単に作れることは初学者にとってとてもいい感動体験ではないかと思う。また、こちらをベースとして発展的なアプリケーションを作ることもできるので、さらなる学びが得られるのではないかと思う。また、何といっても、Bot とのインタラクティブな対話をしながら進める実装は楽しい体験になると思う。是非、一度体験してみて欲しい。

それでは、ステキな開発ライフを。