목표
저는 단 한 명의 고객도 확보하기 전에 서버를 구축하거나, 데이터베이스를 관리하거나, 인프라 비용을 지불할 필요 없이, LLM을 호출하고, 유료 사용자를 보유하며, 인증 기능(auth)과 글로벌 사용자층을 갖춘 실제 SaaS 제품을 출시하는 것이 목표였습니다.
그래서 저는 ReThread(하나의 기술 블로그 게시물을 7가지 플랫폼 네이티브 형식으로 변환하는 AI 도구)를 Cloudflare의 무료 티어만을 사용하여 완전히 구축했습니다. 이 글은 아키텍처, 고려 사항(tradeoffs), 그리고 저와 유사한 것을 구축하려는 분들에게 전하고 싶은 이야기입니다.
하나의 플랫폼에 담긴 전체 스택
서버도, Docker도, 별도의 API 호스팅도 없습니다. 모든 것이 하나의 Cloudflare 프로젝트 내에 존재합니다:
┌──────────────────────────────────────────────┐
│ Cloudflare Pages 프로젝트 │
│ │
│ public/ → 정적 프론트엔드 (HTML/JS) │
│ functions/ → 서버리스 API 엔드포인트 │
│ │
│ 바인딩(Bindings): │
│ AI → Workers AI (Llama 3.3 70B) │
│ WAITLIST → KV (key-value store) │
└──────────────────────────────────────────────┘
│ │
Creem (결제) 글로벌 엣지(300개 이상의 도시)
하나의 wrangler pages 배포만으로 프론트엔드와 API를 함께 전송할 수 있습니다. 이 것만으로도 일반적인 '백엔드를 연결하는' 복잡한 설정 과정 대부분을 제거했습니다.
실제 작업을 수행하는 구성 요소들
- Pages는 API 역할을 수행합니다. functions/ 내의 각 파일은 엔드포인트입니다. functions/repurpose.js는 사용자의 게시물을 받아 LLM을 호출하고 7가지 재작성된 형식을 반환하는 onRequestPost를 내보냅니다. 프레임워크도, Express도 없습니다. 단순히 라우트당 하나의 함수만 있습니다. 저에게 가장 놀라웠던 점은 "백엔드"가 정적 사이트와 함께 배포되는 파일 폴더 그 자체라는 것입니다.
- 추론을 위한 Workers AI LLM 호출은 단일 바인딩(single binding)입니다. API 키나 별도의 제공업체 계정이 필요 없습니다: const result = await env.AI.run("@cf/meta/llama-3.3-70b-instruct-fp8-fast", { messages: [/* ... */] } ); 이 코드는 Cloudflare의 엣지(edge)에 있는 GPU에서 실행되므로, São Paulo의 사용자든 Berlin의 사용자든 모두 가까운 노드를 이용하게 됩니다. 무료 티어는 낮은 볼륨에서는 충분한 일일 할당량을 제공하며 신용카드 등록이 필요 없습니다. (교체 가능: 만약 GPT-4급 품질을 원한다면, 동일한 코드 경로를 환경 변수(env var)를 통해 OpenAI/DeepSeek에 호출할 수 있습니다.)
- 전체 "데이터베이스"로서의 KV Postgres도, Redis도 없습니다. KV는 다음을 저장합니다:
- 권한 부여(Entitlements) — 누가 결제했는지, 어떤 플랜인지, 활성/환불 여부
- 세션(Sessions) — 비밀번호 없는 로그인 토큰 (7일 TTL)
- 라이선스 키(License keys) — 키 → 이메일 매핑
- 속도 제한 카운터(Rate-limit counters) — 사용자별 일일 사용량
- 페이지뷰 카운터(Page-view counters) — "이것을 사용하는 N명의 개발자"와 같은 소셜 프루프(social proof)
KV는 전역적으로 복제된 키-값 저장소입니다. 읽기는 어디서든 빠르지만, 쓰기는 최종적 일관성(eventually consistent)을 가집니다 (보통 1분 이내에 전파됨). 권한 부여나 카운터의 경우 전혀 문제가 없습니다. 저는 강한 일관성(strong consistency)이 필요한 것(재고, 원장 등)에는 사용하지 않을 것입니다.
- 비밀번호 없는 인증(Passwordless auth) + 라이선스 키 유료 티어를 잠금 해제하는 두 가지 방법:
- 라이선스 키: Creem은 구매자에게 키를 이메일로 보내고, 사용자는 이를 앱에 붙여넣습니다. 웹훅이 KV에 키 → {이메일, 플랜}을 저장합니다.
- 이메일 폴백(Email fallback): 웹훅이 이미 결제를 확인했기 때문에, 구매 이메일만으로 Pro를 직접 잠금 해제할 수 있습니다. 세션은 KV에 저장되는 무작위 UUID이며 7일 TTL을 가집니다 — 비밀번호 테이블도, bcrypt도, 재설정 흐름(reset-flow) 이메일도 없습니다.
그리고 권한(entitlement)은 모든 요청마다 재확인되므로, 환불이나 취소 시 접근이 즉시 박탈됩니다 ("아직 Pro가 7일 남았어요" 같은 허점 없음).
- 결제 웹훅(payment webhook) 처리: Creem이 구매/환불 시마다 /webhook으로 요청을 보냅니다. 저는 어떤 것도 신뢰하기 전에 HMAC-SHA256 서명을 확인하고, 이벤트 ID를 사용하여 KV에서 중복 제거(de-dupe)하여 재전송된 이벤트가 이중로 부여되는 것을 막습니다. 그런 다음 권한 상태를 변경하는 간단한 KV 쓰기 작업만 하면 됩니다.
실제 비용은 다음과 같습니다:
서비스 무료 티어 (Service Free tier): 제 트래픽 볼륨 기준 Pages + Functions 무제한 요청, 500 빌드/월 $0, Workers AI 일일 무료 할당량 $0, KV 하루 약 100k 읽기 / 1k 쓰기 $0, Creem — 실제 판매에 대한 수수료만 발생. 총합: 실제 트래픽이 발생할 때까지 월 $0입니다. 첫 청구서는 유료 고객이 생긴 후에야 나타나므로, 이는 정확히 원하는 순서입니다. (무료 티어 숫자는 변경될 수 있으니 Cloudflare의 현재 제한 사항을 확인하세요. 하지만 구조는 유지됩니다: 무료로 많은 것을 얻을 수 있습니다.)
트레이드오프 / 제가 알려드릴 것들
13. KV의 최종 일관성(eventual consistency)이 가장 큰 함정입니다 — 리전 전반에 걸쳐 읽기 후 쓰기 보장(read-after-write guarantees)이 필요한 것은 구축하지 마세요.
14. Workers에는 제한 사항이 있습니다 (CPU 시간, 서브 요청). API 엔드포인트로는 괜찮지만, 비디오 트랜스코딩을 시도해서는 안 됩니다.
15. 하나의 플랫폼 = 하나의 폭발 반경(blast radius)입니다. Cloudflare에 문제가 생기면 전체 스택이 다운됩니다. 사이드 프로젝트에는 허용 가능하지만, 미션 크리티컬한 것이라면 다양화해야 합니다.
- 개발자 경험(Developer Experience, DX)이 정말 뛰어납니다. wrangler pages를 배포하면 전 세계적으로 라이브가 됩니다. 저는 주말 저녁 시간에 이것을 완성했습니다. 제품에 관심이 있다면 ReThread → https://feijihua.top — 코드를 손상시키지 않고 하나의 기술 게시물을 7가지 형식으로 변환합니다. 무료 티어는 계정 없이 하루 10개 생성입니다. 이 제품은 부분적으로 저 자신의 필요를 충족시키고, 부분적으로는 무료 인프라 위에서 실제 수익화된 AI SaaS를 배포할 수 있음을 증명하기 위해 만들었습니다. 댓글에서 질문에 답변해 드릴 준비가 되어 있습니다 — 특히 Workers로 유사한 것을 구축하고 계신 분이라면 더욱 그렇습니다.