クックパッドの Next.js と GraphQL へのリプレース
今日のタブ記事はこちら。
レシピサービスのフロントエンドを Next.js と GraphQL のシステムに置き換えている話
クックパッドは Rails で有名な会社なので、Next.js と GarphQL に置き換えるのすごいな〜と思ったら、CoffeeScript や jQuery が現役で稼働している状態からの置き換えらしい。すごすぎる……
さすがにクックパッドくらいの大きなプロダクトだと一度に全ては無理だよなと思ったらちょっとずつ変えているとのこと。というかクックパッドレベルだとどんな技術の入れ替えや追加や削除でもちょっとずつになりそうだなあ。
- まず TypeScript を中心に据えることを決め
- 次に TypeScript と相性がいい React を使うことを決め
- サイトの性質上 SSR が必要なので Next.js を使うことを決める
という技術選定らしい。合理的だと思う。
そして Next.js から API を直接呼ぼうと思ったが認証や Req/Res の型付けなどの理由で BFF サーバーを挟みそこで GraphQL を利用。自分は GraphQL を利用したことがないので想像でしかないんだけど、Next.js と BFF サーバーのやり取りは GraphQL で、BFF サーバーと API サーバーのやり取りは REST でということなのだろうか。
BFF サーバーに GraphQL を利用する利点として、graphql-codegen というライブラリを利用することで Req/Res の型定義ファイルを自動生成できるらしい。今は大量の Req/Res の型定義があるプロダクトを扱っているので、型定義を自動生成できるのはすごいな〜と思う。
そしてこういうだんだんリプレースしていく系の記事でいつも「特定のページだけ新しいほうを返すのってどうやるの?」ということを疑問に思うのだけど、記事によるとリバースプロキシ(Nginx)で制御しているらしい。
スマートフォンからの/recipe/:id
を Next.js へ、/graphql
を GraphQL の BFF サーバーへルーティングして、それ以外をもともとのモノリシックな Rails のほうへルーティングしているということらしい。なるほど、こうやるのか。
パフォーマンスもかなり改善しているらしく、FCP(First Contentful Paint)がもともと遅かったのがよくなったとのこと。
改善できた理由として、巨大な CSS や defer できない JS が head で読まれていたせいでクリティカルレンダリングパスが最適化できていないことらしい。
分からない言葉がたくさん出てきたのでまとめて調べよう。
FCP
FCP を全然知らなかったのでググったのだが、
First Contentful Paint (FCP) は、ブラウザーが DOM からコンテンツの最初のビットをレンダリングし、ページが実際読み込み中というユーザーへの最初のフィードバックがなされる時間です。
上記はMDNの引用。
リクエストが飛んでからロード中のインジケータが出るまでみたいなことで合ってるのかな?
defer
defer 自体は「延期する」みたいな意味らしい。
わかりすい説明があった。
script タグの属性に async/defer というのがあって、
- 何もつけないと HTML のパース中に script ファイルのダウンロードと実行の処理が割り込みで入ってきて、それが終わるまでは HTML のパースが中断される同期的な処理
- async をつけると、HTML のパースと script ファイルのダウンロードを非同期で行い、script ファイルのダウンロードが終わったら HTML のパースを中断して同期的に script を実行し、それが終わったら中断していた HTML のパースを再開する
- defer をつけると、HTML と script ファイルのダウンロードを非同期で行うまでは async と一緒で、script ファイルのダウンロードが終わっても HTML のパースが終わるまで実行せず、パースが終わったらダウンロードした script ファイルを実行する
ということらしい。
defer を使うことで script タグが書かれた順にちゃんと実行されるのが保証される上、HTML のパースが終わって DOM の構築が完了しているので DOM 操作が確実にできる、とのこと。
クリティカルレンダリングパス
クリティカルレンダリングパスの最適化を参考にしています。
クリティカルレンダリングパスとは「ユーザーの操作に関連するコンテンツの順位付け」という意味らしい。これだけ聞いてもピンとはこない。
これを最適化するには、CSS の最適化と JS の最適化をするといいらしい。
最適化するというのは、ファーストビューのコンテンツに必要な CSS/JS だけをインライン化し、残りの CSS/JS はページの下のほうで読み込ませるようにする、ということらしい。
まずもって、ブラウザは HTML の解析中に外部スクリプトに遭遇するとその実行が終わるまで解析を中断したり、外部 CSS のダウンロードと処理が完了するまでコンテンツの描画をブロックしたりするらしい。それを避けるためにはインライン化が必要だけど、全てをインライン化するわけにはいかないので、ファーストビューの表示に関連するものだけをインライン化するのがよいとのこと。なるほどな〜。
もどる
これらを学んだ上で戻ると、「巨大な CSS や defer できない JS が head で読まれている」というのはかなりキツいことが分かった。
defer できないということはつまり全て同期的にしか処理が進められないということなのでかなり渋そう。
defer できない原因は、haml に書いてある JS が head で読まれる JS に依存しているかららしい。Rails の View Template を使うとそういう問題もあるんだな……。
あと Next.js には Web Vitals の計測機能があるそうなので、今度使ってみたい。
JSON の Dynamic Key について
昨日の投稿で JSON どっちがいいかみたいな話が盛り上がってたの見て書かれた記事だと思う。
ツイートをもう一度貼っておくと
JSON詳しい方にお聞きしたいのですが、Aみたいなデータを返すREST APIの流派があるんでしょうか??
— Tetsu (@wtetsu) June 7, 2021
サーバサイドでもフロントエンドでも、なんかAPI呼んでAが返ってきたら使いづらくて仕方ない気がするのですが…
※私はBにすべきだと思うが「Aがモダンでスタンダード」的な主張とぶつかっている pic.twitter.com/O9NGvmXyRu
これの A は、そもそも JSON の定義としては不適切らしい。あと Array の中は型を定義できるが Map の中だとできないらしい。そうなのか。
A も使われるシーンはあるらしく、一概に A のほうが悪いとは言えないらしい。なるほど。
トレイリングスラッシュについて
「このままだと攻撃者の思う壺」イオンカードで不正利用の注意喚起のはずが…大間違いだった
togetter でイオンカードの不正利用についての注意喚起がよくない書き方してるよ、と話題になっていた。
https://......jp/ とするのを後ろの /が抜けて一大事ってとこですかね。
こういうリプライを見つけたんだけど、つまりトレイリングスラッシュが抜けていてよくない、ってこと?だよね?
トレイリングスラッシュってめちゃ大事じゃん、知らなかった……と思ってググってみた。
- トレイリングスラッシュがない場合はファイルへのリクエスト
- トレイリングスラッシュがある場合にはディレクトリへのリクエスト
で、
- ディレクトリにリクエストするとそのディレクトリが返すべきファイル(だいたい index.html とか)が返される設定をしてあることがほとんど
さらに
- トレイリングスラッシュなしでファイルにリクエストし、そのファイルがない場合 404 になるが、設定してあるとトレイリングスラッシュをつけた状態でもう一度探してくれる
らしい。
色々ググってみたんだけど、トレイリングスラッシュなしがセキュリティ的にまずいというのが分からなかった。誰か詳しい人教えてほしい……。