Open9

最近テストメーカーをServer Componentsに移行しようとした結果

meijinmeijin

データフェッチする関数はServer Actionsなので、コンポーネント内で宣言しようとしたらコンポーネントごとuse serverになってしまう。なので自然にやるなら関数は別ファイルに切ることが推奨っぽい

※追記
もしかしてMutationする関数だけServer Actionsって呼ぶ?そこがごっちゃになっているので、以下、use serverディレクティブを必要とする関数を全部Server Actionsと呼んでいる

meijinmeijin
  • SWRチックに、Server Actionsでフェッチしたデータを再読込したい場合はrevalidatePath関数を使う。この関数もServer Actions内で呼ぶ必要があり、かなり面倒だが、宣言した関数をServer ComponentsからClient ComponentsにPropsで渡していく必要があるっぽい。実際には、この関数をContextに注入する形で解決した
    • さらにいうと、Server ActionsにMutateも統一しておけば、Mutate時にいたって自然な流れでrevalidateできるので、本検証において中途半端にMutateをClientから普通に叩いたことが原因かも
    • export const revalidate = 0にしても、router.pushで切り替えたときにデータが最新に更新される挙動にならなかったので、fetch関数以外に(router側に)キャッシュレイヤーがありそう。一方で、逐一revalidatePathを実行すると、キャッシュを確実に破棄し、最新のデータに変えることができる
meijinmeijin

Server Components→Client Componentsは呼べるが、逆はダメなので、既存コードでフックをカジュアルに呼んでいるところを、各コンポーネントに下げていく作業が必要だった。コンポーネントツリーの大本でフックを呼んでいると詰む

Composition Patternである程度はいけるけど・・・

meijinmeijin

ツール系のサービスなので、layout.tsxをちゃんと組むと結構便利になるので頑張り甲斐はあった。layout.tsxにサイドバーを入れて、サイドバー内のデータをServer Componentsでフェッチすることにした

meijinmeijin
  • テストメーカーはFirebase Authを使っていて、Firebase Auth経由でCookieを発行することで独自バックエンドでも認証を通している。ユーザーがGoogleログインしたとき、ログインページでgetRedirectResultを実行して、ログイン判定して管理ページに自動遷移しているが、管理ページにrouter.pushで遷移すると、管理ページ内のServer Actionsにおいてcookies()関数の返り値が空のままになっている(この時点でCookieを持っているのはClientだけなので)ことでエラーになってしまう
    • テストメーカーの認証が独特ではあるが、逆に言えば、Cookie付与するタイプのサービスで、ログイン後最初にServer Componentsにアクセスしたときにcookies()がまだ空です、といった場面は起き得ないのだろうか?
      • login.tsxからlogin APIをポスト(※ここをServer Actionsにしていないのが悪いという話かもしれない)
      • ポスト後、APIのResponseにSet-Cookieヘッダがありセッション用のCookieを獲得
      • router.pushで管理ページに遷移
      • 管理ページ内のServer Componentsにおいて、Fetch時にcookies()でCookieを取得できないので失敗する※バックエンドにCookieが飛ぶのはHTTPリクエストが飛んだときのはずだが、Server Componentsをrouter.pushの遷移後にFetchする際にCookieが飛んでいないっぽい。ここの詳しい原理は謎
    • ちなみに、ログイン後必ずリロードすることで、一応回避はできたが、SPAとしては敗北を感じる。かつ、Stagingではこの策で認証を通したが、ProductionではCookieの付与に失敗してServer Components内で認証が通らなくなりエラーが頻発した
    • 余談だが、Server Actionsでエラーが起きるとProductionではエラーがOmitされるので、Sentryに投げることが必須になるのだが、普通にSentryを呼んでも記録されなかった。もしかするとServerでの実行になるので勝手が違うのかも。要調査
      • この背景があるので、Productionで落ちたとき原因がわからずただRevertしただけになってしまった
      • 今確認したら個人開発のSentryはケチって無料プランなのでQuota使い切っていただけっぽい。お金払うかぁ
    • 自前でCookieの付与、破棄をやっているのが諸悪の根源なので、NextAuthとか使うといいのかもしれない※NextAuthで確実に治るとはいえないが、文献は増える
meijinmeijin
  • i18n対応の結果、ルートにi18nClientProviderが入ってしまっていたので引き剥がしてツリーの末端に寄せた。
    • これもできるだけServerに寄せるのがベストっぽい
meijinmeijin
  • 全体的な所感として、テストメーカーのようなツール系のサービスではServer Componentsを使う嬉しさがそんなに無いわりに、既存コードの組み換えが多くて結構面倒だった。layout.tsxなどは普通に使えるので、Router周りの機能の恩恵だけ受けておいてもいいかも
    • 嬉しさがそんなに無いとは書いたけど、loading.tsxとかスケルトン細かいところまで組み上げたら、SWRの完全な代替品or上位互換にできそうなので、もう一踏ん張りかも
    • また、認証が一般的なCookie発行フローに沿っていないと罠にハマりそうとも感じた。ログインAPIでCookieを発行し、ブラウザで受け取ったのを確認してリロード、で一般的に良さそうではあるが、テストメーカーではProductionで失敗してしまったので不安定である。2024/06/10現在もProductionではServer Components移行ができていない
    • 個人的には、テストメーカーはWYSIWYGエディタなどをふんだんに使っている関係で、Client Componentsの境界を意外とルート寄りにしないといけないので、技術的には頑張り甲斐はあるけど、費用対効果があまり大きくないかなぁとも思うので、Server Componentsに向いているサービスを別途個人開発してもよさそう・・・
meijinmeijin

思ったより戦い方が違いそうだから、既存Pages Routerアプリケーションの延長線上で考えるより、Examples読みまくって全体感を捉えたほうがよさそう

仮にログインとかMutation系は基本formタグでやるのが推奨ですよ、だったら本当に組み換えになってしまう