본문 바로가기

Projects/2024

[ Project3 / 로그인 ]Supabase 이용한 로그인 (Feat. Auth)

import { login, signup } from './actions'

export default function LoginPage() {
  return (
    <form>
      <label htmlFor="email">Email:</label>
      <input id="email" name="email" type="email" required />
      <label htmlFor="password">Password:</label>
      <input id="password" name="password" type="password" required />
      // button에 actions.ts의 login 연결
      <button formAction={login}>Log in</button>
      // button에 actions.ts의 singup 연결
      <button formAction={signup}>Sign up</button>
    </form>
  )
}

로그인 기능을 붙이려고 생각 중이였다. 어떤 걸 사용할까 생각하다. 현재 DB로 Supabase를 이용하니, 여기 auth 를 써보자고 생각했다.

아래의 문서는 SSR기준이다.

Supabase auth DOC

 

Setting up Server-Side Auth for Next.js | Supabase Docs

Be careful when protecting pages. The server gets the user session from the cookies, which can be spoofed by anyone. Always use supabase.auth.getUser() to protect pages and user data. Never trust supabase.auth.getSession() inside server code such as middle

supabase.com

 

그냥 따라서 붙이기만 하면 된다.

다만 주의해야할께 폴더의 위치이다.

 

처음 이것을 잘못해서, 좌절을 맞봤다. 왜 틀렸는지를 이해하지 못했다. 그냥 폴더는 잘못 생성한 것 뿐이다.

Youtube를 검색하여 폴더 위치를 재확인했다.

 

Middleware Setting

middleware 를 만드는 것이 가장 중요할 것이다.

Create a middleware.ts file at the root of your project.

 

 

code from supabase

import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'

export async function updateSession(request: NextRequest) {
  let supabaseResponse = NextResponse.next({
    request,
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
          supabaseResponse = NextResponse.next({
            request,
          })
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options)
          )
        },
      },
    }
  )

  // IMPORTANT: Avoid writing any logic between createServerClient and
  // supabase.auth.getUser(). A simple mistake could make it very hard to debug
  // issues with users being randomly logged out.


  // 여기 아래 코드가 user 정보를 가져오는 미들웨어 로직이다.
  // 현재 코드는 내가 만든 사이트를 들어갔을 때, user(즉 로그인)정보가 없으면 로그인 페이지로 무조건 redirect하라는 뜻이다.
  // 따라서 개인에 맞는 수정이 필요하다.
  const {
    data: { user },
  } = await supabase.auth.getUser()

  if (
    !user &&
    !request.nextUrl.pathname.startsWith('/login') &&
    !request.nextUrl.pathname.startsWith('/auth')
  ) {
    // no user, potentially respond by redirecting the user to the login page
    const url = request.nextUrl.clone()
    url.pathname = '/login'
    return NextResponse.redirect(url)
  }

  // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
  // creating a new response object with NextResponse.next() make sure to:
  // 1. Pass the request in it, like so:
  //    const myNewResponse = NextResponse.next({ request })
  // 2. Copy over the cookies, like so:
  //    myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
  // 3. Change the myNewResponse object to fit your needs, but avoid changing
  //    the cookies!
  // 4. Finally:
  //    return myNewResponse
  // If this is not done, you may be causing the browser and server to go out
  // of sync and terminate the user's session prematurely!

  return supabaseResponse
}

 

로그인이 정상적으로 작동하면 아래와 같이 서버에서 log 정보를 볼 수 있다.

 

 

actions.ts

- formAction에 연결한 function을 기입된 페이지이다.

import { login, signup } from './actions'

export default function LoginPage() {
  return (
    <form>
      <label htmlFor="email">Email:</label>
      <input id="email" name="email" type="email" required />
      <label htmlFor="password">Password:</label>
      <input id="password" name="password" type="password" required />
      
      //button forAction에 actions.ts를 연결한 
      <button formAction={login}>Log in</button>
      <button formAction={signup}>Sign up</button>
    </form>
  )
}

 

 

로그인의 완료된 이후

- 로그인이 완료되면 된 것인지 알 수가 없다. 따라 확인이 필요하다.

나의 경우 Header에 로그인 관련 정보가 있다.

// 주의할 것은 'createClient'가 server에서 불러와야 한다는 것
import { createClient } from '@/utils/supabase/server';

const supabase = createClient();
const {data : {user} } = await supabase.auth.getUser();

 

User type

interface UserType {
  id: string;
  aud: string;
   role: string;
   email: string;
   email_confirmed_at: string;
   phone: string;
   confirmed_at: string;
   last_sign_in_at: string; app_metadata: {
    provider: string;
    providers: string[];
   };
   user_metadata:Record<string, unknown>;
   identities: Identity[];
   created_at: string;
   updated_at: string;
   is_anonymous: boolean
;}

interface Identity {
   identity_id: string;
   id: string;
   user_id: string;
   identity_data: Record<string, unknown>;
   provider: string;
   last_sign_in_at: string;
   created_at: string;
   updated_at: string;
  email: string;
}

 

singout

app/login/actions.ts

export async function singOut () {
  const supabase = createClient(); => server에서 불러와야 함.
  await supabase.auth.signOut()
  redirect('/login')
;}

 

 

완전한 이해없이 로그인 auth를 만들었다.

이게 맞나라는 생각이 들기도 한다.

 

참조

https://www.youtube.com/watch?v=A6-56miVA_0