개발

Supabase 무료 티어로 SaaS 만들기 — 한계와 우회법

Supabase 무료 플랜으로 SaaS MVP를 만들 때 부딪히는 한계와 우회 전략을 공식 문서 기준으로 정리합니다. 데이터베이스 크기, API 요청, Auth, Storage 제한을 극복하는 방법.

·8 min read
#Supabase#SaaS#무료 티어#백엔드#PostgreSQL

Supabase를 선택한 이유

Supabase 아키텍처

SaaS MVP의 백엔드를 고를 때 흔한 선택지는 세 가지입니다:

  1. 직접 서버 구축 (Node.js + PostgreSQL)
  2. Firebase
  3. Supabase

Firebase는 NoSQL 기반이라 관계형 데이터가 많은 서비스와는 잘 맞지 않고, 직접 서버를 구축하면 인프라 관리에 시간을 뺏깁니다. 그래서 PostgreSQL 기반 + 무료 티어가 넉넉한 Supabase가 자주 선택됩니다.

결론부터 말하면, MVP 단계에서 Supabase 무료 티어는 충분한 편입니다. 다만 사용자가 늘면 몇 가지 벽에 부딪히는데, 이 글은 그 한계와 우회법을 정리합니다. (한도 수치는 작성 시점 기준이니, 최신 값은 공식 Pricing 페이지를 확인하세요.)

무료 티어의 실제 한계

Supabase 공식 Pricing에 나오는 무료 플랜 제한:

항목무료 한도
데이터베이스 크기500MB
Storage1GB
대역폭월 5GB
Edge Functions월 50만 호출
Auth MAU50,000명
프로젝트 수2개
일시 정지7일 미접속 시 자동 정지

마지막 항목이 진짜 문제입니다. 7일 동안 API 호출이 없으면 프로젝트가 자동으로 일시 정지됩니다. SaaS를 운영하는데 백엔드가 갑자기 꺼지면 어떻게 될까요? 사용자 입장에서는 서비스 장애입니다.

한계 #1: 자동 정지 우회

가장 간단한 우회법은 크론 잡으로 주기적 핑을 보내는 겁니다:

// Vercel Cron 또는 GitHub Actions 활용
// cron: 0 */6 * * * (6시간마다)
export async function GET() {
  const { data, error } = await supabase
    .from('health_check')
    .select('id')
    .limit(1);
  
  return Response.json({ status: 'alive', timestamp: new Date() });
}

GitHub Actions를 쓰면 완전 무료로 이 크론을 돌릴 수 있습니다. 예를 들어 6시간마다 한 번씩 health check 쿼리를 날리면 됩니다.

한계 #2: 500MB 데이터베이스

500MB가 작아 보이지만, 실제로 써보면 텍스트 기반 SaaS에서는 꽤 넉넉합니다.

하지만 이미지나 파일을 DB에 직접 저장하면 순식간에 차버립니다. 우회법:

이미지는 Storage로, 메타데이터만 DB에

-- ❌ 이렇게 하지 마세요
CREATE TABLE posts (
  id uuid PRIMARY KEY,
  image bytea  -- DB에 이미지 직접 저장
);

-- ✅ 이렇게 하세요
CREATE TABLE posts (
  id uuid PRIMARY KEY,
  image_url text  -- Storage URL만 저장
);

오래된 데이터 아카이빙

활성 데이터만 Supabase에 두고, 6개월 이상 된 데이터는 주기적으로 내보내서 Cloudflare R2 (무료 10GB)에 보관합니다.

// 월 1회 아카이빙 스크립트
const oldData = await supabase
  .from('logs')
  .select('*')
  .lt('created_at', sixMonthsAgo);

// R2에 JSON으로 업로드
await r2.put(`archive/${year}/${month}.json`, JSON.stringify(oldData));

// Supabase에서 삭제
await supabase
  .from('logs')
  .delete()
  .lt('created_at', sixMonthsAgo);

한계 #3: 프로젝트 2개 제한

무료 플랜에서 프로젝트는 2개까지입니다. 개발(dev)과 프로덕션(prod)으로 나누면 딱 2개라 여유가 없습니다.

우회법: 하나의 프로젝트에서 스키마로 환경 분리

-- 개발 환경
CREATE SCHEMA dev;
CREATE TABLE dev.users (...);

-- 프로덕션 환경  
CREATE SCHEMA prod;
CREATE TABLE prod.users (...);

RLS(Row Level Security) 정책도 스키마별로 다르게 적용할 수 있어서, 하나의 프로젝트로도 환경 분리가 가능합니다.

한계 #4: Edge Functions 제한

월 50만 호출이 많아 보이지만, 실시간 기능이 있으면 금방 소진됩니다. 우회법:

  • 클라이언트 사이드 로직 최대한 활용: 단순 계산은 브라우저에서
  • Batch 처리: API 호출을 묶어서 한 번에
  • 캐싱: 같은 요청은 클라이언트에서 캐시
// ❌ 매번 호출
const user = await supabase.auth.getUser();

// ✅ 캐시 활용
const getCachedUser = (() => {
  let cache: User | null = null;
  let lastFetch = 0;
  
  return async () => {
    if (cache && Date.now() - lastFetch < 300000) return cache;
    const { data } = await supabase.auth.getUser();
    cache = data.user;
    lastFetch = Date.now();
    return cache;
  };
})();

RLS 설정: 무료 티어의 숨은 강점

Supabase의 Row Level Security는 무료 티어에서도 완전히 사용 가능합니다. 관계형 데이터가 많다면 Firebase 대신 Supabase를 고를 만한 큰 이유입니다.

-- 사용자 본인 데이터만 접근 가능
ALTER TABLE user_data ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view own data"
  ON user_data FOR SELECT
  USING (auth.uid() = user_id);

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

서버 사이드 보안 로직을 DB 레벨에서 처리하니, Edge Function 호출을 줄이는 효과도 있습니다.

실전 아키텍처

무료 티어 중심으로 짤 수 있는 아키텍처 예시입니다:

[Next.js on Vercel] ← 프론트엔드
       ↓
[Supabase Free Tier]
  ├── PostgreSQL (데이터)
  ├── Auth (인증)
  ├── Storage (파일)
  └── Realtime (웹소켓)
       ↓
[Cloudflare R2] ← 아카이빙
[GitHub Actions] ← 크론/자동화

이 구성은 각 서비스의 무료 한도 안에서는 월 운영비 0원입니다. 서버를 직접 두는 쪽이 궁금하다면 Mac mini를 24시간 서버로 쓴 기록에 전기세와 세팅을 적었습니다.

유료 전환 타이밍

무료 티어로 버티다가 언제 유료로 갈아타야 할까요? 하나의 기준:

  • DB 사용량이 400MB 넘으면 (80% 도달)
  • MAU가 30,000 넘으면
  • 수익이 발생하기 시작하면 (Pro 플랜 월 $25는 충분히 감당 가능)

마무리

Supabase 무료 티어는 "무료치고 괜찮은" 수준이 아니라, MVP부터 초기 성장까지 진지하게 쓸 수 있는 레벨입니다. 자동 정지와 용량 제한만 잘 우회하면, 유료 플랜 없이도 꽤 오래 버틸 수 있습니다.

관련 프로젝트들도 참고하세요:


참고 링크:

관련 글