카테고리 없음

supabase - ssr 수업내용 정리

riversun1 2024. 7. 15. 02:07

 

원래 평소에 하던 프로젝트에서는

슈퍼베이스를 쓸 때 슈퍼베이스는 브라우저에서 돌아가고 있었다.

그래서 브라우저에서 데이터페칭을 편하게 할 수 있었던 이유는
슈파베이스가 authentication 같은 것도 포함하고 있었기 때문이다.


근데 이제 서버사이드에서는 요청을 주는 이 사람이

로그인을 한 사람인지 안한 사람인지 그걸 직접적으로 알기가 어렵다.

그래서 그거를 Next.js의 서버에도
브라우저에서 지금 로그인한 상태인지를 전달할 수 있는가.
그거를 어떻게 하면 Next.js의 서버에도 브라우저에서 지금 로그인한 상태인지를 전달할 수 있는가.
이제 같이 알아보도록 하자.

 

 

기존 브라우저에서 initiallization 하는 방식.

 

근데 이 방법이 아니라 조금 다르게 설치를 해야한다.

아래를 참고하자.

 

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 Components. It isn'

supabase.com

 

 

 

PUBLIC이라고 적힌건 브라우저에도 노출이되는 환경변수라는 것.

서버사이드에서만 저 환경변수를 쓸거면 앞에 NEXT_PUBLIC이라는 프리픽스를 지워주면 된다.

 

자 이제 supabase를 쓸려면 client가 필요하다.

(그래야 supabase.auth 등등 사용할 수 있으니까.)

우리는 브라우저에서 쓸 client와 서버에서 쓸 client 2개가 필요하다.

(여기서 서버는 우리가 지금 로컬에서 돌리고있지만 사실은 저기 깊은 바닷속에 있는게 서버임.)

 

 

그래서 아무튼 물리적으로 분리된 client 코드를 2가지를 만들어줘야한다!

 

 

utils/supabase/client.ts  (클라이언트 코드)

import { createBrowserClient } from '@supabase/ssr'

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

 

 

utils/supabase/server.ts   (서버 코드)

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

export function createClient() {
  const cookieStore = cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // The `setAll` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  )
}

 

 

그리고 미들웨터 세팅도 해줘야한다.

root에다가 만들라고하는데 이건 src 밖이 아니라 src 안에다가 해야한다.

 

 

middleware.ts

import { type NextRequest } from 'next/server'
import { updateSession } from '@/utils/supabase/middleware'

export async function middleware(request: NextRequest) {
  return await updateSession(request)
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     * Feel free to modify this pattern to include more paths.
     */
    '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
  ],
}

 

 

그리고 supabase 폴더 안에도 middleware.ts를 만들어준다.

 

utils/supabase/middleware.ts

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.

  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
}

 

 

미들웨어가 실제로 2개 있는게아니라

Next.js는 root에 있는 미들웨어를 돌리게 되는데

supabase/middleware를 가지고와서 사용해주고 있다.

updateSession을 모듈화한것.

 

supabase/middleware를 살펴보면

쿠키와 관련한 메소드들을 추가해주고 있다.

왜 쿠키와 관련된게 있을까?

서버에 요청할 때 자동으로 항상 가는게 쿠키이다.

거기안에다가 서버사이드와 통신할 때에

supabase 인증정보를 넣고 빼고 세팅하고 지우고 이럴거기 때문에

쿠키와 관련된 세팅이 들어가는 것.

 

그리고 여기까지 했으면 404페이지가 뜬다. (정상임)

url을 보면 http://localhost:3000/login 이라고 뜨는데 우린 login 페이지를 안만들었기 때문.

 

안만들었는데 어떻게 login 페이지가...? => supabase/middleware.ts를 보면 그 답을 알 수 있다.

 

그리고 회원가입 로직을 구현할때 server용 client를 import해서 사용해주면 된다.

 

그리고 supabase.auth.getUser()를 사용하게 되면 미들웨어에서 처리된 쿠키 덕분에

현재 로그인한 유저를 불러올 수 있다.