사용자에게 전달하기
이 장은 본인 컴퓨터에서 만든 SaaS 를 다른 사람의 브라우저에서 동작하게 옮기는 이야기입니다. 한두 명에게 베타로 보여 주는 정도부터, 결제까지 받는 production 까지 범위가 다양합니다. 처음부터 어디까지 갈지 정해 두고, 본인에게 맞는 절을 따라가세요.
시작 전에 한 가지 짚어 둡니다. base 가 자동으로 해 주는 것은 빌드(pnpm build)와 Edge Function 배포(supabase functions deploy)까지입니다. 그 산출물을 어느 호스팅에 올릴지, custom domain 을 어떻게 잡을지, Stripe production webhook endpoint 를 어디로 박을지는 본인이 직접 결정하고 직접 누르는 자리입니다. 도구가 그 자리까지 끼어들면 본인의 production 키나 결제 계정을 다루게 되어 안전하지 않기 때문입니다.
| 단계 | 누가 | 어떻게 |
|---|---|---|
| Vite SPA 빌드 | base 자동 | pnpm build 또는 deploy-saas 스킬 |
| Edge Function 배포 | base 자동 | supabase functions deploy <name> |
| 호스팅에 올리기 | 본인 | Vercel, Netlify, Cloudflare Pages, self-host 중 하나 |
| Stripe production webhook 등록 | 본인 | Stripe 대시보드에서 endpoint URL 박기 |
| Supabase production 프로젝트 분리 | 본인 | Supabase 대시보드에서 새 프로젝트 + 키 분리 |
아래 네 절 중 본인 상황에 맞는 하나만 골라 그 단계대로 따라가시면 됩니다.
네 가지 배포 경로
섹션 제목: “네 가지 배포 경로”| 경로 | 어울리는 자리 | 미리 갖춰야 할 것 | git push 자동 deploy |
|---|---|---|---|
| Vercel | 가장 손이 덜 가는 자리 | Vercel 계정 | 있음 |
| Netlify | Vercel 대안 | Netlify 계정 | 있음 |
| Cloudflare Pages | Cloudflare 인프라 통합 | Cloudflare 계정 | 있음 |
| self-host | 사내 정책상 외부 호스팅 불가 | 정적 호스팅 가능한 서버 | 수동 또는 CI |
처음이라면 Vercel 이 거의 항상 충분합니다. git push 한 번으로 preview deploy 가 떨어지고, main 브랜치 push 가 production 으로 갑니다. custom domain 과 SSL 도 자동입니다.
deploy-saas 스킬이 끝나면 docs/deployment/DEPLOYMENT.md 파일이 새로 생깁니다. 이 장은 그 파일의 긴 호흡 동반자입니다.
0) Supabase production 프로젝트 분리
섹션 제목: “0) Supabase production 프로젝트 분리”어느 호스팅을 고르든 먼저 결정할 자리입니다. 지금까지 작업한 Supabase 프로젝트는 staging 자리에 두고, production 용으로 새 프로젝트를 하나 더 만듭니다.
Supabase 조직├── my-saas-production ← 실 사용자, 실 Stripe live key└── my-saas-staging ← 개발자 테스트, Stripe test key같은 프로젝트에서 RLS 정책 한 줄 잘못 박힌 실수가 실사용자 데이터에 닿는 자리를 막아 주는 분리입니다. 각 프로젝트가 자기 SUPABASE_URL, ANON_KEY, SERVICE_ROLE_KEY 를 들고 있고, 호스팅의 production env 와 preview env 가 다른 프로젝트를 가리키게 잡습니다.
마이그레이션은 같은 SQL 을 양쪽에 적용합니다.
supabase link --project-ref staging-refsupabase db push # staging 에 먼저# 확인 후supabase link --project-ref production-refsupabase db push # production 에같은 마이그레이션이 두 환경에 같은 모양으로 들어가야 drift 가 새지 않습니다.
1) Vercel
섹션 제목: “1) Vercel”가장 단순한 길이고, 보통 시작점으로 정답에 가깝습니다.
npm i -g vercel # CLI 한 번만 깔면 됨vercel link # 프로젝트 연결vercel env pull .env.local # 원격 env 를 로컬로 (선택)vercel deploy --prod # production deployvercel link 가 한 번 잡힌 다음에는 git push 로 preview deploy 가 자동으로 떨어집니다. production deploy 는 main 브랜치 push 또는 vercel deploy --prod 로 명시 호출합니다.
env 주입은 대시보드의 Settings → Environment Variables 자리. CLI 로도 됩니다.
vercel env add VITE_SUPABASE_URL productionvercel env add VITE_SUPABASE_ANON_KEY productionvercel env add VITE_STRIPE_PUBLISHABLE_KEY productionvercel env add VITE_API_MODE production # edge-functionsSUPABASE_SERVICE_ROLE_KEY 와 STRIPE_SECRET_KEY 는 여기에 박지 않습니다. 그 둘은 Edge Function 쪽에 supabase secrets set 으로 박힙니다. 호스팅 env 에 박으면 SPA 번들에 새지는 않더라도 build 시점에 노출 위험이 생깁니다.
custom domain 은 대시보드의 Domains 자리. DNS A 또는 CNAME 레코드 한 줄 박는 자리고, Vercel 이 SSL 인증서를 자동 발급합니다.
2) Netlify
섹션 제목: “2) Netlify”npm i -g netlify-clinetlify linknetlify env:import .env.productionnetlify deploy --prodnetlify env:import 가 .env.production 파일을 통째로 흡수합니다. .env.production 은 production 키만 들어있는 파일이고 .gitignore 에 있어야 합니다. import 후 그 파일은 지우는 편이 안전합니다.
build 명령은 netlify.toml 에 박힙니다.
[build] command = "pnpm build" publish = "dist"custom domain 과 SSL 은 Vercel 과 동일한 흐름입니다.
3) Cloudflare Pages
섹션 제목: “3) Cloudflare Pages”npm i -g wranglerwrangler loginwrangler pages deploy dist --project-name=my-saasCloudflare Pages 는 git push 로 자동 deploy 를 트리거하거나 wrangler pages deploy dist 로 수동 push 합니다. 두 갈래 다 잘 동작합니다.
env 주입은 대시보드 또는 wrangler.
wrangler pages secret put VITE_SUPABASE_URL --project-name=my-saaswrangler pages secret put VITE_SUPABASE_ANON_KEY --project-name=my-saaswrangler pages secret put VITE_STRIPE_PUBLISHABLE_KEY --project-name=my-saascustom domain 은 대시보드의 Custom domains 자리. Cloudflare 가 DNS 도 같이 관리하면 한 자리에서 끝나서 편합니다.
4) self-host
섹션 제목: “4) self-host”회사 정책상 외부 호스팅이 불가하거나, 자체 인프라 위에 올리고 싶을 때입니다.
pnpm build# dist/ 를 본인 서버의 nginx 또는 s3 static hosting 자리로 옮김dist/ 는 정적 파일 묶음입니다. index.html, vendor chunk, route 별 chunk, CSS 번들. SPA 라 entry point 가 하나여서 nginx 의 try_files $uri /index.html 설정만 박혀 있으면 됩니다.
env 변수는 build 시점에 번들에 박혀 있어서, 환경별로 다른 변수를 쓰려면 환경마다 따로 빌드합니다. 또는 build 시점에 환경변수를 외부에서 주입하는 CI 파이프라인을 짭니다.
Stripe production webhook 등록
섹션 제목: “Stripe production webhook 등록”호스팅에 올라간 자리에서 webhook 이 동작하려면 Stripe 대시보드에서 endpoint 를 새로 등록해야 합니다. 로컬 개발의 stripe listen 과는 다른 자리입니다.
- Stripe 대시보드 좌측 메뉴의 Developers → Webhooks 로 갑니다.
- 우측 상단의 Test mode 토글이 production 으로 가 있는지 확인합니다 (production 배포라면). test mode 에서 등록한 endpoint 는 production 결제 이벤트를 못 받습니다.
- Add endpoint 를 누르고 endpoint URL 을 박습니다.
- URL 은 Edge Function 의 public URL 입니다.
https://<project-ref>.supabase.co/functions/v1/stripe-webhook형태. - Listen to 자리에서 본인 SaaS 가 받을 이벤트를 고릅니다. 보통
checkout.session.completed,customer.subscription.updated,customer.subscription.deleted,invoice.paid정도. - 등록이 끝나면 endpoint 의 Signing secret (
whsec_...) 을 복사합니다. - 그 secret 을 production Supabase 프로젝트에 박습니다.
supabase link --project-ref production-refsupabase secrets set STRIPE_WEBHOOK_SECRET=whsec_...webhook 이 정상으로 등록됐는지는 Stripe 대시보드의 endpoint 페이지에서 Send test webhook 을 눌러 확인합니다. Edge Function 로그에 검증 통과 줄이 떨어지면 정상입니다.
CSP connect-src
섹션 제목: “CSP connect-src”SaaS 가 외부 서비스에 닿는 자리(Supabase, Stripe, 분석 도구 등)는 Content Security Policy 의 connect-src 에 명시되어야 합니다. base 의 index.html 에 기본 골격이 박혀 있습니다.
<meta http-equiv="Content-Security-Policy" content=" default-src 'self'; connect-src 'self' https://*.supabase.co https://api.stripe.com; script-src 'self' https://js.stripe.com;">본인 SaaS 가 추가로 닿는 외부 도메인이 있다면 (예: 분석 도구, CDN) 그 자리에 붙입니다. 이 자리는 fork 시 채워집니다.
키 관리
섹션 제목: “키 관리”production 환경에서 잘못되면 거의 회복 불가에 가까운 자리들입니다. 첫날부터 1순위로 다루세요.
STRIPE_SECRET_KEY(sk_live_...) — Stripe 대시보드에서 회전 가능. 노출되면 즉시 회전SUPABASE_SERVICE_ROLE_KEY— Supabase 대시보드에서 회전 가능. 회전하면 모든 클라이언트가 끊김- 위 두 자리는
supabase secrets set으로만 박힙니다..env*파일에 들어가면 안 됩니다. STRIPE_WEBHOOK_SECRET은 endpoint 별로 다릅니다. test mode 와 production 자리가 다른 값입니다.
.env* 파일을 git 에 커밋하지 마세요. .env.example 만 git 에 들어갑니다. 시작 전에 .gitignore 에 .env* 가 들어있는지 확인하세요.
판매 전에 한 번 더 짚을 것
섹션 제목: “판매 전에 한 번 더 짚을 것”이 base 는 goldtagworks 로부터 라이선스를 받은 상업 템플릿입니다. 그 위에서 본인이 만든 SaaS 는 본인이 정한 라이선스 조건으로 자유롭게 빌드하고 판매할 수 있습니다. 본인 사용자는 goldtagworks 와는 아무 관계도 가지지 않습니다. 사용자에게 보이는 것은 본인의 SaaS 뿐입니다.
다만 base 자체의 사용에 두 가지 제약이 있습니다.
saas-base의 소스 코드나 이 매뉴얼을 누군가에게 재배포하지 마세요. base 위에서 만든 본인의 SaaS 만 배포합니다.- 본인 작업 사본에는
LICENSE와COMMERCIAL-LICENSE.md를 그대로 둡니다. 빌드 산출물dist/안에까지 포함시킬 필요는 없습니다. 사용자에게 가는 것은 본인 SaaS 이지 base 가 아닙니다.
본인 SaaS 의 배포 라이선스는 본인이 정합니다. SaaS 운영에서는 보통 약관(Terms of Service) 과 개인정보 처리방침(Privacy Policy) 두 자리가 더 큽니다. 결제를 받기 전까지는 이 두 자리도 마무리되어야 합니다. base 가 들고 있는 LICENSE 와 COMMERCIAL-LICENSE.md 는 base 의 사용 조건이지 본인 SaaS 의 약관이 아닙니다.
여기까지가 base 가 가정하는 모든 배포 경로입니다. 흐름이 한 번 익숙해졌다면 LICENSE 와 package.json 이 본인 SaaS 정보로 바뀌어 있는지, .env* 파일이 git 에 안 들어갔는지 한 번 더 확인합니다.
배포 도중 막히는 자리가 있다면 09-troubleshooting.md 에서 시작하고, 자동 생성된 docs/deployment/DEPLOYMENT.md 도 옆에 두고 같이 보세요.