Supabase를 사용하여 Nextjs에서 로그인 기능 구현하기

Table of Contents

Next.js 앱에서 supabase auth를 사용해서 로그인 하는 방법을 정리합니다.

Client Component를 사용한 로그인

supabase 클라이언트 만들기

import { createBrowserClient } from '@supabase/ssr';

export function supabaseBrowser() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
}

클라이언트 컴포넌트에서 로그인

'use client';

import { supabaseBrowser } from '@/utils/supabase/browser';
import Link from 'next/link';
import React from 'react';

export default function Page() {
  const supabase = supabaseBrowser();

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    await supabase.auth.signInWithPassword({
      email: (e.currentTarget.elements[0] as HTMLInputElement).value,
      password: (e.currentTarget.elements[1] as HTMLInputElement).value,
    });
  };

  return (
    <main className="flex flex-col items-center justify-between p-24">
      <h1>Log In</h1>
      <form className="flex flex-col gap-2" onSubmit={handleSubmit}>
        <input type="email" id="email" placeholder="Email" />
        <input type="password" id="password" placeholder="Password" />
        <button type="submit" className="bg-blue-500 p-1 rounded-xl">
          Login
        </button>
      </form>
      <div>
        Go to <Link href="/auth/signin">Sign In</Link>
      </div>
    </main>
  );
}

Server Component Client를 사용한 로그인

supabase 클라이언트 만들기

import { CookieOptions, createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';

export function supabaseServer() {
  const cookieStore = cookies();
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          cookieStore.set({ name, value, ...options });
        },
        remove(name: string, options: CookieOptions) {
          cookieStore.delete({ name, ...options });
        },
      },
    }
  );
}

API를 사용한 로그인

로그인 Form

export default function Page() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.id === 'email') {
      setEmail(e.target.value);
    } else {
      setPassword(e.target.value);
    }
  };

  return (
    <main className="flex flex-col items-center justify-between p-24">
      <h1>Log In</h1>
      <form
        action="/api/auth/login"
        method="POST"
        className="flex flex-col gap-2"
      >
        <input
          type="email"
          id="email"
          name="email"
          placeholder="Email"
          value={email}
          onChange={onChange}
        />
        <input
          type="password"
          id="password"
          name="password"
          placeholder="Password"
          value={password}
          onChange={onChange}
        />
        <button type="submit" className="bg-blue-500 p-1 rounded-xl">
          Login
        </button>
      </form>
      <div>
        Go to <Link href="/auth/signin">Sign In</Link>
      </div>
    </main>
  );
}

로그인 API Handler

import { supabaseServer } from '@/utils/supabase/server';
import { NextResponse } from 'next/server';

export const dynamic = 'force-dynamic'; // defaults to auto
export async function POST(request: Request) {
  const supabase = supabaseServer();
  const requestUrl = new URL(request.url);
  const formData = await request.formData();
  const email = formData.get('email') as string;
  const password = formData.get('password') as string;
  const data = await supabase.auth.signInWithPassword({
    email,
    password,
  });

  return NextResponse.redirect(requestUrl.origin, {
    status: 301,
  });
}

Server Action을 사용한 로그인

server action

'use server';
import { supabaseServer } from '@/utils/supabase/server';
import { redirect } from 'next/navigation';

export async function login(formData: FormData) {
  const supabase = supabaseServer();

  const email = formData.get('email') as string;
  const password = formData.get('password') as string;
  const data = await supabase.auth.signInWithPassword({
    email,
    password,
  });

  await supabase.auth.signInWithPassword({ email, password });

  redirect('/');
}

로그인 Form

'use client';

import { login } from '@/app/action';
import { supabaseBrowser } from '@/utils/supabase/browser';
import Link from 'next/link';
import React from 'react';

export default function Page() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.id === 'email') {
      setEmail(e.target.value);
    } else {
      setPassword(e.target.value);
    }
  };

  return (
    <main className="flex flex-col items-center justify-between p-24">
      <h1>Log In</h1>
      <form action={login} className="flex flex-col gap-2">
        <input
          type="email"
          id="email"
          name="email"
          placeholder="Email"
          value={email}
          onChange={onChange}
        />
        <input
          type="password"
          id="password"
          name="password"
          placeholder="Password"
          value={password}
          onChange={onChange}
        />
        <button type="submit" className="bg-blue-500 p-1 rounded-xl">
          Login
        </button>
      </form>
      <div>
        Go to <Link href="/auth/signin">Sign In</Link>
      </div>
    </main>
  );
}