본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout
TechProjectsBooksAbout
© 2026 Kreath. All rights reserved.
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 8장: 서버리스와 엣지 배포
2026년 7월 30일·프로그래밍·

8장: 서버리스와 엣지 배포

Deno Deploy를 중심으로 서버리스 및 엣지 배포 전략을 다룹니다. 엣지 컴퓨팅 개념, 콜드 스타트 성능, Deno KV를 활용한 엣지 상태 관리, Cloudflare Workers 호환성을 분석합니다.

19분896자7개 섹션
typescriptperformancetoolingsecuritydeveloper-experience
공유
deno-runtime8 / 10
12345678910
이전7장: Fresh 프레임워크 - Deno 네이티브 웹 개발다음9장: Deno로 REST API 구축하기

엣지 컴퓨팅의 시대

전통적인 서버 배포 모델에서는 애플리케이션이 특정 리전(Region)의 서버에서 실행됩니다. 서울에 서버를 두면 한국 사용자에게는 빠르지만, 미국이나 유럽 사용자에게는 수백 밀리초의 네트워크 지연이 발생합니다.

엣지 컴퓨팅(Edge Computing)은 이 문제를 해결합니다. 코드를 전 세계 수십 개 지점(엣지 노드)에 분산 배포하여, 사용자와 가장 가까운 서버에서 요청을 처리합니다.

Deno는 엣지 컴퓨팅에 최적화된 런타임입니다. 빠른 시작 시간, 작은 메모리 풋프린트, 웹 표준 API 기반 설계가 엣지 환경의 요구사항과 정확히 맞아떨어집니다.

Deno Deploy

플랫폼 개요

Deno Deploy는 Deno 팀이 운영하는 공식 서버리스 엣지 플랫폼입니다. 전 세계 35개 이상의 리전에서 Deno 코드를 실행할 수 있으며, GitHub 저장소와 연동하여 자동 배포를 지원합니다.

Deno Deploy 핵심 특성
text
- 전 세계 35+ 리전에 자동 분산 배포
- V8 Isolate 기반 실행 (가상 머신이 아님)
- 콜드 스타트 없음에 가까운 시작 시간
- 웹 표준 API 완벽 지원
- Deno KV 전역 분산 데이터베이스 내장
- GitHub 연동 자동 배포 (프리뷰 배포 포함)
- 무료 티어 포함

배포 과정

Deno Deploy로의 배포는 매우 간단합니다.

CLI를 통한 배포
bash
# deployctl 설치
deno install -gArf jsr:@deno/deployctl
 
# 프로젝트 배포
deployctl deploy --project=my-app src/main.ts
 
# 프로덕션 배포 (고정 URL)
deployctl deploy --project=my-app --prod src/main.ts
Deno Deploy 호환 서버 코드
typescript
// main.ts - Deno Deploy에서 실행되는 코드
Deno.serve(async (req: Request) => {
  const url = new URL(req.url);
 
  // 라우팅
  if (url.pathname === "/") {
    return new Response("Deno Deploy에서 실행 중입니다", {
      headers: { "Content-Type": "text/plain; charset=utf-8" },
    });
  }
 
  if (url.pathname === "/api/time") {
    return new Response(
      JSON.stringify({
        timestamp: new Date().toISOString(),
        region: Deno.env.get("DENO_REGION") ?? "unknown",
      }),
      {
        headers: { "Content-Type": "application/json" },
      },
    );
  }
 
  return new Response("Not Found", { status: 404 });
});
Info

Deno Deploy에서는 DENO_REGION 환경 변수를 통해 현재 요청을 처리하는 엣지 노드의 위치를 확인할 수 있습니다. 이를 활용하면 리전별 로직 분기나 지연 시간 모니터링이 가능합니다.

GitHub 연동 자동 배포

GitHub 연동 배포 플로우
text
1. Deno Deploy 대시보드에서 GitHub 저장소 연결
2. 엔트리포인트 파일 지정 (예: src/main.ts)
3. main 브랜치에 push하면 자동 프로덕션 배포
4. PR을 열면 프리뷰 URL 자동 생성
5. 프리뷰 URL로 변경사항 검증 후 머지

이 워크플로우는 Vercel이나 Netlify와 유사하지만, Deno 런타임에 최적화된 환경을 제공합니다.

콜드 스타트 성능

서버리스의 가장 큰 과제

서버리스 플랫폼에서 가장 큰 성능 과제는 콜드 스타트(Cold Start)입니다. 요청이 들어왔을 때 실행 환경이 준비되지 않은 상태라면, 환경을 초기화하는 데 걸리는 시간이 응답 지연으로 직결됩니다.

Deno Deploy의 콜드 스타트 전략

Deno Deploy는 V8 아이솔레이트(Isolate) 기반으로 실행됩니다. 전통적인 컨테이너 기반 서버리스(AWS Lambda 등)와 달리, 아이솔레이트는 밀리초 단위로 생성됩니다.

콜드 스타트 시간 비교 (참고용)
text
Deno Deploy (V8 Isolate)
  - 콜드 스타트: ~10ms
  - 웜 스타트: ~0ms
 
AWS Lambda (Node.js, 컨테이너)
  - 콜드 스타트: ~200-500ms
  - 웜 스타트: ~1-5ms
 
AWS Lambda (Node.js, SnapStart)
  - 콜드 스타트: ~100-200ms
  - 웜 스타트: ~1-5ms
 
Cloudflare Workers (V8 Isolate)
  - 콜드 스타트: ~5ms
  - 웜 스타트: ~0ms
Tip

V8 Isolate 기반 실행의 핵심 장점은 "0ms에 가까운 콜드 스타트"입니다. 컨테이너를 부팅하는 것이 아니라 V8 엔진 내에 격리된 실행 환경을 생성하는 것이므로, 오버헤드가 극히 적습니다. 이것이 Deno Deploy와 Cloudflare Workers가 엣지 환경에서 뛰어난 성능을 보이는 근본적인 이유입니다.

제약 사항

Isolate 기반 실행에는 몇 가지 제약이 있습니다.

Deno Deploy 제약 사항
text
실행 시간 제한
  - 요청당 최대 실행 시간: 50초 (기본), 150초 (유료)
  - 장기 실행 작업에는 적합하지 않음
 
메모리 제한
  - Isolate당 메모리 제한 (약 512MB)
  - 대용량 데이터 처리에 제약
 
파일 시스템
  - 읽기 전용 파일 시스템 (정적 파일만 접근)
  - 동적 파일 생성/수정 불가
 
네이티브 모듈
  - C/C++ 네이티브 모듈 사용 불가
  - Wasm(WebAssembly)으로 대체 가능

Deno KV: 엣지 상태 관리

엣지에서의 데이터 저장 과제

엣지 환경에서 가장 어려운 과제 중 하나는 상태(State) 관리입니다. 요청이 전 세계 어느 엣지 노드에서든 처리될 수 있으므로, 데이터의 일관성과 접근성이 중요합니다.

Deno KV는 이 문제를 해결하는 Deno Deploy의 내장 분산 데이터베이스입니다.

세션 관리 예시

Deno KV로 엣지 세션 관리
typescript
const kv = await Deno.openKv();
 
interface Session {
  userId: string;
  email: string;
  createdAt: string;
  expiresAt: string;
}
 
async function createSession(userId: string, email: string): Promise<string> {
  const sessionId = crypto.randomUUID();
  const now = new Date();
  const expires = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24시간
 
  const session: Session = {
    userId,
    email,
    createdAt: now.toISOString(),
    expiresAt: expires.toISOString(),
  };
 
  await kv.set(["sessions", sessionId], session, {
    expireIn: 24 * 60 * 60 * 1000, // 자동 만료
  });
 
  return sessionId;
}
 
async function getSession(sessionId: string): Promise<Session | null> {
  const result = await kv.get<Session>(["sessions", sessionId]);
  return result.value;
}
 
async function deleteSession(sessionId: string): Promise<void> {
  await kv.delete(["sessions", sessionId]);
}
 
// HTTP 서버에서 활용
Deno.serve(async (req) => {
  const url = new URL(req.url);
 
  if (url.pathname === "/api/login" && req.method === "POST") {
    const { userId, email } = await req.json();
    const sessionId = await createSession(userId, email);
 
    return new Response(JSON.stringify({ ok: true }), {
      headers: {
        "Content-Type": "application/json",
        "Set-Cookie": `session=${sessionId}; HttpOnly; Secure; SameSite=Strict; Path=/`,
      },
    });
  }
 
  if (url.pathname === "/api/me") {
    const cookie = req.headers.get("Cookie") ?? "";
    const sessionId = cookie.match(/session=([^;]+)/)?.[1];
 
    if (!sessionId) {
      return new Response("인증 필요", { status: 401 });
    }
 
    const session = await getSession(sessionId);
    if (!session) {
      return new Response("세션 만료", { status: 401 });
    }
 
    return new Response(JSON.stringify(session), {
      headers: { "Content-Type": "application/json" },
    });
  }
 
  return new Response("Not Found", { status: 404 });
});

방문자 카운터 (엣지 분산 카운터)

Deno KV를 활용한 분산 카운터
typescript
const kv = await Deno.openKv();
 
async function incrementPageView(path: string): Promise<number> {
  // 원자적 증가 연산
  let retries = 0;
  while (retries < 5) {
    const current = await kv.get<number>(["pageviews", path]);
    const newCount = (current.value ?? 0) + 1;
 
    const result = await kv.atomic()
      .check(current) // 낙관적 잠금
      .set(["pageviews", path], newCount)
      .commit();
 
    if (result.ok) {
      return newCount;
    }
 
    retries++;
  }
 
  throw new Error("카운터 업데이트 실패 (동시성 충돌)");
}
 
Deno.serve(async (req) => {
  const url = new URL(req.url);
 
  // 페이지 방문 시 카운터 증가
  const views = await incrementPageView(url.pathname);
 
  return new Response(
    JSON.stringify({
      path: url.pathname,
      views,
      region: Deno.env.get("DENO_REGION"),
    }),
    {
      headers: { "Content-Type": "application/json" },
    },
  );
});
Warning

Deno KV의 전역 분산 모드에서는 최종 일관성(Eventual Consistency) 모델을 사용합니다. 한 리전에서 쓴 데이터가 다른 리전에 반영되기까지 약간의 지연이 있을 수 있습니다. 강한 일관성(Strong Consistency)이 필요한 경우에는 특정 리전을 지정하여 읽기/쓰기를 수행하거나, 별도의 데이터베이스를 사용해야 합니다.

Cloudflare Workers 호환성

WinterCG 기반 코드 이식

Deno와 Cloudflare Workers는 모두 WinterCG 표준을 지향하므로, 핵심 코드의 상당 부분이 양쪽에서 호환됩니다.

WinterCG 호환 핸들러
typescript
// 이 코드는 Deno Deploy와 Cloudflare Workers 모두에서 동작합니다
async function handleRequest(request: Request): Promise<Response> {
  const url = new URL(request.url);
 
  switch (url.pathname) {
    case "/": {
      return new Response("환영합니다", {
        headers: { "Content-Type": "text/plain; charset=utf-8" },
      });
    }
 
    case "/api/data": {
      const data = { timestamp: Date.now(), message: "엣지에서 응답" };
      return new Response(JSON.stringify(data), {
        headers: { "Content-Type": "application/json" },
      });
    }
 
    case "/api/hash": {
      const body = await request.text();
      const encoder = new TextEncoder();
      const hash = await crypto.subtle.digest("SHA-256", encoder.encode(body));
      const hex = Array.from(new Uint8Array(hash))
        .map((b) => b.toString(16).padStart(2, "0"))
        .join("");
 
      return new Response(JSON.stringify({ hash: hex }), {
        headers: { "Content-Type": "application/json" },
      });
    }
 
    default:
      return new Response("Not Found", { status: 404 });
  }
}
 
// Deno Deploy용 엔트리포인트
Deno.serve(handleRequest);
Cloudflare Workers 엔트리포인트 (동일 핸들러 사용)
typescript
// Cloudflare Workers에서 동일한 handleRequest 사용
export default {
  async fetch(request: Request): Promise<Response> {
    return handleRequest(request);
  },
};

호환되지 않는 부분

플랫폼별 차이점
text
Deno Deploy 전용
  - Deno.openKv() - 내장 KV 저장소
  - Deno.cron() - 내장 크론 스케줄러
  - Deno.env.get("DENO_REGION") - 리전 정보
 
Cloudflare Workers 전용
  - KV (Cloudflare KV)
  - R2 (오브젝트 스토리지)
  - D1 (SQLite 기반 DB)
  - Durable Objects (상태 관리)
 
공통 (WinterCG 호환)
  - fetch, Request, Response
  - URL, URLSearchParams
  - crypto.subtle
  - TextEncoder, TextDecoder
  - Headers, AbortController
  - ReadableStream, WritableStream
Info

코드의 이식성을 극대화하려면, 핵심 비즈니스 로직은 웹 표준 API만 사용하여 작성하고, 플랫폼 전용 API(Deno KV, Cloudflare KV 등)는 어댑터 패턴으로 분리하는 것을 권장합니다. 이렇게 하면 플랫폼을 변경할 때 어댑터만 교체하면 됩니다.

엣지 배포 아키텍처 패턴

정적 사이트 + 엣지 API

가장 일반적인 엣지 배포 패턴입니다.

정적 + 엣지 API 아키텍처
text
사용자 요청
  |
  v
엣지 노드 (CDN)
  |-- 정적 파일 (HTML, CSS, JS, 이미지) --> 즉시 응답
  |-- API 요청 (/api/*) --> Deno Deploy 엣지 함수
                              |
                              v
                          Deno KV / 외부 DB

Fresh + Deno Deploy

Fresh 프레임워크와 Deno Deploy의 조합은 가장 자연스러운 Deno 풀스택 배포 패턴입니다.

Fresh + Deno Deploy 구성
typescript
// fresh.config.ts
import { defineConfig } from "fresh";
 
export default defineConfig({
  // Deno Deploy에서 자동으로 최적화됨
});
 
// routes/index.tsx - SSR이 엣지에서 수행
import { page } from "fresh";
 
export default page(function Home() {
  return (
    <div>
      <h1>엣지에서 렌더링된 페이지</h1>
      <p>
        이 페이지는 사용자와 가장 가까운 엣지 노드에서 서버 사이드 렌더링됩니다.
      </p>
    </div>
  );
});

Deno.cron: 엣지 스케줄링

Deno Deploy는 내장 크론 스케줄러도 제공합니다.

Deno.cron 활용
typescript
// 매 시간 실행되는 크론 작업
Deno.cron("hourly-cleanup", "0 * * * *", async () => {
  const kv = await Deno.openKv();
 
  // 만료된 세션 정리
  const iter = kv.list({ prefix: ["sessions"] });
  const now = Date.now();
 
  for await (const entry of iter) {
    const session = entry.value as { expiresAt: string };
    if (new Date(session.expiresAt).getTime() < now) {
      await kv.delete(entry.key);
    }
  }
 
  console.log("만료 세션 정리 완료");
});
 
// 매일 자정에 실행
Deno.cron("daily-stats", "0 0 * * *", async () => {
  const kv = await Deno.openKv();
 
  // 일일 통계 집계
  const stats = { date: new Date().toISOString().split("T")[0], views: 0 };
 
  const iter = kv.list<number>({ prefix: ["pageviews"] });
  for await (const entry of iter) {
    stats.views += entry.value;
  }
 
  await kv.set(["daily-stats", stats.date], stats);
  console.log(`일일 통계 저장: ${stats.views} views`);
});

엣지 배포 모범 사례

엣지 배포 체크리스트
text
성능 최적화
  [ ] 응답에 적절한 Cache-Control 헤더 설정
  [ ] 정적 자산은 CDN에서 서빙
  [ ] KV 조회 결과를 메모리에 캐싱 (짧은 TTL)
  [ ] 불필요한 외부 API 호출 최소화
 
보안
  [ ] 환경 변수로 시크릿 관리 (하드코딩 금지)
  [ ] CORS 헤더 적절히 설정
  [ ] Rate limiting 구현
  [ ] 입력 값 검증 철저히
 
모니터링
  [ ] 에러 로깅 설정
  [ ] 응답 시간 모니터링
  [ ] KV 사용량 추적
  [ ] 리전별 트래픽 분석
Tip

엣지 환경에서는 외부 데이터베이스 호출이 병목이 될 수 있습니다. 가능하면 Deno KV로 자주 접근하는 데이터를 캐시하고, 외부 DB는 비동기 업데이트로 동기화하는 CQRS 패턴을 고려하세요.

다음 장에서는 Deno로 REST API를 구축하는 실습을 진행합니다. Deno.serve()부터 프레임워크 활용, 데이터베이스 연동, 테스트, 배포까지 전 과정을 다룹니다.

이 글이 도움이 되셨나요?

관련 글

프로그래밍

7장: Fresh 프레임워크 - Deno 네이티브 웹 개발

Deno의 공식 웹 프레임워크 Fresh를 심층 분석합니다. Islands Architecture, Preact 기반 컴포넌트, 라우팅, 미들웨어, 데이터 페칭 등 핵심 기능을 다룹니다.

2026년 7월 28일·16분
프로그래밍

9장: Deno로 REST API 구축하기

Deno.serve()를 활용한 HTTP 서버 구축부터 Hono 프레임워크, PostgreSQL 및 Deno KV 데이터베이스 연동, 테스트 작성, 배포까지 REST API 개발 전 과정을 다룹니다.

2026년 8월 2일·16분
프로그래밍

10장: 실전 프로젝트 - Deno 2 풀스택 애플리케이션

Fresh 프론트엔드와 Deno.serve API, Deno KV를 결합한 풀스택 할 일 관리 애플리케이션을 구축합니다. 인증, 배포, 모니터링까지 실전 프로젝트의 전체 과정을 다룹니다.

2026년 8월 5일·22분
이전 글7장: Fresh 프레임워크 - Deno 네이티브 웹 개발
다음 글9장: Deno로 REST API 구축하기

댓글

목차

약 19분 남음
  • 엣지 컴퓨팅의 시대
  • Deno Deploy
    • 플랫폼 개요
    • 배포 과정
    • GitHub 연동 자동 배포
  • 콜드 스타트 성능
    • 서버리스의 가장 큰 과제
    • Deno Deploy의 콜드 스타트 전략
    • 제약 사항
  • Deno KV: 엣지 상태 관리
    • 엣지에서의 데이터 저장 과제
    • 세션 관리 예시
    • 방문자 카운터 (엣지 분산 카운터)
  • Cloudflare Workers 호환성
    • WinterCG 기반 코드 이식
    • 호환되지 않는 부분
  • 엣지 배포 아키텍처 패턴
    • 정적 사이트 + 엣지 API
    • Fresh + Deno Deploy
    • Deno.cron: 엣지 스케줄링
  • 엣지 배포 모범 사례