
โ Installation
์ฟ ํค ๊ธฐ๋ฐ ์ธ์ฆ(Session)์ ์๋ฒ์ ํด๋ผ์ด์ธํธ์์ ๋์์ ๋ค๋ฃจ๊ธฐ ์ํด ์ค์น
npm install @supabase/supabase-js @supabase/ssr
https://supabase.com/docs/guides/auth/server-side/creating-a-client
Creating a Supabase client for SSR | Supabase Docs
Configure your Supabase client to use cookies
supabase.com
โ Usage
<app/supa-client.ts>
export const browserClient = createBrowserClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_PUBLISHABLE_KEY!,
);
export const makeSSRClient = (request: Request) => {
const headers = new Headers();
const serverClient = createServerClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_PUBLISHABLE_KEY!,
{
cookies: {
getAll() {
return parseCookieHeader(request.headers.get("Cookie") ?? "").map(
({ name, value }) => ({ name, value: value ?? "" }),
);
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
headers.append(
"Set-Cookie",
serializeCookieHeader(name, value, options),
);
});
},
},
},
);
return { client: serverClient, headers };
};
<login-page.tsx>
const formSchema = z.object({
email: z
.string("Email should be a string")
.min(1, "Email is required")
.email("Invalid email address"),
password: z
.string()
.min(1, "Password is required")
.min(5, "Password must be at least 5 characters"),
});
export const action = async ({ request }: Route.ActionArgs) => {
const formData = await request.formData();
const { success, data, error } = formSchema.safeParse(
Object.fromEntries(formData),
);
if (!success) {
return {
formErrors: z.flattenError(error).fieldErrors,
loginError: null,
};
}
const { email, password } = data;
const { client, headers } = makeSSRClient(request);
const { error: loginError } = await client.auth.signInWithPassword({
email,
password,
});
if (loginError) {
return { formErrors: null, loginError: loginError.message };
}
return redirect("/", { headers });
};
<Form className="w-full space-y-4" method="POST">
<InputPair
id="email"
label="Email"
description="Enter your email address"
name="email"
required
type="email"
placeholder="i.e wemake@example.com"
/>
{actionData && "formErrors" in actionData && (
<p className="text-sm text-red-500">
{actionData.formErrors?.email?.join(", ")}
</p>
)}
<InputPair
id="password"
label="Password"
description="Enter your password"
name="password"
required
type="password"
placeholder="Enter your password"
/>
{actionData && "formErrors" in actionData && (
<p className="text-sm text-red-500">
{actionData.formErrors?.password?.join(", ")}
</p>
)}
<Button type="submit" className="w-full" disabled={isSubmitting}>
{isSubmitting ? (
<LoaderCircle className="animate-spin" />
) : (
"Log in"
)}
</Button>
{actionData && "loginError" in actionData && (
<p className="text-sm text-red-500">{actionData.loginError}</p>
)}
</Form>'๐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Cloudflared (0) | 2026.07.21 |
|---|---|
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - React Router v7 - useFetcher (0) | 2026.06.26 |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - React Router v7 - clientLoader, HydrateFallback (0) | 2026.05.29 |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - React Router v7 - prefetch (1) | 2026.05.29 |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ - Supabase JavaScript Client Library (0) | 2026.05.27 |