콘텐츠로 이동

사용자에게 전달하기

이 장은 본인 컴퓨터에서 만든 SaaS 를 다른 사람의 브라우저에서 동작하게 옮기는 이야기입니다. 한두 명에게 베타로 보여 주는 정도부터 결제까지 받는 운영 환경까지 범위가 넓습니다. 처음부터 어디까지 갈지 정해 두고, 본인에게 맞는 절을 따라가세요.

시작 전에 한 가지 짚어 둡니다. base 가 자동으로 해 주는 것은 빌드(pnpm build)와 Edge Function 배포(supabase functions deploy)까지입니다. 그 산출물을 어느 호스팅에 올릴지, custom domain 을 어떻게 연결할지, Stripe 운영 환경 webhook endpoint 를 어디로 정할지는 본인이 직접 결정하고 직접 눌러야 합니다. 도구가 거기까지 관여하면 본인의 운영 환경 키나 결제 계정을 다루게 되어 안전하지 않기 때문입니다.

단계누가어떻게
Vite SPA 빌드base 가 자동으로 합니다pnpm build 또는 deploy-saas 스킬을 씁니다
Edge Function 배포base 가 자동으로 합니다supabase functions deploy <name> 을 실행합니다
호스팅에 올리기본인이 합니다Vercel, Netlify, Cloudflare Pages, self-host 중 하나를 고릅니다
Stripe 운영 환경 webhook 등록본인이 합니다Stripe 대시보드에서 endpoint URL 을 등록합니다
Supabase 운영 환경 프로젝트 분리본인이 합니다Supabase 대시보드에서 새 프로젝트를 만들고 키를 분리합니다

아래 네 절 중 본인 상황에 맞는 하나만 골라 그 단계대로 따라가시면 됩니다.

경로어울리는 쓰임미리 갖춰야 할 것git push 자동 deploy
Vercel가장 손이 덜 갑니다Vercel 계정이 필요합니다있습니다
NetlifyVercel 대신 쓸 수 있습니다Netlify 계정이 필요합니다있습니다
Cloudflare PagesCloudflare 인프라와 통합합니다Cloudflare 계정이 필요합니다있습니다
self-host사내 정책상 외부 호스팅이 불가할 때 씁니다정적 호스팅이 가능한 서버가 필요합니다수동으로 하거나 CI 를 씁니다

처음이라면 Vercel 이 거의 항상 충분합니다. git push 한 번으로 preview deploy 가 만들어지고, main 브랜치 push 가 운영 환경으로 갑니다. custom domain 과 SSL 도 자동으로 처리됩니다.

deploy-saas 스킬이 끝나면 docs/deployment/DEPLOYMENT.md 파일이 새로 생깁니다. 이 장은 그 파일을 더 길게 풀어 쓴 안내서입니다.

0) Supabase 운영 환경 프로젝트 분리

섹션 제목: “0) Supabase 운영 환경 프로젝트 분리”

어느 호스팅을 고르든 먼저 결정해야 합니다. 지금까지 작업한 Supabase 프로젝트는 staging 으로 두고, 운영 환경용으로 새 프로젝트를 하나 더 만듭니다.

Supabase 조직
├── my-saas-production ← 실사용자와 Stripe live key 를 씁니다
└── my-saas-staging ← 개발자 테스트와 Stripe test key 를 씁니다

같은 프로젝트에서 행 수준 접근 정책을 한 줄 잘못 쓴 실수가 실사용자 데이터에 닿는 것을 막아 주는 분리입니다. 각 프로젝트가 자기 SUPABASE_URL, ANON_KEY, SERVICE_ROLE_KEY 를 가지고 있고, 호스팅의 운영 환경 env 와 preview env 가 서로 다른 프로젝트를 가리키게 설정합니다.

마이그레이션은 같은 SQL 을 양쪽에 적용합니다.

Terminal window
supabase link --project-ref staging-ref
supabase db push # staging 에 먼저 적용합니다
# 확인한 다음에
supabase link --project-ref production-ref
supabase db push # 운영 환경에 적용합니다

같은 마이그레이션이 두 환경에 같은 모양으로 들어가야 환경 사이에 차이가 생기지 않습니다.

가장 단순한 방법이고, 보통 시작점으로 정답에 가깝습니다.

Terminal window
npm i -g vercel # CLI 는 한 번만 설치하면 됩니다
vercel link # 프로젝트를 연결합니다
vercel env pull .env.local # 원격 env 를 로컬로 받아 옵니다 (선택 사항입니다)
vercel deploy --prod # 운영 환경으로 배포합니다

vercel link 를 한 번 실행한 다음에는 git push 로 preview deploy 가 자동으로 만들어집니다. 운영 환경 배포는 main 브랜치 push 또는 vercel deploy --prod 로 직접 호출합니다.

env 주입은 대시보드의 Settings → Environment Variables 에서 합니다. CLI 로도 됩니다.

Terminal window
vercel env add VITE_SUPABASE_URL production
vercel env add VITE_SUPABASE_ANON_KEY production
vercel env add VITE_STRIPE_PUBLISHABLE_KEY production
vercel env add VITE_API_MODE production # edge-functions 값을 넣습니다

SUPABASE_SERVICE_ROLE_KEY 와 STRIPE_SECRET_KEY 는 여기에 넣지 않습니다. 그 둘은 Edge Function 쪽에 supabase secrets set 으로 등록합니다. 호스팅 env 에 넣으면 SPA 번들로 노출되지는 않더라도 build 시점에 노출될 위험이 생깁니다.

custom domain 은 대시보드의 Domains 메뉴에서 설정합니다. DNS A 또는 CNAME 레코드를 한 줄 넣으면 되고, Vercel 이 SSL 인증서를 자동으로 발급합니다.

Terminal window
npm i -g netlify-cli
netlify link
netlify env:import .env.production
netlify deploy --prod

netlify env:import 가 .env.production 파일을 통째로 읽어 들입니다. .env.production 은 운영 환경 키만 들어 있는 파일이고 .gitignore 에 있어야 합니다. 가져온 다음에는 그 파일을 지우는 편이 안전합니다.

build 명령은 netlify.toml 에 적습니다.

[build]
command = "pnpm build"
publish = "dist"

custom domain 과 SSL 은 Vercel 과 같은 흐름입니다.

Terminal window
npm i -g wrangler
wrangler login
wrangler pages deploy dist --project-name=my-saas

Cloudflare Pages 는 git push 로 자동 deploy 를 실행하거나 wrangler pages deploy dist 로 직접 올립니다. 두 방법 모두 잘 동작합니다.

env 주입은 대시보드에서 하거나 wrangler 로 합니다.

Terminal window
wrangler pages secret put VITE_SUPABASE_URL --project-name=my-saas
wrangler pages secret put VITE_SUPABASE_ANON_KEY --project-name=my-saas
wrangler pages secret put VITE_STRIPE_PUBLISHABLE_KEY --project-name=my-saas

custom domain 은 대시보드의 Custom domains 에서 설정합니다. Cloudflare 가 DNS 도 같이 관리하면 한곳에서 끝나서 편합니다.

회사 정책상 외부 호스팅이 불가하거나, 자체 인프라 위에 올리고 싶을 때 고릅니다.

Terminal window
pnpm build
# dist/ 를 본인 서버의 nginx 또는 s3 static hosting 으로 옮깁니다

dist/ 는 정적 파일 묶음입니다. index.html 과 vendor chunk, route 별 chunk, CSS 번들이 들어 있습니다. SPA 라 진입점이 하나여서 nginx 에 try_files $uri /index.html 설정만 넣어 두면 됩니다.

env 변수는 build 시점에 번들에 포함되기 때문에, 환경별로 다른 변수를 쓰려면 환경마다 따로 빌드합니다. 또는 build 시점에 환경변수를 외부에서 주입하는 CI 파이프라인을 만듭니다.

호스팅에 올라간 다음 webhook 이 동작하려면 Stripe 대시보드에서 endpoint 를 새로 등록해야 합니다. 로컬 개발에서 쓰는 stripe listen 과는 다릅니다.

  1. Stripe 대시보드 왼쪽 메뉴의 Developers → Webhooks 로 갑니다.
  2. 오른쪽 위의 Test mode 토글이 운영 환경 쪽으로 가 있는지 확인합니다 (운영 환경 배포라면 그렇습니다). test mode 에서 등록한 endpoint 는 운영 환경 결제 이벤트를 받지 못합니다.
  3. Add endpoint 를 누르고 endpoint URL 을 입력합니다.
  4. URL 은 Edge Function 의 공개 URL 입니다. https://<project-ref>.supabase.co/functions/v1/stripe-webhook 형태입니다.
  5. Listen to 항목에서 본인 SaaS 가 받을 이벤트를 고릅니다. 보통 checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.paid 정도를 고릅니다.
  6. 등록이 끝나면 endpoint 의 Signing secret (whsec_...) 을 복사합니다.
  7. 그 secret 을 운영 환경 Supabase 프로젝트에 등록합니다.
Terminal window
supabase link --project-ref production-ref
supabase secrets set STRIPE_WEBHOOK_SECRET=whsec_...

webhook 이 정상으로 등록됐는지는 Stripe 대시보드의 endpoint 페이지에서 Send test webhook 을 눌러 확인합니다. Edge Function 로그에 검증 통과 줄이 출력되면 정상입니다.

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 할 때 채워집니다.

운영 환경에서 잘못되면 거의 되돌리기 어려운 값들입니다. 첫날부터 가장 먼저 챙기세요.

  • STRIPE_SECRET_KEY (sk_live_...) 는 Stripe 대시보드에서 교체할 수 있습니다. 노출되면 즉시 교체합니다.
  • SUPABASE_SERVICE_ROLE_KEY 는 Supabase 대시보드에서 교체할 수 있습니다. 교체하면 모든 클라이언트 연결이 끊깁니다.
  • 위 두 값은 supabase secrets set 으로만 넣습니다. .env* 파일에 들어가면 안 됩니다.
  • STRIPE_WEBHOOK_SECRET 은 endpoint 마다 다릅니다. test mode 와 운영 환경이 서로 다른 값입니다.

.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 도 옆에 두고 같이 보세요.