Cloudflare Durable Objects
Serverless stateful backend를 구현할 수 있는 서비스.
Workers의 일종이고, Workers Paid Plan이어야 사용 가능함. 5$ 기본 요금이 있는데, 여기에 durable objects기본 사용량도 포함되어있음. 사용사례로 실시간 채팅, 멀티플레이 게임을 든다.
EC2컴퓨팅 돌리듯이 시간당 청구되는 옵션이 있으니, 다 썼으면 잘 끄고 다녀야한다. 특히, 연결된 스토리지를 비우지 않으면(deleteAll()) 꺼지지 않으니 주의 필요.
wrangler.toml
Durable Objects를 사용하기 위해 바인딩 설정을 확인하세요.
[[durable_objects.bindings]]
name = "MY_CHAT_ROOM"
class_name = "MyChatRoom"
[[migrations]]
tag = "v1"
new_classes = ["MyChatRoom"]
protobuf와 함께 사용하는 방법
ts-proto
import { UserProfile } from "./generated/user"; // ts-proto로 생성된 파일
export class UserDO extends DurableObject {
async saveUser(data: UserProfile) {
// 1. 데이터를 바이너리로 인코딩
const encoded = UserProfile.encode(data).finish();
// 2. DO Storage에 직접 저장 (바이너리 저장이 JSON보다 저렴)
await this.ctx.storage.put("user_data", encoded);
}
async getUser(): Promise<UserProfile | undefined> {
const stored = await this.ctx.storage.get<Uint8Array>("user_data");
if (!stored) return undefined;
// 3. 디코딩하여 객체로 복원
return UserProfile.decode(stored);
}
}
Connect RPC
API 통신까지 고려한다면 추천... ?
클라이언트와 직접 gRPC 스타일로 통신해야 한다면 Connect를 추천 한다고함.... <- 확인 필요.
protobufjs
기존에 가장 많이 쓰이던 라이브러리입니다. 모든 기능이 들어있는 전체 패키지 대신 protobufjs/light 또는 생성된 정적 코드만 사용하는 방식을 권장합니다.
Hibernation API
가장 중요한 점은 this.ctx.acceptWebSocket(ws)를 호출하여 상태를 위임하는 것입니다.
export class MyChatRoom extends DurableObject {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
}
async fetch(request: Request) {
// 1. WebSocket 업그레이드 요청 확인
const webSocketPair = new WebSocketPair();
const [client, server] = Object.values(webSocketPair);
// 2. 서버 측 소켓 수락 및 'Hibernation' 모드 활성화
this.ctx.acceptWebSocket(server);
return new Response(null, { status: 101, webSocket: client });
}
// 3. 메시지가 도착했을 때만 DO가 깨어나서 이 메서드를 실행합니다.
async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer) {
// 메시지 처리 및 브로드캐스트
this.ctx.getWebSockets().forEach(peer => {
if (peer !== ws) peer.send(`상대방: ${message}`);
});
}
// 4. 연결이 끊겼을 때 실행
async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean) {
console.log("연결 종료");
}
}
상태(State) 관리 주의사항
Hibernation 모드에서는 DO가 수시로 메모리에서 내려갔다가(잠들었다가) 다시 올라옵니다. 따라서 클래스 멤버 변수에 데이터를 저장하면 안 됩니다.
- 잘못된 예:
this.users = [](잠들면 초기화됨) - 올바른 예:
this.ctx.storage.put()또는ws.serializeAttachment()를 사용하여 상태를 영속화해야 합니다.
See also
- Cloudflare
- S3
- Rivet Kit - Durable Objects의 오픈소스 대안
- @cloudflare/actors