Skip to content
VibeStartVibeStart介绍博客
返回列表

마이크로 SaaS 90일 빌드 — Stripe·Supabase·Vercel 무료 plan으로 $1,200 MRR (2026)

Bolt prototype에서 Stripe checkout·Supabase Auth·Vercel deploy까지 90일 안에 마이크로 SaaS 빌드 흐름. 무료 plan으로 시작해 결제 webhook 통합과 사용자 5명 → 48명 ($1,200 MRR) 도달까지 실제 코드 30-50줄 가이드.

마이크로 SaaSStripe 통합Supabase AuthVercel 배포Next.js App RouterMRRindie devAI 빌드결제 webhookproduction migration

🤔 마이크로 SaaS 90일 빌드 진짜 질문

마이크로 SaaS를 빌드해서 90일 안에 첫 결제 사용자 5명을 만들 수 있나. 답은 "가능하지만 빌드 흐름과 통합 패턴이 정해져 있을 때만 그렇다"이다. 마이크로 SaaS 시장의 중앙값은 90일 후 $1,200 MRR(유료 사용자 약 48명 × $25)이고, 이 도달 곡선이 Stripe checkout + Supabase Auth + Vercel Functions 조합으로 거의 표준화돼 있다. 본인이 같은 패턴을 따르면 90일 동안 실제로 빌드해야 할 코드는 30-50줄 수준이고, 나머지는 사용자 인터뷰·UX 다듬기·외부 마케팅이 차지한다.

이 글은 비전공자 톤의 짝꿍 글 AI 부업 월 1500$ 가능? 바이브코딩 수익 5가지 데이터가 분포·5경로 비교를 다뤘다면, 이쪽은 개발자 시각으로 90일 빌드의 코드와 통합 패턴을 단계별로 풀어낸다. Bolt prototype에서 본인 Next.js로 옮기는 시점 판단은 별도 정리한 Bolt.new에서 본인 Next.js 프로젝트로 졸업하는 시점 글에서 다뤘으니 함께 보면 그림이 잡힌다.

📋 90일 빌드 흐름 — Phase 1-3

Phase기간결과물핵심 코드
1Day 1-30MVP + 무료 사용자 5명Supabase schema·Auth, Bolt 또는 Next.js scaffold
2Day 31-60결제 통합 + 유료 5명 ($125 MRR)Stripe checkout session + webhook handler
3Day 61-90사용자 48명 + $1,200 MRREmail automation·Product Hunt launch·자동화

3 phases가 시간 흐름과 결과물 모두 다른데, 가장 결정적 단계는 Phase 2 결제 통합이다. 무료 사용자에서 유료 사용자로 카테고리가 넘어가는 순간이고, 이 시점부터 본인이 "결제 받는 사람"으로 분류된다.

🏗 Phase 1 — MVP 빌드 (Day 1-30)

먼저 빌드 도구를 결정해야 한다. 두 갈래가 있다. Bolt 또는 Lovable 같은 AI 빌더로 prototype을 빠르게 만들고 graduate하는 길과, 처음부터 본인 Next.js + Supabase로 빌드하는 길이다. 90일 흐름에서는 첫 1-2주는 Bolt prototype이 사용자 피드백 받는 데 빠르고, 결제 통합 시점부터 본인 Next.js로 graduate하는 게 표준이다.

🗄 Supabase schema 첫 설계

특정 직군 1개를 타겟하는 작은 도구라면 데이터 모델은 보통 3-4 테이블로 끝난다. 사용자(profiles)·구독 상태(subscriptions)·핵심 도메인 entity 1개·로그(events) 정도다. Stripe webhook이 들어올 때 subscriptions 테이블이 단일 source of truth 역할을 하기 때문에 처음부터 명확하게 잡아두면 Phase 2가 빠르다.

-- supabase/migrations/0001_init.sql
create table profiles (
  id uuid primary key references auth.users on delete cascade,
  email text unique not null,
  created_at timestamptz default now()
);

create table subscriptions (
  user_id uuid primary key references profiles on delete cascade,
  stripe_customer_id text unique,
  stripe_subscription_id text unique,
  status text check (status in ('active','trialing','past_due','canceled')),
  current_period_end timestamptz,
  updated_at timestamptz default now()
);

create index subscriptions_status_idx on subscriptions(status);

profilesauth.users를 참조하기 때문에 Supabase Auth가 자동으로 사용자를 만들면 trigger로 profiles row를 생성하는 게 정석이다. Supabase 공식 문서의 handle_new_user trigger 예시를 그대로 가져다 쓰면 된다. 마무리 확인은 Supabase Studio에서 테이블 3개가 보이고 RLS(Row Level Security)가 활성화돼 있는지 점검하는 것이다.

🔐 Supabase Auth로 가입 흐름

가입 흐름은 이메일 magic link 또는 OAuth(Google·GitHub) 둘 중 하나로 시작하는 게 90일 흐름에 맞다. 외부 사용자 5명을 빠르게 받는 게 목표라 회원가입 마찰이 가장 적은 방식을 고른다. Next.js App Router에서는 @supabase/ssr 패키지가 표준이고, 서버 컴포넌트에서 세션 읽는 패턴이 정해져 있다.

// app/auth/callback/route.ts
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get('code');

  if (code) {
    const cookieStore = cookies();
    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      { cookies: { /* ssr cookie adapter */ } }
    );
    await supabase.auth.exchangeCodeForSession(code);
  }
  return NextResponse.redirect(`${origin}/dashboard`);
}

본인 도메인의 /auth/callback URL을 Supabase Auth Providers 설정에 등록하는 게 마지막 점검 단계다. Day 14 정도면 가입한 사용자가 dashboard로 들어가는 흐름이 동작해야 한다.

💳 Phase 2 — Stripe 결제 통합 (Day 31-60)

Phase 2는 90일 흐름의 가장 결정적 단계다. 무료 5명에서 유료 5명($125 MRR)으로 카테고리가 바뀌고, 이 시점부터 본인은 "결제 받는 사람"으로 분류된다. 한국 시장 우선이면 토스페이먼츠가 정석이고, 글로벌 우선이면 Stripe가 표준이다. 두 결제 통합 패턴은 별도 정리한 Stripe·토스페이먼츠 결제 받기 가이드 글에서 비교했다.

🛒 Stripe Checkout Session 만들기

Stripe Checkout은 호스팅된 결제 페이지를 제공해서 본인이 결제 UI를 만들 필요가 없다. Next.js App Router의 Server Action 또는 Route Handler에서 session을 만들고 사용자를 redirect시키는 게 가장 단순한 패턴이다.

// app/api/checkout/route.ts
import Stripe from 'stripe';
import { NextResponse } from 'next/server';
import { getCurrentUser } from '@/lib/auth';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: Request) {
  const user = await getCurrentUser();
  if (!user) return new NextResponse('Unauthorized', { status: 401 });

  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    customer_email: user.email,
    line_items: [{ price: process.env.STRIPE_PRICE_ID_PRO!, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?success=1`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing?canceled=1`,
    metadata: { user_id: user.id },
  });

  return NextResponse.json({ url: session.url });
}

metadata.user_id가 webhook에서 다시 사용자를 매칭하는 키라 빼먹지 말아야 한다. 마무리 확인은 Stripe Dashboard의 Test mode에서 테스트 카드 4242 4242 4242 4242로 결제를 완료해보고 Stripe events에 checkout.session.completed가 들어오는지 보는 것이다.

🪝 Webhook Handler — 단일 source of truth

결제 완료 → subscriptions 테이블 업데이트의 흐름은 webhook이 책임진다. Stripe가 보내는 이벤트를 받아 본인 DB의 구독 상태를 갱신하는 게 표준이고, Vercel Edge Function이나 Node Function으로 가볍게 처리할 수 있다.

// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe';
import { NextResponse } from 'next/server';
import { createServiceClient } from '@/lib/supabase-admin';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;

export async function POST(req: Request) {
  const body = await req.text();
  const sig = req.headers.get('stripe-signature')!;
  const event = stripe.webhooks.constructEvent(body, sig, webhookSecret);

  const supabase = createServiceClient();

  if (event.type === 'checkout.session.completed' ||
      event.type === 'customer.subscription.updated') {
    const sub = event.data.object as Stripe.Subscription;
    await supabase.from('subscriptions').upsert({
      user_id: sub.metadata.user_id,
      stripe_customer_id: sub.customer as string,
      stripe_subscription_id: sub.id,
      status: sub.status,
      current_period_end: new Date(sub.current_period_end * 1000),
      updated_at: new Date(),
    });
  }
  return NextResponse.json({ received: true });
}

stripe.webhooks.constructEvent가 signature 검증까지 자동으로 해주기 때문에 임의 요청을 막아준다. Stripe CLI의 stripe listen --forward-to localhost:3000/api/webhooks/stripe로 로컬 테스트가 가능하고, 마무리 확인은 결제 한 번에 subscriptions.status = 'active'가 DB에 박혀 있는지 보는 것이다.

🚧 자주 막히는 실수 3가지

처음 결제 통합에서 막히는 패턴이 거의 정해져 있다. 첫째는 webhook secret이 환경별로 다른 걸 모르고 production secret을 local에 쓰는 경우다. 둘째는 metadata.user_id를 빼먹어서 webhook에서 사용자 매칭이 안 되는 경우. 셋째는 webhook handler가 5초 안에 응답을 못 줘서 Stripe가 retry하는데 idempotency 처리를 안 해 같은 row가 여러 번 박히는 경우다. upsert를 쓰고 응답을 즉시 200으로 돌려준 다음 무거운 작업은 background job으로 분리하는 게 정석이다.

📈 Phase 3 — 사용자 확보와 자동화 (Day 61-90)

Phase 3은 빌드 작업보다 외부 마케팅 + 자동화 비중이 크다. Day 61-75에 Product Hunt 런칭 또는 인플루언서 3명 접촉으로 신규 사용자 30-50명을 받고, Day 76-90에 그 중 결제 전환을 만들어 유료 48명($1,200 MRR) 도달이 목표다.

📮 Email automation — Resend로 30줄

신규 가입 직후 환영 메일, 무료 trial 종료 D-3 알림, 결제 후 영수증 같은 트랜잭셔널 이메일은 Resend 또는 Postmark로 처리하는 게 가볍다. Vercel과의 통합이 잘 돼 있고 무료 plan이 한 달 3,000건이라 Phase 3 초반은 무료 plan으로 충분하다.

// lib/email.ts
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY!);

export async function sendWelcome(email: string) {
  return resend.emails.send({
    from: 'hello@yoursaas.com',
    to: email,
    subject: '환영합니다 — 첫 시작 가이드 3분',
    html: '<p>안녕하세요...</p>',
  });
}

Supabase Auth의 signed_up event를 webhook으로 받거나, 회원가입 직후 Server Action에서 직접 호출하는 두 패턴이 있다. 트래픽이 적을 때는 직접 호출이 단순해서 좋다.

🚀 Product Hunt 런칭 점검 목록

Product Hunt 런칭은 1회성 이벤트지만 신규 사용자 100-300명을 한 번에 받을 수 있는 가장 효율적 채널이다. 화요일에서 목요일 사이 PT 자정에 런칭하고, 본인 채널에 24시간 동안 5번 정도 노출하는 게 정석이다. 점검할 항목은 첫 화면 스크린샷·30초 데모 영상·gallery 이미지 5장·tagline 60자 이내·사용자 댓글 응답 패턴이다. 마무리 확인은 런칭 24시간 후 Product Hunt 페이지의 사용자 수가 100명 이상이고 MAKER 댓글 응답률이 90% 이상인 것이다.

🔁 자동화 — 운영 부하 줄이기

Day 90 이후 본인이 손을 떼도 도구가 자동 운영되는 구조를 만들어야 한다. Resend email automation·Stripe billing portal(사용자가 직접 결제 카드 변경·구독 취소)·Supabase RLS로 데이터 격리·Sentry 에러 추적 4가지면 95%의 운영 케이스가 자동 처리된다. 사용자 5%만 본인이 직접 응대하는 구조가 되면 Phase 3 끝에서 다음 90일을 시작할 시간이 만들어진다.

🚨 흔한 실수 5가지

90일 빌드에서 본인이 빠지기 쉬운 실수 패턴이 정해져 있다.

#실수결과예방
1너무 일반적 도구 (모든 직군 대상)무료에서 유료 전환 0%특정 직군 1개 타겟 명확히
2결제 통합을 Phase 1에 시도MVP 출시 지연 1개월Phase 2까지 무료 사용자 검증
3webhook idempotency 미처리DB 중복 row, status 꼬임upsert + event ID 기반 dedup
4무료 plan만 운영, 유료 시도 안 함90일 후 MRR $0Day 30 직후 가격 책정 + 결제 통합
5Product Hunt 런칭 준비 부족신규 사용자 30명 미만자료 5종 + 24시간 응답 패턴 미리

5개 모두 첫 빌드에서 누구나 한두 개는 한다. Phase 1·2 시작 전에 이 표를 출력해서 책상 옆에 붙여두면 같은 실수의 빈도가 줄어든다.

🔍 비교 — Stripe vs Lemon Squeezy

결제 통합 도구를 정할 때 Stripe와 Lemon Squeezy가 가장 큰 두 갈래다.

기준StripeLemon Squeezy
한국 카드 결제✅ 가능 (직접 onboarding)❌ 글로벌 카드만
Tax/VAT 자동 처리❌ 본인 처리 (Stripe Tax 별도)✅ Merchant of Record
통합 코드 분량약 50줄약 30줄
수수료2.9% + $0.305% + $0.50
운영 부하중 (Tax 신고 필요)낮음 (LMSY가 신고)

대부분 indie developer는 Lemon Squeezy가 운영 부하 낮아서 빠른 시작에 유리하지만, 한국 카드 결제 비중이 높은 시장이면 Stripe + 토스페이먼츠 조합이 정답이다. 본인 시장에 따라 1주 안에 결정하는 게 빠르다.

🛠 운영 팁 — Vercel + Supabase 비용 한도

90일 동안 Vercel과 Supabase 무료 plan 한도를 넘지 않는 운영 패턴이 있다.

  • Vercel Hobby plan: 100GB bandwidth/월, Function 100 hours, 100k Edge Requests. 첫 1,000 사용자까지 무료 plan 충분하다.
  • Supabase Free plan: DB 500MB, Auth 50,000 MAU, Storage 1GB. Phase 3 끝까지 무료 plan 가능하다.
  • 트래픽이 무료 plan 한도 80% 도달하면 Pro plan으로 옮긴다. Vercel Pro $20/월, Supabase Pro $25/월이라 합쳐도 $45/월이고, 이 시점에 본인 MRR이 이미 $1,200 이상이라 비용 비중이 4% 미만이다.

비용 모니터링은 Vercel Dashboard의 Usage 탭과 Supabase Dashboard의 Project Settings → Usage에서 매주 1회 5분이면 끝난다.

⚠️ 주의: 환율·서비스 가격 정책은 분기 단위로 바뀐다. 본 글의 비용 수치는 2026년 5월 기준이고 실제 청구 시점에 공식 페이지에서 직접 확인해야 한다. webhook signature 검증을 우회하거나 비활성화하면 임의 요청 공격에 노출되니 production에서는 절대 끄지 말아야 한다.

❓ 자주 묻는 질문

Q. 90일에 정말 $1,200 MRR이 가능한가요?

마이크로 SaaS 패턴의 중앙값 추정이다. 즉 정상 곡선의 절반은 도달, 절반은 미달이라는 뜻이다. 도구 카테고리 적합성·마케팅 일관성·사용자 피드백 반영 속도에 따라 ±50% 범위에서 움직인다. 첫 90일에 $0-500 구간에 머무는 70%도 정상이고 다음 90일에 다음 구간으로 넘어가는 흐름이 더 중요하다.

Q. Bolt로 시작하는 게 빠른가요, 처음부터 Next.js로 가는 게 빠른가요?

90일 흐름에서는 Bolt 또는 Lovable로 첫 1-2주 prototype을 빠르게 만들어 사용자 피드백을 받고, Phase 2 결제 통합 시점에 본인 Next.js로 graduate하는 게 정석이다. 처음부터 Next.js로 가면 Day 1-7에 환경 셋업과 boilerplate에 시간을 빼앗기고 사용자 검증이 늦어진다.

Q. Stripe 한국 onboarding은 얼마나 걸리나요?

평균 5-10영업일이다. 사업자 등록증·통장 사본·신분증을 제출하고 Stripe 측 검토를 거친다. 개인 사업자도 가능하고 법인이 더 빠른 편이다. 한국 카드 결제 비중이 50% 이상이면 Stripe + 토스페이먼츠 조합이 정답이고, 20% 미만이면 Lemon Squeezy가 운영 부하 낮아서 유리하다.

Q. webhook handler가 5초 안에 응답을 못 주면 어떻게 되나요?

Stripe가 retry를 시작한다. 같은 이벤트가 3-5번 들어오는 게 보통이라 idempotency 처리를 안 하면 DB가 꼬인다. handler에서 무거운 작업(이메일 발송·외부 API 호출)은 background job으로 분리하고 webhook 자체는 200을 즉시 돌려준 다음 비동기로 처리하는 게 표준이다.

Q. 무료 사용자에서 유료 사용자로 전환율이 낮으면?

자주 있는 상황이다. 무료 사용자 5명 중 1-2명이 유료 전환되는 게 일반 패턴인데 0명이면 두 가지를 점검한다. 첫째는 도구가 정말 사용자의 문제를 풀고 있는지, 둘째는 가격이 사용자 인식 가치 대비 적절한지다. 사용자 5명에게 직접 인터뷰 30분씩 진행하면 답이 나온다.

Q. Supabase 대신 Firebase나 PlanetScale이 더 좋지 않나요?

도구마다 강점이 다르다. Supabase는 Postgres + Auth + Storage가 한 번에 통합되고 RLS가 강력해 1인 dev에 유리하다. Firebase는 NoSQL 기반이라 데이터 모델이 단순할 때 빠르고, PlanetScale은 MySQL 호환이 필요할 때 선택지가 된다. 90일 빌드의 95%는 Supabase가 가장 마찰이 적다.

Q. Email automation을 처음부터 통합해야 하나요?

Phase 1 말부터 Phase 2 초에 통합하는 게 정석이다. Phase 1에 사용자 5명일 때는 본인이 직접 메일 보내도 되고, 결제 통합 시점부터 트랜잭셔널 이메일(영수증·구독 갱신 알림)이 필수가 돼 자동화가 의미있다. Resend Free plan이 월 3,000건이라 Phase 3 초반까지 충분하다.

Q. 90일 후 도구 매각이 정말 가능한가요?

$1,000 MRR 이상이면 Acquire·MicroAcquire 같은 마켓플레이스에 등록 가능하다. 일반 거래 범위가 24-48배 multiple이라 $1,200 MRR이면 $28K-58K 매각이 정량적으로 가능하다. 도구 카테고리·성장률·이탈률에 따라 달라지고, 매각이 목표라면 처음부터 매각 가능 구조(사용자 데이터 분리·코드 정리·운영 매뉴얼)로 만들어두면 좋다.

안내: 본 글의 코드는 2026년 5월 기준 Stripe Node.js SDK v15·@supabase/ssr v0.5·Next.js 15 App Router 기준이다. 라이브러리 버전 업데이트 또는 API 변경에 따라 import 경로·메서드 시그니처가 달라질 수 있어 production 적용 전 공식 문서 최신 버전을 확인해야 한다. 결제 통합은 본인 사업자 등록 상태와 결제 도구 onboarding 정책에 따라 추가 절차가 발생할 수 있어 Stripe·토스페이먼츠 공식 가이드와 함께 진행하는 게 안전하다.

🔗 관련 글

90일 빌드 흐름은 Phase 1-3의 코드와 통합 패턴을 한 번 익히면 두 번째 도구부터는 Phase 2가 절반 시간으로 줄어든다. 본인이 만든 첫 마이크로 SaaS의 코드와 운영 노트가 다음 도구의 가장 강력한 자산이 된다.

📚 참고 자료