백엔드를 처음부터 구성하려면 보통 DB, 인증, 파일 스토리지, 실시간 알림을 각각 다른 서비스로 붙이게 됩니다. DB는 RDS, 인증은 Auth0나 직접 구현한 JWT 발급 서버, 파일은 S3, 실시간은 Socket.io 서버를 따로 띄우는 식입니다. 서비스 하나하나는 각자 잘 만들어져 있어도, 이들을 엮는 인프라 코드와 권한 체계를 맞추는 작업이 꽤 커집니다. Supabase는 오픈소스 Postgres 위에 이 네 가지를 전부 올려서, 프로젝트 하나 만들면 DB와 인증·스토리지·실시간 구독이 같은 권한 체계 안에서 바로 연결됩니다.

Supabase 대시보드 공식 스크린샷 — 테이블 에디터와 SQL 에디터, 프로젝트 개요 화면
이미지: supabase/supabase 공식 GitHub 저장소 README에 포함된 대시보드 스크린샷

핵심 개념

  • 실체는 관리형 Postgres: 프로젝트를 만들면 실제 Postgres 인스턴스가 하나 뜹니다. 테이블을 SQL로 직접 만들어도 되고, 대시보드의 테이블 에디터로 만들어도 결과는 같은 DB에 남습니다. 나중에 쿼리가 복잡해지면 psql로 바로 붙어서 디버깅할 수 있다는 뜻이기도 합니다.
  • RLS(Row Level Security)가 권한의 전부: 클라이언트 SDK는 익명 키(anon key)로 DB에 직접 질의합니다. 서버 쪽 권한 체크 레이어가 따로 없고, 대신 Postgres 자체의 RLS 정책이 "이 행을 이 사용자가 읽거나 쓸 수 있는가"를 결정합니다. RLS를 켜지 않은 테이블은 익명 키만으로 전체 데이터를 읽고 쓸 수 있으므로, 새 테이블을 만들면 RLS부터 켜는 습관이 필요합니다.
  • Auth는 Postgres 사용자 테이블과 연결: 이메일/비밀번호, OAuth, 매직링크로 로그인하면 auth.users 테이블에 사용자가 생기고, 이 uid()를 RLS 정책 안에서 그대로 참조할 수 있습니다. "내가 쓴 글만 수정 가능"같은 규칙을 애플리케이션 코드가 아니라 SQL 정책으로 표현합니다.
  • Realtime은 Postgres WAL을 구독: 테이블에 INSERT/UPDATE/DELETE가 일어나면 Write-Ahead Log 변경분을 읽어서 구독 중인 클라이언트에 그대로 흘려보냅니다. 별도의 pub/sub 서버를 붙일 필요 없이 DB 변경 자체가 실시간 이벤트가 됩니다.
  • 오픈소스라 셀프호스팅 가능: 코어 전체가 Docker Compose로 묶여 있어 자체 서버에 올릴 수 있습니다. 다만 대시보드의 일부 관리 기능(조직 단위 빌링, 일부 로그 뷰어)은 호스팅 버전에만 있어서, 완전히 동일한 경험은 아닙니다.

실전 예시

클라이언트는 @supabase/supabase-js 하나면 됩니다.

npm install @supabase/supabase-js
// lib/supabase.ts
import { createClient } from "@supabase/supabase-js";

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

테이블은 SQL 에디터에서 만들고, 바로 RLS를 켭니다.

create table posts (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users not null,
  title text not null,
  created_at timestamptz default now()
);

alter table posts enable row level security;

create policy "본인 글만 작성 가능"
  on posts for insert
  with check (auth.uid() = user_id);

create policy "본인 글만 수정 가능"
  on posts for update
  using (auth.uid() = user_id);

create policy "누구나 읽기 가능"
  on posts for select
  using (true);

이메일/비밀번호 로그인과 글 작성은 클라이언트 코드에서 바로 호출합니다.

// 로그인
const { data, error } = await supabase.auth.signInWithPassword({
  email: "test@example.com",
  password: "password123",
});

// 로그인한 사용자로 글 작성 — user_id는 RLS가 자동으로 검증
const { error: insertError } = await supabase.from("posts").insert({
  user_id: data.user?.id,
  title: "첫 글",
});

실시간 구독도 몇 줄이면 됩니다.

supabase
  .channel("posts-changes")
  .on(
    "postgres_changes",
    { event: "INSERT", schema: "public", table: "posts" },
    (payload) => {
      console.log("새 글 도착:", payload.new);
    }
  )
  .subscribe();

RLS 정책을 빼먹고 테스트했을 때 실제로 겪은 상황인데, posts for update using (auth.uid() = user_id) 정책 없이 update를 호출하면 에러 없이 조용히 0개 행이 바뀌고 끝납니다. UPDATE 쿼리 자체는 성공 응답을 받기 때문에, 처음에는 "왜 수정이 안 되지"가 아니라 "분명 성공했는데 왜 화면이 그대로지"로 헤매게 됩니다. select 정책이 없는 상태에서 insert 후 반환값을 못 받는 것도 같은 패턴이라, 변경이 안 먹힐 때는 가장 먼저 해당 테이블의 RLS 정책 목록을 확인하는 게 빠릅니다.

비슷한 서비스와 비교

항목 Supabase Firebase PlanetScale
DB Postgres (관계형, SQL) Firestore/Realtime DB (NoSQL) MySQL 호환 (관계형, SQL)
권한 모델 Postgres RLS 정책 Firestore 보안 규칙(자체 DSL) 애플리케이션 레이어에서 직접 구현
인증 내장 (이메일, OAuth, 매직링크) 내장 (Firebase Auth) 없음 — 별도 서비스 연동 필요
실시간 구독 내장 (Postgres WAL 기반) 내장 (Firestore 기본 기능) 없음
파일 스토리지 내장 내장 (Blaze 플랜부터, 2026-02 변경) 없음
오픈소스 / 셀프호스팅 가능 (Docker Compose) 불가능 불가능

Firestore는 NoSQL이라 데이터를 문서 단위로 비정규화해서 저장하는 모델링이 필요하고, 복잡한 조인이나 집계 쿼리는 애초에 설계에서 피하는 방향으로 갑니다. 반면 Supabase는 그냥 Postgres라서 기존에 SQL을 써본 적이 있다면 모델링 방식을 새로 배울 필요가 없습니다. PlanetScale은 DB 자체의 성능과 브랜칭(스키마 변경을 git처럼 브랜치로 다루는 기능)에 강점이 있지만, 인증·스토리지·실시간은 아예 제공하지 않아서 그 부분은 별도로 구성해야 합니다.

비용

Free 플랜은 프로젝트당 DB 500MB, 파일 스토리지 1GB, 월간 활성 사용자(MAU) 50,000명까지 무료입니다. 다만 7일간 API 호출이 없으면 프로젝트가 자동으로 일시정지되고(대시보드에서 수동으로 재시작 가능), 무료 조직당 활성 프로젝트는 2개로 제한됩니다. 사이드 프로젝트나 데모 용도로는 충분하지만, 상시 운영되는 서비스라면 이 일시정지 정책 때문에 사실상 Pro 플랜부터 시작하게 됩니다.

Pro 플랜은 프로젝트당 월 25달러부터 시작하고, DB 8GB, 파일 스토리지 100GB, 아웃바운드 트래픽(egress) 250GB, MAU 100,000명, 매일 자동 백업이 포함됩니다. 이 한도를 넘으면 항목별로 추가 과금되는 구조입니다 (예: DB 용량 추가, egress 추가). 정확한 초과 과금 단가는 변경될 수 있으니 실제 도입 전에는 공식 요금 페이지에서 최신 수치를 다시 확인하는 걸 권합니다.

Pro 플랜부터는 프로젝트 일시정지가 없어지고 리소스 한도도 크게 올라가는 게 핵심 차이라, "무료 티어로 얼마나 버티는가"보다는 "언제 Pro로 넘어가는가"를 기준으로 비용을 가늠하는 게 더 맞는 접근입니다.

이 정도 기능을 직접 구축하려면 Postgres 호스팅, 인증 서버, S3 같은 스토리지, WebSocket 서버를 각각 운영해야 하는데, 작은 프로젝트에서는 이 네 가지를 따로 운영하는 수고가 월 25달러보다 더 큰 경우가 많습니다.

같은 테이블에 익명 키로 직접 질의하는 구조라, RLS 정책을 테이블마다 꼼꼼히 작성해야 한다는 부담은 분명히 있습니다. 반면 서버 쪽 API 레이어를 따로 두지 않아도 되는 만큼 초기 셋업 속도는 빠르고, Postgres 확장(pgvector 같은)을 그대로 쓸 수 있다는 점도 다른 BaaS에서는 흔치 않은 장점입니다.