본문으로 건너뛰기
Previous
Next
Supabase 완전 가이드 | Firebase를 뛰어넘는 오픈소스 백엔드

Supabase 완전 가이드 | Firebase를 뛰어넘는 오픈소스 백엔드

Supabase 완전 가이드 | Firebase를 뛰어넘는 오픈소스 백엔드

이 글의 핵심

Firebase의 강력한 대안 Supabase. PostgreSQL 기반으로 더 강력한 쿼리, Row Level Security, 실시간 구독, Edge Functions까지 제공하며, 완전한 오픈소스로 벤더 락인 없이 사용할 수 있습니다.

Firebase에서 Supabase로 옮기면 처음엔 좀 헷갈려. 인증·실시간·스토리지 비슷해 보이는데, 데이터 모델이 완전히 달라지거든. Firestore는 문서·컬렉션, Supabase는 그냥 Postgres라서 “쿼리로 때려 박는” 쪽으로 머리를 바꿔야 해. 난 이게 오히려 편하다고 봄. 조인·트랜잭션·제약이 한 번에 들어가니까, 나중에 스키마 갈아엎을 일이 줄어든다.

솔직히 말하면, Supabase 쓸 때 진짜 핵심은 RLS(Row Level Security) 설정이야. SDK로 from('posts') 쿼리 날릴 때, “이 유저는 이 row만 본다”를 앱 서버에 다 몰아넣지 말고 DB에 박아두는 게 정신 건강에 좋음. RLS를 제대로 안 깔고 anon 키로 클라를 찍으면, 그날로 보안 사고 뉴스 각이니까, 마이그레이션 끝나고 제일 먼저 RLS 켜고 policy부터 짜는 쪽을 추천한다. 나는 “Supabase = Postgres + RLS”라고 외우는 편.

Supabase는 2020년쯤 나온 Firebase 대안 계열이고, 인증·DB·스토리지·리얼타임·엣지 함수가 한 대시보드에 모여 있음. 오픈소스고 Docker로도 돌릴 수 있어서, 구글 락인이 싫으면 이쪽 루트가 많이 나감. 프로젝트는 supabase.com에서 만들고, 지역은 가까운 쪽(한국 쓰면 Seoul) 골라두면 돼. 로컬은 npm i -g supabasesupabase initsupabase start (Docker 필요).

Next.js면 @supabase/supabase-js@supabase/ssr 같이 쓰는 게 보통이고, .env에는 NEXT_PUBLIC_SUPABASE_URL이랑 NEXT_PUBLIC_SUPABASE_ANON_KEY만 박아도 시작은 된다. 서버 컴포넌트는 쿠키 꽂아서 createServerClient, 클라는 createBrowserClient — 한 프로젝트에 둘 다 두는 패턴이 익숙해지면 편해.

클라이언트 예시(서버용)

// lib/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: {
        get(name: string) {
          return cookieStore.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          cookieStore.set({ name, value, ...options });
        },
        remove(name: string, options: CookieOptions) {
          cookieStore.set({ name, value: '', ...options });
        },
      },
    }
  );
}

브라우저

// 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!
  );
}

인증은 signUp / signInWithPassword / signInWithOAuth / signOut / getUser 정도로 끝나고, onAuthStateChange 쓰면 앱 쪽에서 세션 흐름 잡기도 쉬움. Firebase에서 오면 “Auth UID가 있고, 이제 그게 Postgres의 user_id랑 맞다”고만 생각하면 이해가 빨라.

DB는 PostgREST로 REST + 클라 쿼리 빌더로 잡힌다. insert·select·update·delete는 익숙한 체이닝이고, RLS가 켜져 있으면 policy가 먼저 걸러준다. 그래서 eq('user_id', user.id)만 앱에 억지로 쏟아붓지 말고, RLS에 “진짜 규칙”을 써 두는 쪽이 낫다 — 서버 누락 나도 DB가 막아줌.

RLS 한 덩어리 예시(이게 익숙해지면 끝에 가깝다)

ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Anyone can view posts"
ON posts FOR SELECT
USING (true);

CREATE POLICY "Users can insert own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);

CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
USING (auth.uid() = user_id);

CREATE POLICY "Users can delete own posts"
ON posts FOR DELETE
USING (auth.uid() = user_id);

위에서 말한 “핵심”이 이거. SELECT는 열어 두고, 쓰기는 auth.uid()에 묶는 식. 프로덕션에선 true로 다 열어둔 select도 조심하고, “공개/비공개”는 컬럼이랑 policy 둘 다로 나누는 게 흔함.

실시간은 채널 잡고 postgres_changes로 테이블 이벤트 받으면 돼. Firestore snapshot 리스너 느낌인데, 밑에는 Postgres CDC라서 모델만 SQL에 맞추면 됨.

const channel = supabase
  .channel('posts')
  .on('postgres_changes',
    { event: '*', schema: 'public', table: 'posts' },
    (payload) => console.log(payload)
  )
  .subscribe();

스토리지는 버킷 하나 파고, 업로드 후 getPublicUrl 쓰면 끝. RLS 말고 스토리지 쪽 policy도 따로 잡는 거 잊지 말 것 — “파일 URL만 숨기고 누구나 읽기”는 여전히 구멍일 수 있으니까.

엣지 함수는 Deno, supabase functions new → 로컬 servedeploy 흐름. 결제·웹훅·이메일 같이 “키 절대 클라이언트에 안 내려” 같은 거 올릴 때 쓰면 좋다.

Supabase vs Firebase를 표로 뽑아 비교하던 시절은 지나갔다고 치고, 한 줄로 말하면 SQL·오픈소스·셀프호스팅이 당기면 Supabase, 모바일 SDK랑 Google 생태가 우선이면 Firebase 쪽이 더 무난한 경우가 많다. Firestore 쿼리로 애쓰던 “관계”를 RLS+FK로 풀 수 있을 때 Supabase로 옮기는 게 제일 이득이야.

끝으로: 무료 티어로 샌드박스 만들고, migrations 폴더에 스키마를 코드로 쌓아두는 습관만 들이면, Firebase 시절보다 “나중에 운영”이 훨씬 낫다. 문서는 supabase.com/docs, 막히면 Discord 가도 답 잘 옴. 그리고 다시 강조하지만, RLS 먼저다.

시작은 supabase.com에서 프로젝트 하나 파고, RLS 켜고 policy부터. 그다음이 SDK 이야기.