Bolt 케이스 스터디: 솔로 개발자가 주말 48시간 만에 풀스택 SaaS MVP를 출시한 방법
개요: 주말 48시간, 프롬프트 기반 개발로 SaaS MVP 완성
풀스택 SaaS 제품을 혼자서 주말에 출시한다는 것은 불과 1년 전만 해도 불가능에 가까웠습니다. 하지만 AI 코드 생성 도구 Bolt의 등장으로 이 시나리오는 현실이 되었습니다. 이 케이스 스터디에서는 프리랜서 개발자 김민수(가명)가 Bolt의 프롬프트 기반 스캐폴딩, Supabase 백엔드 통합, Stripe 결제 시스템을 활용하여 구독형 SaaS MVP를 48시간 만에 배포한 전체 워크플로우를 재현합니다.
프로젝트 요구사항
| 항목 | 상세 |
|---|---|
| 제품 유형 | AI 기반 이미지 리사이즈 구독 서비스 |
| 프론트엔드 | React + Tailwind CSS |
| 백엔드/DB | Supabase (Auth, PostgreSQL, Storage) |
| 결제 | Stripe Checkout (월간/연간 구독) |
| 배포 | Netlify |
| 개발 기간 | 48시간 (주말) |
Step 1: Bolt에서 프로젝트 스캐폴딩 (토요일 오전)
Bolt(bolt.new)에 접속하여 프롬프트 하나로 전체 프로젝트 구조를 생성합니다.
초기 프롬프트 작성
Build a full-stack SaaS application for image resizing with the following:
- React + Vite + Tailwind CSS frontend
- Supabase for authentication and database
- Three pricing tiers: Free, Pro ($9/mo), Enterprise ($29/mo)
- Landing page with hero section, pricing cards, and feature list
- Dashboard with file upload and resize history
- Protected routes requiring authentication
- Responsive design for mobile and desktopBolt는 이 프롬프트로부터 약 15개 파일로 구성된 완전한 프로젝트를 30초 이내에 생성합니다. 생성된 구조는 다음과 같습니다:
src/
├── components/
│ ├── Landing.jsx
│ ├── PricingCards.jsx
│ ├── Dashboard.jsx
│ ├── FileUpload.jsx
│ └── Navbar.jsx
├── lib/
│ └── supabase.js
├── hooks/
│ └── useAuth.js
├── App.jsx
└── main.jsxStep 2: Supabase 연동 설정 (토요일 오후)
Supabase 프로젝트를 생성한 후 Bolt 내에서 바로 환경변수를 설정합니다.
Supabase 클라이언트 구성
// src/lib/supabase.js import { createClient } from '@supabase/supabase-js';const supabaseUrl = import.meta.env.VITE_SUPABASE_URL || ‘https://YOUR_PROJECT.supabase.co’; const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY || ‘YOUR_API_KEY’;
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
.env 파일 설정
VITE_SUPABASE_URL=https://YOUR_PROJECT.supabase.co
VITE_SUPABASE_ANON_KEY=YOUR_API_KEY
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_YOUR_API_KEYBolt 프롬프트로 DB 스키마 생성
Bolt에 다음 프롬프트를 입력하면 Supabase SQL Editor에서 실행할 마이그레이션 코드가 생성됩니다:
Add Supabase database tables:
- profiles table linked to auth.users with subscription_tier and stripe_customer_id
- resize_jobs table with user_id, original_url, resized_url, dimensions, created_at
Add Row Level Security policies so users can only see their own data
생성된 SQL을 Supabase SQL Editor에서 실행합니다:
— profiles 테이블 create table public.profiles ( id uuid references auth.users on delete cascade primary key, email text, subscription_tier text default ‘free’, stripe_customer_id text, created_at timestamptz default now() );alter table public.profiles enable row level security; create policy “Users can view own profile” on public.profiles for select using (auth.uid() = id);
— resize_jobs 테이블 create table public.resize_jobs ( id uuid default gen_random_uuid() primary key, user_id uuid references public.profiles(id) on delete cascade, original_url text not null, resized_url text, width int, height int, status text default ‘pending’, created_at timestamptz default now() );
alter table public.resize_jobs enable row level security; create policy “Users can manage own jobs” on public.resize_jobs for all using (auth.uid() = user_id);
Step 3: Stripe Checkout 통합 (일요일 오전)
결제 시스템은 Bolt 프롬프트로 Stripe Checkout 세션 생성 로직을 만듭니다:
Add Stripe checkout integration:
- Create a checkout function that redirects to Stripe Checkout
- Support monthly and yearly billing for Pro and Enterprise tiers
Handle success and cancel redirect URLs
생성된 결제 컴포넌트 핵심 코드
// src/components/PricingCards.jsx const PRICE_IDS = { pro_monthly: ‘price_YOUR_PRICE_ID_MONTHLY’, pro_yearly: ‘price_YOUR_PRICE_ID_YEARLY’, enterprise_monthly: ‘price_YOUR_ENTERPRISE_MONTHLY’, enterprise_yearly: ‘price_YOUR_ENTERPRISE_YEARLY’, };
async function handleCheckout(priceId) { const response = await fetch(‘/api/create-checkout-session’, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ priceId, customerId: user.stripe_customer_id, successUrl: window.location.origin + ‘/dashboard?session_id={CHECKOUT_SESSION_ID}’, cancelUrl: window.location.origin + ‘/pricing’, }), }); const { url } = await response.json(); window.location.href = url; }
Supabase Edge Function으로 서버사이드 처리
// supabase/functions/create-checkout-session/index.ts import Stripe from ‘https://esm.sh/stripe@14.14.0’;const stripe = new Stripe(Deno.env.get(‘STRIPE_SECRET_KEY’)!);
Deno.serve(async (req) => { const { priceId, customerId, successUrl, cancelUrl } = await req.json();
const session = await stripe.checkout.sessions.create({ customer: customerId, line_items: [{ price: priceId, quantity: 1 }], mode: ‘subscription’, success_url: successUrl, cancel_url: cancelUrl, });
return new Response(JSON.stringify({ url: session.url }), { headers: { ‘Content-Type’: ‘application/json’ }, }); });
Edge Function 배포 명령어:
npx supabase functions deploy create-checkout-session —no-verify-jwt
npx supabase secrets set STRIPE_SECRET_KEY=sk_test_YOUR_API_KEYStep 4: 배포 (일요일 오후)
Bolt에서 코드를 GitHub에 연결한 뒤 Netlify로 배포합니다:
npm run build
npx netlify-cli deploy —prod —dir=dist최종 결과 타임라인
| 시간 | 완료 항목 | Bolt 프롬프트 수 |
|---|---|---|
| 토요일 오전 (4h) | 프로젝트 스캐폴딩 + 랜딩 페이지 | 3회 |
| 토요일 오후 (5h) | Supabase 인증 + DB 스키마 + 대시보드 | 5회 |
| 일요일 오전 (4h) | Stripe 결제 + Edge Function | 4회 |
| 일요일 오후 (3h) | 버그 수정 + 배포 + 테스트 | 3회 |
| 총 16시간 | MVP 완성 및 라이브 배포 | 15회 |
Pro Tips: Bolt 파워 유저를 위한 팁
- 프롬프트 체이닝: 한 번에 모든 것을 요청하지 말고 기능 단위로 프롬프트를 나누세요. 스캐폴딩 → 인증 → DB → 결제 순서가 가장 효율적입니다.
- 컨텍스트 유지: Bolt 채팅 내에서 “이전에 만든 supabase.js를 활용해서”처럼 기존 파일을 참조하면 일관성 있는 코드가 생성됩니다.
- 부분 수정 프롬프트: 전체를 다시 만들지 말고 “PricingCards 컴포넌트에서 연간 결제 토글 버튼을 추가해줘”처럼 구체적으로 지시하세요.
- 환경변수 분리: Bolt에서 생성된 코드의 하드코딩된 키를 반드시 .env로 분리하고 .gitignore에 추가하세요.
- Supabase 타입 자동 생성:
npx supabase gen types typescript —project-id YOUR_PROJECT_ID > src/types/database.ts로 타입 안전성을 확보하세요.
Troubleshooting: 자주 발생하는 오류
| 오류 | 원인 | 해결 방법 |
|---|---|---|
AuthApiError: Invalid API key | Supabase anon key가 잘못 설정됨 | .env 파일의 VITE_SUPABASE_ANON_KEY 값을 Supabase 대시보드 Settings > API에서 재확인 |
CORS error on Edge Function | Edge Function에 CORS 헤더 누락 | 응답에 Access-Control-Allow-Origin: 헤더를 추가하거나 supabase.functions.invoke() 사용 |
Stripe redirect 404 | success_url 경로가 SPA 라우팅과 불일치 | Netlify에 _redirects 파일 추가: / /index.html 200 |
RLS policy violation | Row Level Security가 INSERT를 차단 | profiles 테이블에 INSERT 정책 추가: create policy “Users can insert own profile” on public.profiles for insert with check (auth.uid() = id); |
| Bolt 생성 코드 빌드 실패 | 패키지 버전 충돌 | rm -rf node_modules package-lock.json && npm install 후 재빌드 |
자주 묻는 질문 (FAQ)
Q1: Bolt로 생성된 코드의 품질은 프로덕션에 사용해도 될 수준인가요?
Bolt가 생성하는 코드는 MVP 수준으로 충분히 사용 가능하지만, 프로덕션 배포 전에 반드시 보안 검토가 필요합니다. 특히 API 키 노출 여부, RLS 정책의 완전성, 입력값 검증 로직을 수동으로 확인해야 합니다. Bolt는 빠른 프로토타이핑에 최적화되어 있으므로 에러 핸들링이나 엣지 케이스 처리는 직접 보강하는 것이 좋습니다.
Q2: Supabase Edge Function 대신 별도 백엔드 서버를 사용해야 하나요?
MVP 단계에서는 Supabase Edge Function만으로 Stripe 웹훅 처리, 결제 세션 생성 등 핵심 서버 로직을 충분히 구현할 수 있습니다. 다만 복잡한 비즈니스 로직이나 장시간 실행 작업이 필요한 경우에는 별도의 Node.js 또는 Python 백엔드를 고려하세요. Edge Function의 실행 시간 제한(기본 60초)과 메모리 제한(256MB)을 확인하는 것이 중요합니다.
Q3: Bolt의 무료 플랜으로도 이 케이스 스터디를 재현할 수 있나요?
Bolt의 무료 플랜에서는 일일 토큰 사용량에 제한이 있어 15회의 프롬프트를 하루에 모두 실행하기 어려울 수 있습니다. 주말 집중 개발을 계획한다면 Pro 플랜(월 $20)을 권장합니다. 대안으로 무료 플랜에서 핵심 스캐폴딩만 생성한 후 나머지를 로컬 IDE에서 직접 작성하는 하이브리드 방식도 효과적입니다.