Firebase 요금 폭탄 맞고 Supabase로 갈아탄 후기
사이드 프로젝트에 Firebase를 쓰다가 어느 달 읽기 요청이 폭증하며 청구서가 30달러를 넘겼다. Firestore의 문서 읽기 단위 과금 구조는 트래픽이 조금만 늘어도 요금이 가파르게 오른다. 그날 바로 대안을 찾다가 Supabase를 발견했다.
Supabase는 오픈소스 Firebase 대체제로 불리는 백엔드 서비스다. PostgreSQL 기반이라 SQL을 그대로 쓸 수 있고, 인증·스토리지·실시간 구독까지 한 플랫폼에서 해결된다. 무료 플랜이 상당히 넉넉하다. 계정 생성부터 파이썬으로 데이터를 읽고 쓰는 실전 코드까지, 직접 검증한 내용을 모두 공개한다.
1. Supabase 무료 플랜 조건과 Firebase와의 핵심 차이
Supabase 무료 플랜(Free Tier)의 조건은 개인 프로젝트 기준으로 상당히 넉넉하다. 데이터베이스 500MB, 파일 스토리지 1GB, 월 50만 건의 Edge Function 호출, 그리고 무제한 API 요청이 포함된다. 한 가지 주의할 점은 프로젝트가 7일 이상 비활성 상태이면 자동으로 일시 정지된다는 것이다. 주기적으로 접속하거나 GitHub Actions로 헬스체크 요청을 보내두면 이 문제를 피할 수 있다.
Firebase와 가장 다른 점은 데이터 모델이다. Firebase는 NoSQL 문서 구조라 복잡한 관계형 쿼리가 어렵다. Supabase는 PostgreSQL이라 JOIN, 트랜잭션, 인덱스를 그대로 쓴다. SQL에 익숙한 개발자라면 오히려 더 편하다. 과금 구조도 다르다. Firebase는 읽기·쓰기 단위로 과금하고, Supabase 무료 플랜은 용량 기준이라 요청이 많아도 요금이 갑자기 오르지 않는다.
2. 프로젝트 생성 및 API 키 발급
계정 생성 및 프로젝트 셋업
supabase.com에서 구글 계정으로 가입하면 바로 대시보드로 진입한다. [New Project]를 클릭하고 프로젝트 이름, 데이터베이스 비밀번호(나중에 바꿀 수 없으니 안전한 것으로), 리전을 설정한다. 리전은 Northeast Asia (Seoul)을 선택하면 국내 사용자 기준 응답 속도가 가장 빠르다. 프로젝트 생성에 약 2분이 걸린다.
API 키와 URL 확인
대시보드 좌측 메뉴 [Project Settings] → [API]로 이동하면 두 가지 중요한 값이 있다. Project URL은 데이터베이스 엔드포인트이고, anon public 키는 클라이언트에서 사용하는 공개 API 키다. service_role 키는 서버 사이드에서만 써야 하는 비밀 키로 절대 공개 저장소에 올리면 안 된다. 두 값을 .env 파일에 저장한다.
SUPABASE_URL=https://프로젝트ID.supabase.co
SUPABASE_KEY=여기에_anon_public_키
테이블 생성: SQL 에디터 활용
대시보드의 [SQL Editor]에서 직접 SQL을 실행해 테이블을 만든다. 뉴스 크롤링 데이터를 저장하는 테이블을 예시로 만든다.
CREATE TABLE articles (
id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
url TEXT UNIQUE NOT NULL,
summary TEXT,
keywords TEXT[], -- 배열 타입 지원
sentiment TEXT,
source_file TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 중복 URL 삽입 방지 인덱스
CREATE UNIQUE INDEX articles_url_idx ON articles(url);
3. 파이썬 연동 실전 코드: CRUD 완성
설치 및 클라이언트 초기화
pip install supabase python-dotenv
import os
from supabase import create_client, Client
from dotenv import load_dotenv
load_dotenv()
supabase: Client = create_client(
os.getenv("SUPABASE_URL"),
os.getenv("SUPABASE_KEY")
)
데이터 삽입 (Insert)
Gemini API로 추출한 JSON 데이터를 Supabase에 저장한다. upsert를 쓰면 URL이 이미 존재할 때 에러 대신 업데이트를 실행하므로 중복 문제를 자연스럽게 처리할 수 있다.
def save_article(article_data: dict) -> dict:
"""분석된 기사 데이터를 Supabase에 저장 (중복 URL은 업데이트)"""
response = supabase.table("articles").upsert(
article_data,
on_conflict="url" # URL 중복 시 나머지 필드 업데이트
).execute()
return response.data
# 사용 예시 — Gemini API로 추출한 데이터 그대로 저장
article = {
"title": "OpenAI, GPT-5 정식 출시 발표",
"url": "https://example.com/news/123",
"summary": "OpenAI가 차세대 모델 GPT-5를 공개했다. 추론 능력이 대폭 향상됐다.",
"keywords": ["OpenAI", "GPT-5", "AI"],
"sentiment": "긍정"
}
saved = save_article(article)
print(f"저장 완료: ID {saved[0]['id']}")
데이터 조회 (Select) 및 필터링
Supabase 파이썬 SDK는 메서드 체이닝 방식으로 쿼리를 쌓는다. SQL을 직접 쓰지 않아도 .eq(), .gte(), .order() 같은 메서드로 원하는 조건을 표현할 수 있다.
def get_recent_articles(limit: int = 10, sentiment: str = None) -> list:
"""최근 기사를 조회 — 감성 필터 옵션"""
query = supabase.table("articles") \
.select("id, title, summary, sentiment, created_at") \
.order("created_at", desc=True) \
.limit(limit)
if sentiment:
query = query.eq("sentiment", sentiment)
response = query.execute()
return response.data
# 최근 긍정 기사 5건 조회
positive_news = get_recent_articles(limit=5, sentiment="긍정")
for article in positive_news:
print(f"[{article['sentiment']}] {article['title']}")
데이터 삭제 (Delete)
def delete_old_articles(days_old: int = 30) -> int:
"""30일 이상 된 기사 자동 삭제"""
from datetime import datetime, timedelta
cutoff = (datetime.utcnow() - timedelta(days=days_old)).isoformat()
response = supabase.table("articles") \
.delete() \
.lt("created_at", cutoff) \
.execute()
count = len(response.data)
print(f"{count}건 삭제 완료")
return count
4. 직접 맞은 에러 2가지와 해결법
에러 ① 프로젝트가 갑자기 접속 불가 — 7일 비활성 자동 정지
일주일 출장을 다녀왔더니 자동화 봇이 전부 멈춰 있었다. Supabase 대시보드에 접속하니 “Project paused due to inactivity”라는 메시지가 떴다. 무료 플랜은 7일간 API 요청이 없으면 프로젝트를 자동으로 정지시킨다. 대시보드에서 [Restore project] 버튼을 누르면 2~3분 안에 복구된다. 재발 방지를 위해 GitHub Actions에 매일 한 번 SELECT 1을 실행하는 헬스체크 워크플로우를 추가했다. 그 이후로 한 번도 정지되지 않았다.
에러 ② RLS 정책 미설정으로 데이터 전체 조회 불가
테이블을 만들고 파이썬에서 데이터를 조회했는데 빈 배열만 돌아왔다. 데이터는 분명히 들어가 있었다. 원인은 Supabase의 RLS(Row Level Security)였다. 테이블 생성 시 기본으로 RLS가 활성화되는데, 정책(Policy)을 설정하지 않으면 아무 데이터도 반환하지 않는다. SQL 에디터에서 아래 명령어로 anon 키에 읽기 권한을 부여하자 해결됐다. 보안이 중요한 테이블에는 더 세밀한 정책을 적용해야 한다.
-- anon 사용자에게 articles 테이블 읽기 허용
CREATE POLICY "Allow public read" ON articles
FOR SELECT USING (true);
-- anon 사용자에게 삽입 허용
CREATE POLICY "Allow public insert" ON articles
FOR INSERT WITH CHECK (true);
5. 자동화 봇에 Supabase를 붙이면 달라지는 것들
지금까지 이 시리즈에서 만든 자동화 파이프라인에 Supabase를 데이터 저장소로 붙이면 구조가 완성된다.
- Gemini API 뉴스 수집 봇: 크롤링한 기사를 매일 Supabase에 누적 저장. 날짜·키워드 기준으로 언제든 꺼내볼 수 있는 개인 뉴스 아카이브가 된다
- Whisper 강의 노트 봇: 변환된 스크립트와 요약을 Supabase에 저장하면 영상 URL로 중복 처리가 자동 적용돼 같은 영상을 두 번 처리하는 낭비가 사라진다
- n8n 워크플로우: n8n의 Supabase 노드와 직접 연결해 코드 없이 데이터베이스에 읽고 쓰는 워크플로우를 시각적으로 구성할 수 있다
- 텔레그램 봇 히스토리: 봇이 보낸 알림 이력을 Supabase에 쌓아두면 중복 알림 방지 로직을 쉽게 구현할 수 있다
Firebase 요금 폭탄을 맞고 Supabase로 갈아탄 지 석 달이 지났다. 그동안 청구된 금액은 정확히 0원이다. SQL을 아는 사람이라면 오히려 더 직관적이고 강력하다는 것을 금방 느낀다. 개인 자동화 프로젝트의 데이터 저장소로는 현재로선 이보다 나은 선택을 찾지 못했다.