
Cloudflare에서 free-tier 서비스를 많이 제공해줘서 개인적인 사이드 프로젝트에 많이 사용하는 편이다.
기존에 Pages Workers 서비스들이 무료였는데 이제는 d1 Durable Objects 서비스도 무료로 제공해줘서 한번 사용해 보았다.
https://blog.cloudflare.com/building-ai-agents-with-mcp-authn-authz-and-durable-objects/
기존에 Workers 에서는 stateless한 서비스만 개발할 수 있었다.
하지만 Durable Objects 에서는 sqlite 저장소에 접근해서 stateful한 서비스를 쉽게 개발할 수 있다.
또한 WebSocket 기능도 간단하게 구현할 수 있다.
예시로 채팅 서비스를 Durable Objects 에서 구현한다면?
sqlite에 저장하는 서비스를 쉽게 만들 수 있다!
sqlite 사용하기export class MyDurableObject extends DurableObject {
constructor(ctx, env) {
super(ctx, env);
this.sql = ctx.storage.sql;
this.sql.exec(`
CREATE TABLE IF NOT EXISTS chat (
id INTEGER PRIMARY KEY AUTOINCREMENT,
body TEXT NOT NULL,
author TEXT NOT NULL,
created_at DEFAULT CURRENT_TIMESTAMP
);
`); // 테이블 생성하기
}
async getChats() {
const chats = this.sql.exec('SELECT * FROM chat').toArray();
return chats;
}
}
export default {
async fetch(request, env, ctx) {
const id = env.MY_DURABLE_OBJECT.idFromName('foo'); // 네임스페이스
const stub = env.MY_DURABLE_OBJECT.get(id);
return Response.json(await stub.getChats());
}
}
WebSocket 사용하기돈을 아끼기 위해 hibernate 가능한 웹소켓 API를 사용하자
hibernate시 인스턴스가 새로 생성되는거여서 클래스에 데이터를 저장하면 안되고 sqlite or attachment에 데이터를 저장해야한다.
DurableObject클래스에서 웹소켓 시작 메소드는 항상fetch()이여야 한다!!!
https://github.com/cloudflare/workerd/issues/2319
export class MyDurableObject extends DurableObject {
async fetch(request) {
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
this.ctx.acceptWebSocket(server);
const ip = request.headers.get('CF-Connecting-IP');
server.serializeAttachment({ ip }); // 소켓마다 ip정보 attachment로 저장하기
return new Response(null, {
status: 101,
webSocket: client,
});
}
async webSocketMessage(ws, message) {
const { ip } = ws.deserializeAttachment() // 소켓 ip attachment 가져오기
ws.send(`[${ip}]: ${message}`);
}
async webSocketClose(ws, code, reason, wasClean) {
ws.close(code, 'Durable Object is closing WebSocket');
}
}
export default {
async fetch(request, env, ctx) {
const id = env.MY_DURABLE_OBJECT.idFromName('foo'); // 네임스페이스
const stub = env.MY_DURABLE_OBJECT.get(id);
return stub.fetch(request);
}
}
Durable Objects의 namespace는 각각의 storage를 가진다. CF-Connecting-IP 헤더는 wrangler dev 환경에서 안보인다
Durable Objects로 만든 채팅 서비스 리포지토리
https://github.com/stupidJoon/supa-chat/tree/cf-do