Open9
最近テストメーカーをServer Componentsに移行しようとした結果
テストメーカー
- App Routerには移行済み
- 設計はPages Router時代のまま
データフェッチする関数はServer Actionsなので、コンポーネント内で宣言しようとしたらコンポーネントごとuse serverになってしまう。なので自然にやるなら関数は別ファイルに切ることが推奨っぽい
※追記
もしかしてMutationする関数だけServer Actionsって呼ぶ?そこがごっちゃになっているので、以下、use serverディレクティブを必要とする関数を全部Server Actionsと呼んでいる
- 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を実行すると、キャッシュを確実に破棄し、最新のデータに変えることができる
Server Components→Client Componentsは呼べるが、逆はダメなので、既存コードでフックをカジュアルに呼んでいるところを、各コンポーネントに下げていく作業が必要だった。コンポーネントツリーの大本でフックを呼んでいると詰む
Composition Patternである程度はいけるけど・・・
ツール系のサービスなので、layout.tsxをちゃんと組むと結構便利になるので頑張り甲斐はあった。layout.tsxにサイドバーを入れて、サイドバー内のデータをServer Componentsでフェッチすることにした
- テストメーカーは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で確実に治るとはいえないが、文献は増える
- テストメーカーの認証が独特ではあるが、逆に言えば、Cookie付与するタイプのサービスで、ログイン後最初にServer Componentsにアクセスしたときにcookies()がまだ空です、といった場面は起き得ないのだろうか?
- i18n対応の結果、ルートにi18nClientProviderが入ってしまっていたので引き剥がしてツリーの末端に寄せた。
- これもできるだけServerに寄せるのがベストっぽい
- 全体的な所感として、テストメーカーのようなツール系のサービスではServer Componentsを使う嬉しさがそんなに無いわりに、既存コードの組み換えが多くて結構面倒だった。layout.tsxなどは普通に使えるので、Router周りの機能の恩恵だけ受けておいてもいいかも
- 嬉しさがそんなに無いとは書いたけど、loading.tsxとかスケルトン細かいところまで組み上げたら、SWRの完全な代替品or上位互換にできそうなので、もう一踏ん張りかも
- また、認証が一般的なCookie発行フローに沿っていないと罠にハマりそうとも感じた。ログインAPIでCookieを発行し、ブラウザで受け取ったのを確認してリロード、で一般的に良さそうではあるが、テストメーカーではProductionで失敗してしまったので不安定である。2024/06/10現在もProductionではServer Components移行ができていない
- 個人的には、テストメーカーはWYSIWYGエディタなどをふんだんに使っている関係で、Client Componentsの境界を意外とルート寄りにしないといけないので、技術的には頑張り甲斐はあるけど、費用対効果があまり大きくないかなぁとも思うので、Server Componentsに向いているサービスを別途個人開発してもよさそう・・・
思ったより戦い方が違いそうだから、既存Pages Routerアプリケーションの延長線上で考えるより、Examples読みまくって全体感を捉えたほうがよさそう
仮にログインとかMutation系は基本formタグでやるのが推奨ですよ、だったら本当に組み換えになってしまう