본문으로 건너뛰기
Previous
Next
Supabase Complete Guide | Auth, Database, Storage, Realtime

Supabase Complete Guide | Auth, Database, Storage, Realtime

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:

FeatureSupabaseFirebase
DatabasePostgreSQL (SQL)Firestore (NoSQL)
AuthOpen source (GoTrue)Proprietary
Self-hostingYesNo
QueryingSQL, joins, full-text searchDocument queries, limited joins
PricingFree tier: 500MB, 2GB bandwidthFree 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

FeatureUse for
AuthEmail/password, OAuth, magic links
DatabasePostgres with REST API + type-safe client
RLSRow-level access control per user
RealtimeLive database changes + broadcast events
StorageFile uploads with access control
Edge FunctionsServer-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 등으로 검색하시면 이 글이 도움이 됩니다.