supabase - ssr 수업내용 정리
원래 평소에 하던 프로젝트에서는
슈퍼베이스를 쓸 때 슈퍼베이스는 브라우저에서 돌아가고 있었다.
그래서 브라우저에서 데이터페칭을 편하게 할 수 있었던 이유는
슈파베이스가 authentication 같은 것도 포함하고 있었기 때문이다.
근데 이제 서버사이드에서는 요청을 주는 이 사람이
로그인을 한 사람인지 안한 사람인지 그걸 직접적으로 알기가 어렵다.
그래서 그거를 Next.js의 서버에도
브라우저에서 지금 로그인한 상태인지를 전달할 수 있는가.
그거를 어떻게 하면 Next.js의 서버에도 브라우저에서 지금 로그인한 상태인지를 전달할 수 있는가.
이제 같이 알아보도록 하자.
기존 브라우저에서 initiallization 하는 방식.
근데 이 방법이 아니라 조금 다르게 설치를 해야한다.
아래를 참고하자.
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()를 사용하게 되면 미들웨어에서 처리된 쿠키 덕분에
현재 로그인한 유저를 불러올 수 있다.