Cloudflare Workers, D1으로 비용 0원 서버 이용하기
사이드 프로젝트의 고정 비용 0원 서버 운영하기, Cloudflare Workers와 D1, 그리고 Hono를 선택한 이유와 Cron Triggers를 활용한 경험을 공유합니다.
2026년 1월 2일
GhostGains를 개발하면서 제일 고민되었던 건 역시 서버이었다. 금융 데이터 특성상 매일 수많은 자산 가격을 갱신해야 하고, 사용자가 들어올 때마다 과거 데이터랑 현재 수익률을 실시간으로 계산해서 보여줘야 한다. 보통 이럴 땐 EC2에 서버를 올리고 AWS RDS나 Supabase 같은 RDB를 먼저 떠올리겠지만, 선택한 건 Cloudflare workers와 D1이다. Workers와 같은 엣지 네트워크에서 도는 SQLite라 속도도 빠르고, 무엇보다 무료 티어가 넉넉해서 비용 부담이 거의 없다. 그리고 단순한 조회 수준이라 복잡한 기능이 들어간 DB는 너무 오버스펙이었다.
이번 글에서는 왜 Workers + D1을 골랐는지 정리해본다.
1. Cloudflare Workers
보통 서버라고 하면 AWS EC2 같은 가상 머신이나, 서울 리전에 박혀있는 람다(Lambda)를 떠올린다. 하지만 Cloudflare Workers는 개념이 좀 다르다.
전 세계에 퍼져있는 엣지 로케이션(Edge Location) 전체가 내 서버가 된다. 사용자가 런던에서 접속하면 런던 서버가, 서울에서 접속하면 서울 서버가 응답한다.
왜 Free Tier가 사기인가?
사이드 프로젝트 개발자에게 가장 중요한 건 역시 비용이다. Cloudflare Workers의 무료 플랜은 하루에 10만 건(Requests) 까지 무료다. 안 쓸 이유가 없다.
2. Hono
Workers 환경은 Node.js가 아니라 V8 엔진 기반의 독자적인 런타임을 쓴다. 그래서 무거운 Express.js 대신 엣지 환경에 최적화된 프레임워크가 필요했다.
그게 바로 **Hono(호노)**다.
- 가볍다: 용량이 14KB밖에 안 된다.
- 빠르다: 웹 표준(Web Standard) API를 기반으로 만들어져서 Cloudflare Workers와 찰떡궁합이다.
- 익숙하다: 라우팅 문법이 Express와 거의 똑같다
import { Hono } from 'hono';
import { cors } from 'hono/cors';
const app = new Hono();
app.use('/*', cors());
app.get('/', (c) => c.text('👻 GhostGains API is Live'));
export default app;
3. D1: DB도 엣지에 놓기
백엔드(Workers)는 전 세계에 퍼져있는데, DB가 미국에 하나만 있다면? 결국 데이터 요청시간 때문에 엣지의 장점이 다소 사라진다.
Cloudflare D1은 엣지 네이티브 분산형 SQLite 데이터베이스다.
왜 D1인가?
- workers와 같은 네트워크: D1은 Workers 내부 프로토콜(HTTP 기반)로 통신하기 때문에 연결 지연이 '0'에 가깝다.
- 표준 SQL 지원: 익숙한
SELECT * FROM쿼리를 그대로 쓰면 된다.
Hono와의 연동도 정말 심플하다. wrangler.toml에 DB만 연결해 주면, 코드에서는 환경변수처럼 꺼내 쓸 수 있다.
type Bindings = {
DB: D1Database; // 바인딩된 DB가 여기로 들어옴
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/assets', async (c) => {
// ORM 없이 생 쿼리(Raw SQL)를 날려도 안전하고 빠르다
const { results } = await c.env.DB.prepare("SELECT * FROM Assets").all();
return c.json(results);
});
4. Cron Triggers: "서버 없이 돌아가는 스케줄러"
GhostGains는 매일 주식 시장이 마감되면 새로운 가격 데이터를 긁어와야(Crawling) 한다.
예전 같았으면 EC2 하나 띄워놓고 리눅스 crontab을 돌리거나, AWS EventBridge를 세팅해야 했을 거다.
Cloudflare Workers는 Cron Triggers라는 기능을 기본으로 제공한다.
설정 파일(wrangler.toml)에 시간만 적어주면 끝이다.
# wrangler.toml
[triggers]
crons = ["0 0 * * *"] # 매일 자정에 실행
코드에서는 scheduled 함수만 구현하면 된다.
export default {
fetch: app.fetch, // 일반 API 요청 처리
// ⏰ 크론잡이 실행될 때 여기로 들어옴
async scheduled(event, env, ctx) {
console.log("주식 데이터 업데이트 시작...");
ctx.waitUntil(runDailyUpdate(env));
}
};
서버를 24시간 켜놓을 필요도 없고, 스케줄링을 위해 별도의 인프라를 구축할 필요도 없다. 그냥 함수 하나 추가했을 뿐인데 매일 데이터를 최신화해준다.
마무리
- 비용: 프리티어 이용으로 0원.
- 성능: 글로벌 엣지 배포로 로컬 수준의 응답 속도.
- 편의성: DB 관리, 크론잡, API 서버가 하나의 코드베이스에서 관리됨.
물론 D1이 RDB처럼 복잡한 트랜잭션 처리에 완벽하진 않지만, 조회 위주의 서비스라면 차고 넘치는 성능을 보여준다.