はじめに
web系を中心に開発している、エンジニアの yh1110 です。
個人開発として進めているwebアプリにて、cookieの扱い方で行き詰まった部分があったので備忘録として書き残しておきます。
サーバーアクションまわりでの扱い方で少し苦戦したので誰かの助けになれば幸いです。
概要
- server actionsでのcookieの扱い方
- client, server componentでの扱い方
前提
@next/headersのcookiesについての扱い方を主に書いていきます。
本題
そもそもとしてNext.jsのApp Routerでは、cookiesの.set()や.delete()が使えるのは サーバーアクションかルートハンドラーでしか使用できません。(document.cookieは問題ないですが、今回の趣旨とはずれるので割愛)
基本的にサーバーコンポーネントやクライアントコンポーネントではcookieの値の操作は取得のみとなります。
参考↓
なぜサーバーアクション or ルートハンドラーのみか
ざっくりした解説
cookieとはブラウザにkeyとvalueにて保存されます。
headerにcookieの値をくっつけてブラウザにリクエスト → 無事ブラウザのcookieに保存されます。
例えば下記のようなクッキーをセットするだけのサーバーアクションが実行されると
"use server";
import { cookies } from "next/headers";
export async function setCookies() {
const cookieStore = await cookies();
const cookie = cookieStore.set("key", "value");
}
サーバーアクションの実行をトリガーとして、このようなヘッダーをブラウザに投げます。
Set-Cookie: key=value; Path=/
このような処理の流れとなるので、ブラウザに対してcookieをどう送るかがカギとなります。
ルートハンドラも同様です。
cookie情報を含んだヘッダーはブラウザに対して投げないと、セットできないためエラーとなります。
サーバーコンポーネントもSSRでリクエスト送ってるからcookieのセット出来るんじゃないのか
SSRはレスポンスを返す処理ではないので出来ません。
あくまでもサーバーでReactをレンダリングしているにすぎないので、レスポンス自体にはアクセスできません。
また、Next.jsの設計の思想上、レスポンスを書き換える副作用の処理なので、制限されています。(宣言的UIの原則に反する)
クライアントコンポーネントはさすがに出来ますよね
cookieをセットすることは出来ますが、document.cookie
でしか出来ないです。
そもそもnext/headersがnode.jsランタイム上で動いているため、エラーとなります。
セキュアな値でなければdocument.cookie
でも問題ないし、テーマとかのUI関連ならむしろそっちを使うべきだと思っています。
実行コンテキスト制限
これは私がハマったとこなのですが、下記のようにcookieをセットする関数を作成し、サーバーアクション内で呼び出すとエラーとなります。
// actions/setCookiesAction.ts
"use server";
import { cookies } from "next/headers";
import { setCookie } from "@/utils/setCookie";
export async function setCookiesAction() {
await setCookie();
}
// utils/setCookie.ts
"use server"
export async function setCookie() {
const cookieStore = await cookies();
const cookie = cookieStore.set("key", "value");
}
ざっくりした解説
cookie().set()
を読んだコードがサーバーアクションのコンテキスト内でなければエラーとなります。
cookies().set()
は副作用を発生させる(レスポンスヘッダーを書き換える)ため、Next.jsが完全に責任を持って実行・追跡出来ないからです。
そのためcookieのセットなどの副作用APIはサーバーアクションのトップレベルで使用する必要があるということになります。
じゃあ取得はなんでサーバーコンポーネントでもOKなのか
リクエストに含まれてるヘッダー情報を読み取る処理だからです。
.set()
の場合はレスポンスを書き換えるような「副作用」を持っていますが、取得に関しては純粋な読み取りなので、問題ないです。
まとめ
cookieに関してはoAuth等で使用することはありますが、特に気にも留めずに (エンジニアとして良くない) 実装することが大半だったので、エラーを通じてcookieの仕様を「完全に理解」しました。
cookieのセットに関しての制約が意外とありますが、それと同時にNext.jsの設計思想を強く感じました。
ここまで読んでいただきありがとうございました!