생각보다 많은 사람이 로컬에서 개발하고 실제 배포까지 하는 것에 어려움을 느낌을 알게되어 이 글을 적게 되었다. AI가 개발을 해주는 시대에 배포를 못해서 출시를 못했다와 같은 경험은 하지 않길 바라며, 특히 AWS 요금 폭탄… 아무튼 여러 사람들이 도움되길 바라며 작성했다.
선택 기준
도구를 고를 때는 다섯 가지를 확인한다. 출시 속도 측면에서 첫 사용자를 빨리 만날 수 있는지, 운영 부담 면에서 장애 대응과 백업과 패치를 누가 책임지는지 따져본다. 비용 구조에서는 첫해 할인보다 갱신 가격과 usage 과금 단위를 살피고, 이동 가능성에서는 PostgreSQL·S3 API·Docker처럼 표준 인터페이스인지 확인한다. 마지막으로 실제 병목이 생기기도 전에 복잡한 인프라를 들여오고 있지 않은지 점검한다.
관리형 서비스 + PostgreSQL + S3 호환 스토리지 + Docker 가능한 앱 구조로 시작하고, 측정된 병목이 생길 때만 분리한다.
배포
범용 MVP의 기본값은 Railway다. App, API, worker, cron, PostgreSQL까지 한 프로젝트에서 관리되니 도구를 옮겨 다닐 필요가 없다.
Next.js 제품이라면 Vercel이 기본값이다. Preview Deployment 덕분에 PR마다 결과물을 바로 볼 수 있고 Functions까지 있어서 프론트 전용이라고 보기 어렵다. 다만 지속 실행 워커나 장시간 작업은 여기 두지 않는다.
Render는 Web Service, Background Worker, Cron, Managed Postgres가 명확히 나뉜 전통적인 PaaS다. 좋지만 서비스 개수만큼 비용이 붙는 구조라 미리 따져봐야 한다. Fly.io는 Docker 머신과 리전 배치를 직접 제어해야 할 때 쓰는데, 멀티 리전이 실제 요구사항으로 확인된 경우에만 선택한다. 정적 사이트나 엣지 API 중심이라면 Cloudflare Workers로 간다.
서버 비용을 줄이고 싶으면 OpenShip를 써보는 것도 나쁘지 않을 수 있다. 하지만 Vercel 개발자가 만든 오픈소스로 아직은 생태계가 성숙해질 때까지 기다리는 것이 좋을 것 같다.
어느 쪽이든 마찬가지지만, 패치·보안·백업 책임은 다시 나에게 돌아온다. 거의 무료 Railway가 아니라 운영비를 현금 대신 내 시간으로 지불하는 선택이다.
데이터베이스와 스토리지
DB는 특별한 이유가 없으면 PostgreSQL부터 시작한다. Railway·Render 내부 DB로 시작해도 되고, branching이나 scale-to-zero가 필요하면 Neon, Postgres와 Auth·Storage·Realtime을 한 번에 쓰고 싶으면 Supabase다. Supabase는 빠르지만 기능을 많이 쓸수록 Supabase의 권한·런타임 모델에 결합된다는 걸 감안해야 한다. Cloudflare D1이나 Turso는 더 싸 보여서 쓰는 게 아니라 Workers 결합, tenant별 DB, local-first 같은 명확한 요구가 있을 때 선택한다.
스토리지는 셋을 구분하는 것부터 시작한다. Database는 관계와 트랜잭션, Object Storage는 이미지·PDF·첨부파일, Persistent Volume은 특정 서버의 로컬 파일이다.
Object Storage의 기본값은 Cloudflare R2로 본다. egress 요금이 없어서 공개 미디어 트래픽에 강하다. Supabase를 이미 쓰고 있으면 Supabase Storage 같은 대안이 있다.
사용자 업로드를 앱 서버 로컬 디스크에 저장하지 않는다. presigned URL로 Object Storage에 직접 업로드하고, DB에는 key·소유자·상태·크기만 저장한다.
도메인, DNS, 인증
도메인은 Cloudflare Registrar, Namecheap 등에서 사고, DNS도 Cloudflare로 두고 관리한다.
proxy를 켰다고 HTML·JSON이 자동으로 캐시되진 않는다. Cache-Control과 Cache Rules는 직접 설계해야 한다.
인증은 데이터 소유권이 중요하면 Better Auth, 로그인 UI를 가장 빨리 붙이고 싶으면 Clerk다. Supabase를 이미 쓰고 있다면 Supabase Auth로 RLS와 묶는 편이 자연스럽다.
인증과 인가는 다르다. 로그인 성공 여부와 별개로 workspace·role·ownership은 서버에서 검증한다.
하지만 인증은 최대한 없는 것이 마음 편하다…
결제
결제 흐름을 직접 통제하려면 Stripe, 세금 처리까지 맡기고 싶으면 Polar나 Paddle 같은 Merchant of Record, 국내 원화 결제가 중심이면 Toss Payments다.
요금제가 단일 정액이면 Stripe만으로 충분하다. credit이나 usage 과금처럼 로직이 복잡해질 때 Stripe 위에 entitlement layer(Autumn 등)를 얹는다.
결제가 성공했다고 해서 같은 요청에서 곧바로 제품 권한을 부여하지 않는다. 서명을 검증한 webhook으로 권한을 확정하고, 중복 이벤트를 막는 idempotency key와 결제 실패 시 유예 기간(grace period)까지 설계해야 한다. 첫 결제 전에 꼭 확인하자 🥹
Polar 의 경우에는 Polar Sandbox가 존재하니 로컬에서는 이것으로 테스트해보자.
이메일, 큐, 관측
이메일은 Resend로 시작하고 bounce 추적이 중요해지면 Postmark로 옮긴다. 인증 메일과 뉴스레터는 처음부터 발신 서브도메인을 분리해 관리한다.
AI 생성, 파일 변환, webhook 재처리 같은 작업은 HTTP 요청 안에서 끝내려 하지 않는다. 가볍게 시작하려면 Trigger.dev나 Inngest, QStash 같은 관리형 큐로 출발하면 된다. 워크플로우가 길고 상태 보존·재시도·human approval이 진짜 요구사항이 된 시점에는 Temporal을 검토한다. durable execution의 사실상 표준이라 step별 재시도, suspend/resume, 버저닝이 잘 잡혀 있다. 다만 self-host 운영 부담이 크므로 솔로 파운더는 Temporal Cloud부터 보는 편이 맞다. 어느 쪽이든 job handler는 멱등성을 보장해야 한다.
분석 도구는 Amplitude(분석 깊이) 또는 PostHog(올인원) 중 하나만 고른다. 이벤트 이름은 button_clicked가 아니라 generation_started, content_published처럼 사용자 의도를 나타낸다. 에러는 Sentry를 추천한다. 여러 서비스 로그를 trace 단위로 함께 뒤져야 하는 순간이 왔을 때 Axiom 같은 로그 플랫폼을 추가한다.
UI와 아이콘
React·Tailwind면 shadcn/ui + icones를 적극 사용한다. icones에서 컴포넌트로 바로 사용이 가능하고 shadcn의 경우도 유명한 만큼 추천한다.
AI 에이전트 개발
에이전트 제품은 모델 API 호출 하나로 끝나지 않는다.
runtime부터 보면, 짧은 요청·응답은 provider SDK나 Vercel AI SDK로 충분하다. 상태 보존·승인·재개가 필요해지면 LangGraph를 붙인다. business workflow가 길어지면 agent loop와 분리해서 Temporal 같은 durable workflow 엔진에 넘긴다.
Gateway는 “언젠가 모델을 바꿀 수도 있어서” 추가하는 게 아니다. multi-provider, 중앙 budget, fallback이 실제로 필요할 때 붙인다. tracing과 evaluation 도구는 Langfuse 또는 LangSmith 중 하나를 추천한다. Sentry를 대체하지 않는다. Sentry는 exception을 보고, 이쪽은 prompt·tool path·token cost를 본다.
AI가 만든 코드나 shell command를 실행해야 한다면 production host에서 직접 돌리지 않는다. Vercel Sandbox나 E2B로 격리한다. MCP는 tool interface 표준이지 security boundary가 아니라서, 최소 권한과 timeout, audit log는 별도로 둔다.
billing에서는 Gateway의 token cost와 고객 과금을 구분해야 한다. 전자는 내 원가를 관측하기 위한 지표이고, 후자는 고객에게 허용할 사용량과 과금 상태다. 그래서 요청을 처리하기 전에 권한을 확인하고, 과금 대상이 되는 결과가 성공한 뒤에 사용량을 기록한다. streaming 중단이나 retry에도 이중 차감되지 않도록 usage event에 idempotency key를 둔다.
추천 조합
A. 가장 빨리 출시하는 Next.js SaaS
Vercel + Neon Postgres + Better Auth/Clerk + Cloudflare R2 + Trigger.dev + Resend + Stripe + PostHog + Sentry + shadcn/ui
B. 벤더 수를 줄이는 범용 SaaS
Railway(App/API/Worker/Cron/Postgres/Bucket) + Better Auth + Resend + PostHog + Sentry
C. Supabase 중심
Supabase(DB/Auth/Storage/Realtime) + Vercel 프론트 + Inngest
D. 비용 절감 Engineer Mode
OpenShip self-host on VPS + R2 + Cloudflare DNS. 다만 외부 백업, restore rehearsal, 서버 모니터링까지 감당할 준비가 되었을 때만 선택한다.
도입 순서
출시 전에는 배포, PostgreSQL, 인증, DNS, 트랜잭션 이메일, Sentry를 준비하고 백업 복원 절차까지 실제로 확인한다. 첫 결제 전에는 webhook 서명 검증, idempotency, 환불 처리, 약관을 점검한다. 사용자가 생긴 뒤에 분석 도구와 rate limiting을 추가하고, 나머지는 실제 운영 고통을 겪은 뒤에 붙인다.
마이그레이션 트리거도 미리 적어둔다.
- 플랫폼 cron → Trigger.dev/Inngest: 재시도·동시성 제어·단계별 상태가 필요할 때
- Trigger.dev/Inngest → Temporal: 워크플로우가 길어지고 versioning·human-in-the-loop가 실제 요구가 됐을 때
- 통합 DB → 독립 DB: DB 수명주기와 앱 배포를 분리해야 할 때
- Postgres search → 별도 검색 엔진: relevance와 오타 허용 한계를 실제로 측정했을 때
처음부터 하지 말 것
첫날 Kubernetes, 직접 만든 비밀번호 인증, 외부 백업 없는 self-hosted DB, Postgres로 검증하기 전 Elasticsearch, 필요성이 없는 Redis, 웹 요청 안에서 돌리는 장시간 AI 작업. 그리고 ~~“백업 있음”~~이라고 확인만 해 두고 restore를 단 한 번도 실행하지 않은 상태로 두는 일도 마찬가지다.
결론
처음에는 가장 싼 스택이 아니라, 장애 표면적과 운영 대상이 가장 작은 스택을 고른다.
무료 티어 조합으로 절약한 현금보다, 도구를 오가며 잃은 시간이 컸다. 표준 인터페이스(PostgreSQL, S3 API, Docker)만 지키면 규모가 커졌을 때 언제든 옮길 수 있다. 그리고 옮겨야 할 시점이 되면 이미 그만한 가치가 생겼을 것이다.
August 25, 2026