Next.js에 Supabase 적용하기
Next.js(App Router) 프로젝트에 Supabase를 연동하는 방법을 안내합니다. 인증, 데이터베이스 CRUD, 실시간 구독까지 다룹니다.
1. Supabase 프로젝트 생성
- supabase.com에 접속하여 Start your project 클릭
- GitHub 계정으로 로그인
- New Project 클릭
- Organization 선택, 프로젝트 이름, DB 비밀번호, Region 설정
- Create new project 클릭 (생성까지 약 2분)
API 키 확인
프로젝트 생성 후 Settings → API에서 확인합니다.
- Project URL —
https://xxxxx.supabase.co - anon public key — 클라이언트용 공개 키
- service_role key — 서버 전용 비공개 키
2. 패키지 설치
npm install @supabase/supabase-js @supabase/ssr
@supabase/supabase-js— Supabase 클라이언트 SDK@supabase/ssr— Next.js 서버/클라이언트 환경에 맞는 유틸리티
3. 환경 변수 설정
.env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
주의
service_role 키는 서버에서만 사용해야 합니다. NEXT_PUBLIC_ 접두사를 붙이지 마세요.
4. Supabase 클라이언트 설정
브라우저용 클라이언트
lib/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!,
);
}
서버용 클라이언트
lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options),
);
},
},
},
);
}
미들웨어 설정
세션을 자동으로 갱신하기 위해 미들웨어를 추가합니다.
middleware.ts
import { createServerClient } from '@supabase/ssr';
import { NextResponse, type NextRequest } from 'next/server';
export async function middleware(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 }) =>
request.cookies.set(name, value),
);
supabaseResponse = NextResponse.next({ request });
cookiesToSet.forEach(({ name, value, options }) =>
supabaseResponse.cookies.set(name, value, options),
);
},
},
},
);
await supabase.auth.getUser();
return supabaseResponse;
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
5. 인증 구현
회원가입
app/signup/page.tsx
'use client';
import { createClient } from '@/lib/supabase/client';
import { useState } from 'react';
export default function SignUpPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const supabase = createClient();
const handleSignUp = async () => {
const { error } = await supabase.auth.signUp({
email,
password,
});
if (error) {
alert(error.message);
} else {
alert('인증 이메일을 확인해 주세요.');
}
};
return (
<form onSubmit={(e) => { e.preventDefault(); handleSignUp(); }}>
<input type="email" placeholder="이메일" value={email}
onChange={(e) => setEmail(e.target.value)} />
<input type="password" placeholder="비밀번호" value={password}
onChange={(e) => setPassword(e.target.value)} />
<button type="submit">회원가입</button>
</form>
);
}
로그인 / 로그아웃
// 로그인
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
// 로그아웃
await supabase.auth.signOut();
소셜 로그인 (Google 예시)
Supabase Dashboard → Authentication → Providers에서 Google을 활성화한 후:
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`,
},
});
OAuth 콜백 처리
app/auth/callback/route.ts
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get('code');
if (code) {
const supabase = await createClient();
await supabase.auth.exchangeCodeForSession(code);
}
return NextResponse.redirect(origin);
}
서버 컴포넌트에서 사용자 확인
app/dashboard/page.tsx
import { createClient } from '@/lib/supabase/server';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
redirect('/login');
}
return <div>{user.email}님의 대시보드</div>;
}
6. 데이터베이스 CRUD
Supabase Dashboard의 Table Editor에서 테이블을 생성하거나, SQL Editor에서 직접 생성합니다.
SQL Editor
CREATE TABLE posts (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
user_id UUID REFERENCES auth.users(id),
created_at TIMESTAMPTZ DEFAULT now()
);
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "누구나 조회 가능"
ON posts FOR SELECT
TO authenticated, anon
USING (true);
CREATE POLICY "본인만 작성 가능"
ON posts FOR INSERT
TO authenticated
WITH CHECK (auth.uid() = user_id);
조회
const { data: posts, error } = await supabase
.from('posts')
.select('*')
.order('created_at', { ascending: false });
생성
const { error } = await supabase
.from('posts')
.insert({ title: '제목', content: '내용', user_id: user.id });
수정
const { error } = await supabase
.from('posts')
.update({ title: '수정된 제목' })
.eq('id', postId);
삭제
const { error } = await supabase
.from('posts')
.delete()
.eq('id', postId);
7. 실시간 구독
데이터 변경을 실시간으로 감지합니다.
'use client';
import { createClient } from '@/lib/supabase/client';
import { useEffect, useState } from 'react';
export default function RealtimePosts() {
const [posts, setPosts] = useState([]);
const supabase = createClient();
useEffect(() => {
// 초기 데이터 로드
supabase.from('posts').select('*').then(({ data }) => {
setPosts(data || []);
});
// 실시간 구독
const channel = supabase
.channel('posts-changes')
.on('postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => {
if (payload.eventType === 'INSERT') {
setPosts((prev) => [payload.new, ...prev]);
}
},
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, []);
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
실시간 활성화
Supabase Dashboard → Database → Replication에서 해당 테이블의 Realtime을 활성화해야 합니다.
전체 흐름 요약
1. Supabase 프로젝트 생성 & API 키 확인
2. npm install @supabase/supabase-js @supabase/ssr
3. .env.local에 URL과 anon key 설정
4. lib/supabase/에 client.ts, server.ts 생성
5. middleware.ts로 세션 자동 갱신
6. supabase.auth로 인증, supabase.from()으로 DB 조작