Supabase Complete Guide | Auth, Database, Storage, Realtime
이 글의 핵심
Supabase gives you a full backend — Postgres database, authentication, file storage, and realtime subscriptions — without managing infrastructure. This guide covers all four pillars with practical examples.
What This Guide Covers
Supabase provides authentication, a managed PostgreSQL database, file storage, and realtime subscriptions — all accessible via a simple JavaScript SDK. This guide covers each feature with practical examples.
Why Supabase?
Supabase has emerged as the leading open-source Firebase alternative, with over 50,000 projects on their hosted platform and thousands of self-hosted deployments.
Real-world adoption:
- GitHub uses Supabase for internal tools
- Mobbin (design inspiration platform, 1M+ users) built entirely on Supabase
- Vercel templates include Supabase as the default backend option
- Thousands of startups ship MVPs in days instead of weeks
Why teams choose Supabase over building custom backends:
- Launch in hours, not weeks — authentication, database, and file storage work out of the box
- PostgreSQL, not NoSQL — use SQL, joins, transactions, and your existing database knowledge
- Row Level Security — built-in authorization at the database level (not application layer)
- Realtime by default — subscribe to database changes without WebSocket boilerplate
- Open source — self-host if needed, no vendor lock-in
Supabase vs Firebase:
| Feature | Supabase | Firebase |
|---|---|---|
| Database | PostgreSQL (SQL) | Firestore (NoSQL) |
| Auth | Open source (GoTrue) | Proprietary |
| Self-hosting | Yes | No |
| Querying | SQL, joins, full-text search | Document queries, limited joins |
| Pricing | Free tier: 500MB, 2GB bandwidth | Free tier: 1GB storage, 10GB bandwidth |
When to use Supabase:
- You prefer SQL and relational data modeling
- Need complex queries, joins, or full-text search
- Want to self-host or avoid vendor lock-in
- Building web apps (Next.js, Remix, SvelteKit)
When to use Firebase:
- Need the most mature mobile SDK (Flutter, iOS, Android)
- Require Google ecosystem integration (Firebase ML, Analytics)
- Prefer NoSQL document structure
- Need advanced real-time features (Firestore’s nested listeners)
Setup
npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
Get your URL and anon key from the Supabase dashboard under Project Settings → API.
1. Authentication
Email & Password
// Sign up
const { data, error } = await supabase.auth.signUp({
email: '[email protected]',
password: 'secure-password',
});
// Sign in
const { data, error } = await supabase.auth.signInWithPassword({
email: '[email protected]',
password: 'secure-password',
});
// Sign out
await supabase.auth.signOut();
// Get current user
const { data: { user } } = await supabase.auth.getUser();
OAuth (Google, GitHub, etc.)
await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: 'http://localhost:3000/auth/callback',
},
});
Auth State Listener
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
if (event === 'SIGNED_IN') console.log('Signed in:', session.user.email);
if (event === 'SIGNED_OUT') console.log('Signed out');
}
);
// Cleanup
subscription.unsubscribe();
2. Database (PostgreSQL)
Supabase exposes your Postgres database via a REST API. The JavaScript client provides a fluent query builder.
Basic CRUD
// SELECT
const { data, error } = await supabase
.from('posts')
.select('id, title, created_at')
.order('created_at', { ascending: false })
.limit(10);
// INSERT
const { data, error } = await supabase
.from('posts')
.insert({ title: 'Hello World', body: 'My first post', user_id: user.id })
.select()
.single();
// UPDATE
const { error } = await supabase
.from('posts')
.update({ title: 'Updated Title' })
.eq('id', postId);
// DELETE
const { error } = await supabase
.from('posts')
.delete()
.eq('id', postId);
Filtering
// Equality
.eq('status', 'published')
// Range
.gte('views', 100).lte('views', 1000)
// Pattern matching
.ilike('title', '%supabase%')
// In array
.in('category', ['tech', 'ai', 'web'])
// Is null
.is('deleted_at', null)
Joins
const { data } = await supabase
.from('posts')
.select(`
id,
title,
author:profiles(id, username, avatar_url),
comments(id, body)
`);
3. Row Level Security (RLS)
RLS is Supabase’s most important security feature. Enable it on every table, then write policies:
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Anyone can read published posts
CREATE POLICY "Public posts are visible to all"
ON posts FOR SELECT
USING (status = 'published');
-- Users can only edit their own posts
CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
USING (auth.uid() = user_id);
-- Users can only insert as themselves
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
With RLS enabled, your anon key is safe to use in client-side code — database policies enforce access control.
4. Realtime Subscriptions
Subscribe to database changes in real time:
// Subscribe to all changes on a table
const channel = supabase
.channel('posts-changes')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => {
if (payload.eventType === 'INSERT') {
setPosts(prev => [payload.new, ...prev]);
}
if (payload.eventType === 'UPDATE') {
setPosts(prev => prev.map(p => p.id === payload.new.id ? payload.new : p));
}
if (payload.eventType === 'DELETE') {
setPosts(prev => prev.filter(p => p.id !== payload.old.id));
}
}
)
.subscribe();
// Cleanup
supabase.removeChannel(channel);
Broadcast (custom events)
Send events between clients without touching the database:
const channel = supabase.channel('room-1');
// Listen
channel.on('broadcast', { event: 'cursor' }, ({ payload }) => {
updateCursor(payload.userId, payload.x, payload.y);
}).subscribe();
// Send
channel.send({
type: 'broadcast',
event: 'cursor',
payload: { userId: user.id, x: 100, y: 200 },
});
5. Storage
// Upload a file
const { data, error } = await supabase.storage
.from('avatars')
.upload(`public/${user.id}.jpg`, file, {
cacheControl: '3600',
upsert: true,
});
// Get public URL
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`public/${user.id}.jpg`);
// Download
const { data, error } = await supabase.storage
.from('avatars')
.download(`public/${user.id}.jpg`);
// Delete
const { error } = await supabase.storage
.from('avatars')
.remove([`public/${user.id}.jpg`]);
6. Edge Functions
Supabase Edge Functions run TypeScript/JavaScript at the edge (Deno runtime):
// supabase/functions/send-email/index.ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";
serve(async (req) => {
const { to, subject, body } = await req.json();
// Call external email API...
return new Response(JSON.stringify({ sent: true }), {
headers: { "Content-Type": "application/json" },
});
});
supabase functions deploy send-email
Call from client:
const { data, error } = await supabase.functions.invoke('send-email', {
body: { to: '[email protected]', subject: 'Hello', body: 'World' }
});
Next.js App Router Integration
npm install @supabase/ssr
// lib/supabase/server.ts
import { createServerClient } 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) { return cookieStore.get(name)?.value; },
set(name, value, options) { cookieStore.set({ name, value, ...options }); },
remove(name, options) { cookieStore.set({ name, value: '', ...options }); },
},
}
);
}
// app/dashboard/page.tsx
export default async function Dashboard() {
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect('/login');
const { data: posts } = await supabase.from('posts').select('*');
return <PostList posts={posts} />;
}
Key Takeaways
| Feature | Use for |
|---|---|
| Auth | Email/password, OAuth, magic links |
| Database | Postgres with REST API + type-safe client |
| RLS | Row-level access control per user |
| Realtime | Live database changes + broadcast events |
| Storage | File uploads with access control |
| Edge Functions | Server-side logic, webhooks, integrations |
Enable RLS on every table from day one — it’s much harder to add later. Start with the anon key for public data and the service_role key only in secure server-side contexts.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. Build full-stack apps with Supabase — the open-source Firebase alternative. Covers Auth, PostgreSQL, Row Level Security,… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.
Q. 더 깊이 공부하려면?
A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- [Firebase Complete Guide | Firestore· Auth](/en/blog/firebase-complete-guide/
- [Next.js App Router: SSR, SSG, and ISR](/en/blog/nextjs-app-router-ssr-ssg-isr/
이 글에서 다루는 키워드 (관련 검색어)
Supabase, PostgreSQL, Auth, Realtime, Backend, Firebase 등으로 검색하시면 이 글이 도움이 됩니다.