Supabase Auth와 Next.js Middleware를 활용한 초고속 사용자 인증 구현하기
최근 들어 풀스택 사이드 프로젝트나 MVP(최소 기능 제품)를 빠르게 제작하려는 개발자들 사이에서 **'Supabase(수파베이스)'**는 가히 필수적인 백엔드 서비스(BaaS)로 자리를 잡았습니다. 특히 Next.js 14의 서버 컴포넌트(Server Components) 패러다임과 궁합이 무척이나 잘 맞기 때문입니다.
웹 서비스 개발에서 가장 골치 아픈 영역 중 하나인 '사용자 회원가입 및 로그인(Authentication)'을 Supabase Auth와 Next.js Middleware를 조합해 단 몇 분 만에 완벽히 안전하고 빠른 서버사이드 검증 구조로 설계하는 방법을 정리해 드립니다.
1. 1단계: Supabase SSR 설치 및 클라이언트 헬퍼 구성
과거에는 클라이언트 사이드 브라우저 위주로 로그인 세션을 관리했지만, Next.js App Router 환경에서는 쿠키(Cookie)를 매개로 서버 컴포넌트와 Middleware에서 인증 정보를 실시간 공유해야 합니다. 이를 위해 Supabase는 최신 @supabase/ssr 패키지를 제공합니다.
- 필요 패키지 설치:
npm install @supabase/ssr @supabase/supabase-js - 서버용 Supabase 클라이언트 팩토리 (utils/supabase/server.ts):
쿠키를 사용하여 서버 상에서 Supabase 클라이언트를 인스턴스화합니다.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: { get(name: string) { return cookieStore.get(name)?.value; }, set(name: string, value: string, options: CookieOptions) { try { cookieStore.set({ name, value, ...options }); } catch (error) { // 서버 컴포넌트에서 쿠키를 세팅하려 할 때 발생할 수 있는 에러 무시 } }, remove(name: string, options: CookieOptions) { try { cookieStore.set({ name, value: '', ...options }); } catch (error) { // 에러 무시 } }, }, } ); }
2. 2단계: Middleware를 이용한 라우트 보호 (Protected Routes)
로그인하지 않은 사용자가 대시보드나 회원 전용 페이지(/dashboard/*, /profile)에 직접 URL을 치고 접근하려 할 때, 클라이언트가 아닌 서버 측 Middleware 단계에서 세션을 판별해 미리 로그인 페이지로 튕겨내야 보안과 UX를 동시에 챙길 수 있습니다.
- middleware.ts 작성 (프로젝트 루트):
import { createServerClient, type CookieOptions } from '@supabase/ssr'; import { NextResponse, type NextRequest } from 'next/server'; export async function middleware(request: NextRequest) { let response = NextResponse.next({ request: { headers: request.headers, }, }); const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { get(name: string) { return request.cookies.get(name)?.value; }, set(name: string, value: string, options: CookieOptions) { request.cookies.set({ name, value, ...options }); response = NextResponse.next({ request: { headers: request.headers, }, }); response.cookies.set({ name, value, ...options }); }, remove(name: string, options: CookieOptions) { request.cookies.set({ name, value: '', ...options }); response = NextResponse.next({ request: { headers: request.headers, }, }); response.cookies.set({ name, value: '', ...options }); }, }, } ); // 사용자 세션 조회 const { data: { user } } = await supabase.auth.getUser(); // 보호된 경로 진입 필터링 if (request.nextUrl.pathname.startsWith('/dashboard') && !user) { return NextResponse.redirect(new URL('/login', request.url)); } return response; } export const config = { matcher: ['/dashboard/:path*', '/profile/:path*'], };
3. 3단계: Server Actions를 이용한 초간단 로그인/로그아웃
Next.js 14의 Server Actions를 활용하면 API 라우트를 별도로 구현할 필요 없이, 컴포넌트 폼 내부에서 곧장 보안 로그인을 수행할 수 있습니다.
- app/login/actions.ts 작성:
'use server'; import { revalidatePath } from 'next/cache'; import { redirect } from 'next/navigation'; import { createClient } from '@/utils/supabase/server'; export async function login(formData: FormData) { const supabase = createClient(); const email = formData.get('email') as string; const password = formData.get('password') as string; const { error } = await supabase.auth.signInWithPassword({ email, password }); if (error) { return redirect('/login?error=Could not authenticate user'); } revalidatePath('/', 'layout'); return redirect('/dashboard'); } - 로그인 폼 컴포넌트 활용:
import { login } from './actions'; export default function LoginPage() { return ( <form action={login} className="flex flex-col gap-4 max-w-sm mx-auto my-20"> <label htmlFor="email">Email</label> <input id="email" name="email" type="email" required className="border p-2" /> <label htmlFor="password">Password</label> <input id="password" name="password" type="password" required className="border p-2" /> <button className="bg-blue-600 text-white p-2 rounded">Log In</button> </form> ); }
✍️ 글을 마치며
과거에는 인증 상태 관리를 위해 Redux나 Context API로 프론트엔드 전역 상태를 휘감고 쿠키 만료를 체크하는 기나긴 삽질을 해야 했습니다.
그러나 Next.js 14와 Supabase SSR의 조합은 서버 단계에서 유저 세션을 완벽히 판별하여 쿠키 수명 주기와 Middleware 보안 제어까지 몇 십 줄의 코드로 깔끔하게 단축해 줍니다. 이번 사이드 프로젝트에는 이 검증된 풀스택 인증 아키텍처를 도입해 보세요!