
안녕하세요!
재아군의 관찰인생입니다.
오늘은 "오픈소스 Firebase 대안"으로 빠르게 성장하고 있는 Supabase를 깊이 있게 분석해보겠습니다.
Firebase의 편리함은 좋지만 벤더 종속이 걱정되거나, PostgreSQL 기반의 관계형 데이터베이스가 필요한 프로젝트를 진행하고 계신다면 이 글이 특히 도움이 될 것입니다.
이 글은 Supabase를 처음 접하는 분부터, 이미 사용 중이지만 아키텍처를 더 깊이 이해하고 싶은 분 모두를 위해 작성되었습니다. 핵심 개념 → 아키텍처 → 실전 코드 → 비교 분석 → 베스트 프랙티스 순서로 진행합니다.

1. Supabase란 무엇인가?
핵심 정의
Supabase는 PostgreSQL을 기반으로 한 오픈소스 Backend-as-a-Service(BaaS) 플랫폼입니다. 2020년에 등장하여 "오픈소스 Firebase 대안"이라는 포지션으로 빠르게 성장했으며, 2025년 기준 GitHub 스타 75,000개 이상, 100만 개 이상의 데이터베이스가 호스팅되고 있습니다.
Firebase와 가장 큰 차이점은 관계형 데이터베이스(PostgreSQL)를 핵심에 두고 있다는 것입니다. NoSQL 기반인 Firebase Firestore와 달리, 복잡한 JOIN 쿼리, 트랜잭션, 외래 키 제약 등 RDBMS의 강점을 그대로 활용할 수 있습니다.
Supabase가 제공하는 핵심 서비스
| 서비스 | 기반 기술 | 설명 |
|---|---|---|
| Database | PostgreSQL | 완전한 PostgreSQL 인스턴스 — RLS, 함수, 트리거 모두 사용 가능 |
| Auth | GoTrue | 이메일, OAuth(Google, GitHub 등), 매직링크, 전화번호 인증 |
| Storage | S3 호환 | 파일 업로드/다운로드, 이미지 변환, CDN 배포 |
| Realtime | Phoenix(Elixir) | DB 변경사항 실시간 구독, Presence, Broadcast |
| Edge Functions | Deno | 서버리스 함수 — TypeScript, 글로벌 엣지 배포 |
| Vectors | pgvector | AI 임베딩 저장 및 유사도 검색 (RAG 파이프라인) |
등장 배경 — 왜 Supabase가 필요했나?
Firebase는 빠른 프로토타이핑에 탁월하지만, 프로젝트가 성장하면서 몇 가지 근본적인 한계에 부딪히게 됩니다:
- 벤더 종속(Vendor Lock-in) — Firestore의 독자적 쿼리 문법과 데이터 모델은 다른 DB로 마이그레이션이 극히 어렵습니다
- 복잡한 쿼리의 한계 — NoSQL 특성상 다중 테이블 JOIN, 서브쿼리, 집계 함수가 제한적입니다
- 비용 예측 어려움 — 읽기/쓰기 횟수 기반 과금으로 트래픽 증가 시 비용이 급증할 수 있습니다
- 오픈소스 부재 — 셀프호스팅이 불가능하고, 데이터 주권(Sovereignty) 확보가 어렵습니다
Supabase는 이런 문제를 "PostgreSQL이라는 검증된 기반 위에, Firebase 수준의 개발자 경험(DX)"을 제공하는 방식으로 해결했습니다.

2. 기술 아키텍처 심층 분석
Supabase의 가장 큰 기술적 특징은 기존 오픈소스 도구들을 조합하여 통합 플랫폼을 만들었다는 점입니다. 자체 기술을 최소화하고, 각 영역에서 가장 검증된 도구를 선택했습니다.
핵심 컴포넌트 구조
┌─────────────────────────────────────────────────┐
│ Client SDK │
│ (JS/TS, Flutter, Python, Swift) │
└──────────┬──────────────┬───────────────────────┘
│ │
┌──────▼──────┐ ┌────▼─────────────┐
│ PostgREST │ │ GoTrue (Auth) │
│ (REST API) │ │ JWT 발급/검증 │
└──────┬──────┘ └────┬─────────────┘
│ │
┌──────▼──────────────▼───────────────┐
│ PostgreSQL │
│ ┌──────────┐ ┌──────────┐ │
│ │ RLS │ │ pgvector │ │
│ │ (보안정책)│ │ (AI벡터) │ │
│ └──────────┘ └──────────┘ │
└──────┬──────────────────────────────┘
│
┌──────▼──────────────────────────────┐
│ Realtime (Phoenix/Elixir) │
│ DB 변경 → WebSocket 브로드캐스트 │
└─────────────────────────────────────┘
PostgREST — SQL을 REST API로
PostgREST는 PostgreSQL 스키마를 자동으로 RESTful API로 노출하는 Haskell 기반 서버입니다. 테이블을 만들면 즉시 CRUD 엔드포인트가 생성되며, 별도의 API 서버 코드를 작성할 필요가 없습니다.
// Supabase Client로 데이터 조회 — 내부적으로 PostgREST 호출
const { data, error } = await supabase
.from('posts')
.select(`
id, title, created_at,
author:profiles(name, avatar_url),
comments(count)
`)
.eq('status', 'published')
.order('created_at', { ascending: false })
.range(0, 9);
// 위 코드는 내부적으로 이런 SQL로 변환됩니다:
// SELECT id, title, created_at, profiles.name, profiles.avatar_url, count(comments)
// FROM posts
// JOIN profiles ON posts.author_id = profiles.id
// LEFT JOIN comments ON comments.post_id = posts.id
// WHERE status = 'published'
// ORDER BY created_at DESC LIMIT 10;
Row Level Security (RLS) — 데이터베이스 레벨 보안
Supabase 보안의 핵심은 RLS(Row Level Security)입니다. API 서버에서 권한 체크를 하는 것이 아니라, PostgreSQL 자체에서 행 단위로 접근을 제어합니다.
-- 사용자는 자신의 데이터만 읽을 수 있음
CREATE POLICY "Users can read own data"
ON profiles FOR SELECT
USING (auth.uid() = user_id);
-- 인증된 사용자만 게시글 작성 가능
CREATE POLICY "Authenticated users can insert posts"
ON posts FOR INSERT
WITH CHECK (auth.role() = 'authenticated');
-- 관리자는 모든 데이터 접근 가능
CREATE POLICY "Admins have full access"
ON posts FOR ALL
USING (auth.jwt() ->> 'role' = 'admin');
이 방식의 장점은 어떤 클라이언트(웹, 모바일, 서버)에서 접근하든 동일한 보안 정책이 적용된다는 것입니다. API 미들웨어에서 권한 체크를 빠뜨리는 실수를 원천적으로 방지할 수 있습니다.

3. Realtime과 Edge Functions
Realtime — DB 변경을 실시간으로
Supabase Realtime은 Elixir/Phoenix 기반으로 구축되어 있으며, PostgreSQL의 WAL(Write-Ahead Log)을 감시하여 데이터 변경사항을 WebSocket으로 브로드캐스트합니다.
// 실시간 채팅 구독 예시
const channel = supabase
.channel('chat-room-1')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages',
filter: 'room_id=eq.1' },
(payload) => {
console.log('새 메시지:', payload.new);
addMessageToUI(payload.new);
}
)
.on('presence', { event: 'sync' }, () => {
const state = channel.presenceState();
updateOnlineUsers(Object.keys(state));
})
.subscribe();
// Presence — 현재 접속자 추적
await channel.track({
user_id: currentUser.id,
online_at: new Date().toISOString()
});
Realtime은 세 가지 모드를 지원합니다:
- Postgres Changes — DB INSERT/UPDATE/DELETE를 실시간 감지 (RLS 정책이 그대로 적용됨)
- Broadcast — 채널 기반 메시지 전송 (DB를 거치지 않아 초저지연)
- Presence — 접속자 상태 추적 (온라인/오프라인, 커서 위치 등)
Edge Functions — 서버리스 로직
Deno 런타임 기반의 Edge Functions는 TypeScript를 네이티브로 지원하며, 전 세계 엣지 노드에 배포됩니다. Webhook 처리, 외부 API 연동, 결제 로직 등 클라이언트에 노출할 수 없는 서버 로직을 처리할 때 사용합니다.
// supabase/functions/send-welcome-email/index.ts
import { serve } from 'https://deno.land/std/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
serve(async (req) => {
const { record } = await req.json(); // DB trigger에서 전달된 신규 유저
// 환영 이메일 발송
await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: { Authorization: `Bearer ${Deno.env.get('RESEND_API_KEY')}` },
body: JSON.stringify({
to: record.email,
subject: '가입을 환영합니다!',
html: `
${record.name}님, 환영합니다!
`
})
});
return new Response(JSON.stringify({ success: true }));
});
4. 실전 프로젝트 셋업 가이드
Supabase로 실제 프로젝트를 시작하는 과정을 단계별로 살펴보겠습니다.
Step 1: 프로젝트 초기화
# Supabase CLI 설치
npm install -g supabase
# 프로젝트 초기화
supabase init
supabase start # 로컬 Docker 환경 실행
# 클라이언트 SDK 설치
npm install @supabase/supabase-js
Step 2: 스키마 설계 & 마이그레이션
-- supabase/migrations/001_create_tables.sql
CREATE TABLE profiles (
id UUID REFERENCES auth.users PRIMARY KEY,
name TEXT NOT NULL,
avatar_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
author_id UUID REFERENCES profiles(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT,
status TEXT DEFAULT 'draft' CHECK (status IN ('draft', 'published')),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- RLS 활성화
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- 신규 가입 시 자동으로 profiles 레코드 생성
CREATE FUNCTION handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO profiles (id, name, avatar_url)
VALUES (NEW.id, NEW.raw_user_meta_data->>'name', NEW.raw_user_meta_data->>'avatar_url');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION handle_new_user();
Step 3: 클라이언트 연동 (Next.js 예시)
// lib/supabase.ts
import { createBrowserClient } from '@supabase/ssr';
export const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
// app/posts/page.tsx — 게시글 목록
export default async function PostsPage() {
const { data: posts } = await supabase
.from('posts')
.select('id, title, created_at, author:profiles(name)')
.eq('status', 'published')
.order('created_at', { ascending: false });
return (
<ul>
{posts?.map(post => (
<li key={post.id}>
{post.title} — {post.author.name}
</li>
))}
</ul>
);
}

5. 경쟁 기술 비교 분석
BaaS 시장의 주요 선택지를 다각도로 비교해보겠습니다.
| 항목 | Supabase | Firebase | Appwrite | PocketBase |
|---|---|---|---|---|
| DB 유형 | PostgreSQL (관계형) | Firestore (NoSQL) | MariaDB | SQLite |
| 오픈소스 | O (Apache 2.0) | X | O (BSD 3) | O (MIT) |
| 셀프호스팅 | Docker Compose | 불가 | Docker | 단일 바이너리 |
| 실시간 | WebSocket + DB변경감지 | 네이티브 지원 | WebSocket | SSE |
| AI/벡터 지원 | pgvector 내장 | Vertex AI 연동 | X | X |
| 무료 티어 | 2개 프로젝트, 500MB | Spark(무제한 프로젝트) | 셀프호스팅 무료 | 완전 무료 |
| 추천 시나리오 | 관계형 데이터 + 확장성 | 빠른 프로토타입 + GCP | 완전 자체 운영 | 소규모/사이드 프로젝트 |
Supabase가 유리한 경우
- 복잡한 관계형 데이터 모델이 필요한 프로젝트 (e-커머스, SaaS, CRM)
- 기존 PostgreSQL 경험이 있는 팀
- 벤더 종속을 피하고 싶은 경우 (언제든 셀프호스팅 전환 가능)
- AI 기능(벡터 검색, 임베딩)을 DB와 통합하고 싶은 경우
Firebase가 유리한 경우
- GCP 생태계를 이미 사용 중인 팀
- 문서형 데이터(NoSQL)가 자연스러운 도메인 (채팅, IoT 센서 데이터)
- 모바일 앱 중심 + FCM 푸시 알림이 핵심인 프로젝트
6. 도입 시 베스트 프랙티스
실제 프로덕션에서 Supabase를 운영하면서 얻은 핵심 원칙들입니다.
1) RLS는 처음부터 설계하라
RLS를 나중에 추가하면 기존 쿼리가 예상치 못하게 빈 결과를 반환할 수 있습니다. 테이블 생성과 동시에 RLS 정책을 정의하는 것을 습관화해야 합니다.
2) 마이그레이션을 코드로 관리하라
Supabase 대시보드의 SQL Editor는 편리하지만, 프로덕션에서는 반드시 supabase/migrations/ 디렉토리에 SQL 파일로 관리해야 합니다. CI/CD에서 supabase db push로 자동 배포할 수 있습니다.
3) TypeScript 타입을 자동 생성하라
# DB 스키마에서 TypeScript 타입 자동 생성
supabase gen types typescript --local > src/types/database.ts
# 타입 안전한 클라이언트 사용
import { Database } from '@/types/database';
const supabase = createClient<Database>(url, key);
4) 인덱스를 잊지 마라
PostgREST가 자동으로 API를 생성해주지만, 쿼리 성능은 PostgreSQL 인덱스에 의존합니다. 자주 필터링하는 컬럼에는 반드시 인덱스를 추가하세요.
-- 자주 사용하는 필터 컬럼에 인덱스
CREATE INDEX idx_posts_status ON posts(status);
CREATE INDEX idx_posts_author ON posts(author_id);
CREATE INDEX idx_posts_created ON posts(created_at DESC);
5) 로컬 개발 환경을 적극 활용하라
supabase start로 Docker 기반 로컬 환경이 구동되며, 대시보드(localhost:54323), DB(localhost:54322), API(localhost:54321)를 모두 로컬에서 테스트할 수 있습니다. 클라우드 프로젝트를 소모하지 않고 개발할 수 있다는 큰 장점이 있습니다.

7. AI 시대의 Supabase — pgvector와 RAG
Supabase가 최근 가장 주목받는 이유 중 하나는 pgvector 확장을 통한 AI 워크로드 지원입니다. 별도의 벡터 DB(Pinecone, Weaviate 등)를 운영할 필요 없이, 기존 PostgreSQL 안에서 임베딩 저장과 유사도 검색을 처리할 수 있습니다.
-- pgvector 확장 활성화
CREATE EXTENSION IF NOT EXISTS vector;
-- 문서 임베딩 테이블
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(1536), -- OpenAI text-embedding-3-small 차원
metadata JSONB
);
-- 유사도 검색 함수
CREATE FUNCTION match_documents(
query_embedding VECTOR(1536),
match_count INT DEFAULT 5,
match_threshold FLOAT DEFAULT 0.7
) RETURNS TABLE (id BIGINT, content TEXT, similarity FLOAT)
AS $$
SELECT id, content, 1 - (embedding <=> query_embedding) as similarity
FROM documents
WHERE 1 - (embedding <=> query_embedding) > match_threshold
ORDER BY embedding <=> query_embedding
LIMIT match_count;
$$ LANGUAGE sql;
이 접근법의 핵심 장점은 관계형 데이터와 벡터 데이터를 하나의 트랜잭션에서 다룰 수 있다는 것입니다. 예를 들어 "이 사용자가 작성한 문서 중 질문과 유사한 것"을 검색할 때, RLS + pgvector를 결합하면 한 번의 쿼리로 보안과 유사도 검색을 동시에 해결할 수 있습니다.
마무리
Supabase는 단순한 "Firebase 대안"을 넘어, PostgreSQL이라는 40년 검증된 기반 위에 현대적 개발자 경험을 올린 플랫폼으로 진화하고 있습니다.
특히 인상적인 점은:
- 오픈소스 조합 전략 — 자체 기술 대신 PostgREST, GoTrue, Phoenix 같은 검증된 도구를 통합
- RLS 중심 보안 — 데이터베이스 레벨에서 보안을 해결하여 API 계층의 복잡도를 크게 줄임
- pgvector 네이티브 지원 — AI 시대에 별도 벡터 DB 없이도 RAG 파이프라인 구축 가능
- 셀프호스팅 자유 — 클라우드에서 시작하고, 필요할 때 자체 인프라로 전환 가능
관계형 데이터 모델이 필요하고, 벤더 종속을 피하면서도 Firebase 수준의 개발 속도를 원한다면 Supabase는 현재 가장 균형 잡힌 선택지라고 할 수 있습니다.
이 글이 도움이 되셨다면 댓글과 공유 부탁드립니다. Supabase 관련 궁금한 점이 있다면 언제든 댓글로 남겨주세요!
다음 글에서 더 깊이 있는 내용으로 찾아뵙겠습니다.
감사합니다!
'개발&프로그래밍' 카테고리의 다른 글
| [Claude Code] VS Code 연동 완벽 가이드 — 설치부터 실전 활용까지 (0) | 2026.04.04 |
|---|---|
| tmux vs iTerm2 완벽 비교: 터미널 멀티플렉서와 터미널 에뮬레이터의 차이점, 장단점, 활용법 (0) | 2026.04.02 |
| [Claude] Claude Code CLI 명령어 완전 정복: 터미널에서 AI와 협업하는 실전 가이드 (0) | 2026.04.02 |
| [tmux vs iTerm2 완벽 비교: 터미널 멀티플렉서와 터미널 에뮬레이터의 차이점, 장단점, 활용법] 핵심 정리 — 현업 개발자가 알아야 할 모든 것 (0) | 2026.03.31 |
| [Obsidian] 심층 분석 — 아키텍처와 실전 활용 (0) | 2026.03.31 |
댓글