こんにちは、サーバーサイドエンジニアの柿本です。
みなさんは Jamstack をご存知ですか??!!
先日知人が Jamstack について熱く語ってくれまして、私は「ナニソレオイシイノ?」と思って聞いていたのですが、彼があまりに熱く語るので「サゾカシオイシイニチガイナイ!」ということでいろいろ調べてみました。
「 SPA と SSR のいいとこ取り」や「動的サイトと静的サイトのいいとこ取り」といった魅力的な言葉が並ぶのですが、つまるところ、
サイトの中の静的な箇所は完全に静的ファイル化しておいて、動的な部分はAPIを叩いてよろしくやろうぜ!
ということのようです。
早速入門してみようとしたところ、 Jamstack の入門記事は主に ヘッドレス CMS や SSG (静的サイトジェネレータ)との繋ぎ込みなど 静的ファイル化部分 に主眼が置かれておりました。
動的によろしくやる方法を知りたかったので、自分でやってみることにします。
TL;DR
1クリックで Netlify にデプロイすることができます!
事前準備として FaunaDB のセットアップと Server secret の取得 が必要です。
デプロイ完了後に Netlify Identity の設定 が必要です。
目次
技術スタック
- 静的サイトジェネレータ: Next.js
- ソースコード管理: GitHub
- ホスティング・CDN: Netlify
- データベース: FaunaDB
- ヘッドレスCMS: 利用なし(*動的部分をターゲットとしてるので使いません)
Jamstack の構成としては標準っぽい物を選定しました。
構想
動的サイトといえば ユーザーログイン や データ保存が基本となるので、 『ユーザー登録制の掲示板サイトもどき』 を作ってみます。
ユーザーストーリー
- ユーザーがログインできる
- ユーザーが自分の名前でメッセージを投稿できる
- メッセージ一覧を誰でもみれる
画面構成
/ フロントページ(メッセージ一覧) └/ メッセージ投稿画面
非常にシンプルですね!
最終形態
最終成果物
GitHub に公開 しました!
開発手順の詳細も README に記載してあるので、参考にしてみてください。
開発の流れ
- Next.js のセットアップ
- GitHub に push
- Netlify にデプロイ
- Netlify Identity の設定
- フロントコーディング
- FaunaDB の設定
- Netlify Functions コーディング
実装のポイント
フロントエンド
フロントエンドを構成するソースファイルは以下です。
dynamic-jamstack (App root) ├── pages │ ├── index.js // ホーム(メッセージ一覧)画面 │ └── new_message.js // メッセージ投稿画面 ├── components │ ├── Message.js // 1つのメッセージ表示 │ ├── MessageList.js // メッセージを一覧で表示&データ取得処理 │ └── MessageForm.js // メッセージ入力フォーム&データ送信 └── utils └── netlifyAuth.js // Netlify Identityの認証処理
/pages/
配下は Next.js により静的化されるページです。
デプロイ時に静的化されるため、ここに動的な処理(データ取得&描画)を記述しても想定どおりに動きません。
/components/
配下は React コンポーネントです。
動的な処理をここに記述し、 pages のファイルで読み込むことで動的な挙動を実現できます。
Netlify Functions (API)
API のロジックは以下で構成されます。
dynamic-jamstack (App root) └── functions ├── messages-create.js // メッセージ保存 └── messages-read-all.js // メッセージ一覧取得
Netlify Functions の中身は AWS Lambda なので、書き方は同じです。
/messages-create
はログイン済みのユーザーのみに制限する必要があり、クライアントと Functions のそれぞれで以下の対応が必要です。
クライアント - Header に Netlify Identity Widget から取得した token をセットします。
// components/MessageForm.js generateHeaders() { const headers = { "Content-Type": "application/json" } if (netlifyIdentity.currentUser()) { return netlifyIdentity.currentUser().jwt().then((token) => { return { ...headers, Authorization: `Bearer ${token}` } }) } return Promise.resolve(headers) }
Functions - context.clientContext.user
でユーザー情報を取得することができます。
// functions/messages-create.js const user = context.clientContext && context.clientContext.user; if (!user) { return callback(null, { statusCode: 401, body: JSON.stringify({ "error": "You must be signed in to call this function" }) }) }
考察
今回は "Jamstack構成で動的な処理を実装する" を目的として Next.js をベースに
- 動的な処理を React コンポーネントで記述
- 静的ファイル化される箇所からコンポーネントを呼び出し
として実現しました。
わかったこと
- Jamstack 構成で動的サイトは実現可能
- ユーザー認証は Netlify Identity で実現可能
- ローカル(静的ファイル内)でも API 内でもユーザー情報を取得可能
- データ保存はサーバーレスのデータストアにより実現可能
課題
1) サイトによる向き不向きがありそう
いわゆる SNS のような動的な処理がメインのサイトの場合、静的ファイル化できる部分が限られるためあまり恩恵を受けられないかもしれません。
メリットを最大化するには静的化する箇所を適切に切り出す必要がありそうです。
例えば弊社サービスのルクミーフォトの販売画面(購入する写真を保護者が選択する画面)などは、販売写真の設定がされたタイミングで静的ファイル化すればハイパフォーマンスのメリットがありそうです。
そのためには incremental build が適切に実行されるようにする、などの複雑な設定も必要になるかと思います。
2) pages 配下は静的ファイル化される、と意識してコーディングする必要がある
Next.js の仕組み上当たり前のことなのですが、ローカルの開発サーバー上でホットビルドをしているとついつい忘れてしまいます。
これは慣れの問題だとは思いますが、 Netlify にデプロイしてから「あれれ?」となることが何度かありました。(笑)
まとめ
Jamstack で動的サイトを作ることが可能なことは分かりましたが、感想としては「ちょっと無理やり感が否めない」といったところです。
とはいえ Netlify などの CDN 経由で静的ファイルを配信できることのメリットは大きいので、サイトの向き不向きは選びますが、活用していければ良いなと思いました。
ユニファでは保育をハックするエンジニアを募集中です。 一緒にスマート保育園を実現しましょう!
Appendix
FaunaDBの設定
https://fauna.com/ からアクセスし、 Signup します。 Netlify のアカウントでも Signup できます。
任意の名前で Database を作成し、以下を設定します。
- Collection - Name:
messages
- Index - Name:
all_messages
SECURITY メニューから key を作成し、 Server secret を取得します。
Server secret は2度と表示されないので、無くさないように保管します。
Netlify Identityの設定
Netlify の dashboard の Site 一覧から対象の Site を選択します。
Identity
> Enable Identity
の順にクリックしてアドオンを有効化します。
Setting and usage
> Registration
> External providers
で追加したい認証プロバイダーを選択します。