[2026] Edge Computing 실전 가이드 | Cloudflare Workers, Vercel Edge, Deno Deploy

[2026] Edge Computing 실전 가이드 | Cloudflare Workers, Vercel Edge, Deno Deploy

이 글의 핵심

Edge Computing으로 전 세계 사용자에게 빠른 응답을 제공하세요. Cloudflare Workers, Vercel Edge Functions, Deno Deploy 비교, 실전 구현, 제약사항, 최적화 기법을 다룹니다.

들어가며

Edge Computing은 코드를 전 세계 CDN 노드에서 실행하여 사용자와 가장 가까운 위치에서 응답을 생성하는 기술입니다. 서울 사용자는 서울 노드에서, 뉴욕 사용자는 뉴욕 노드에서 실행되므로 지연 시간이 50ms 이하로 낮아집니다. 이 글은 Edge Computing의 핵심 개념, 주요 플랫폼 비교 (Cloudflare Workers, Vercel Edge, Deno Deploy), 실전 구현, 제약사항, 최적화 기법을 단계별로 설명합니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

목차

  1. Edge Computing이란?
  2. 플랫폼 비교
  3. Cloudflare Workers
  4. Vercel Edge Functions
  5. Deno Deploy
  6. Edge 데이터베이스
  7. 제약사항 및 해결
  8. 성능 최적화
  9. 실무 사례
  10. 트러블슈팅
  11. 마무리

Edge Computing이란?

아키텍처 비교

전통적인 서버:

사용자 (서울) → 서버 (미국 버지니아) → 응답
지연 시간: 200-300ms

CDN (정적 콘텐츠):

사용자 (서울) → CDN 노드 (서울) → 캐시된 파일
지연 시간: 10-20ms

Edge Computing (동적 콘텐츠):

사용자 (서울) → Edge 노드 (서울) → 코드 실행 → 응답
지연 시간: 30-50ms

핵심 특징

1. 글로벌 분산

  • 전 세계 200+ 노드에서 동일 코드 실행
  • 사용자와 가장 가까운 노드 자동 선택
  • 지역별 트래픽 폭증에 자동 대응 2. 서버리스 실행
  • 서버 관리 불필요
  • 자동 스케일링
  • 사용량 기반 과금 3. 낮은 콜드 스타트
  • V8 Isolate 기반 (Cloudflare)
  • 콜드 스타트: ~5ms (일반 Lambda: ~100-500ms) 4. 제약사항
  • 실행 시간: 10-30초
  • 메모리: 128MB
  • CPU 제한
  • Node.js API 일부만 지원

플랫폼 비교

항목Cloudflare WorkersVercel EdgeDeno Deploy
노드 수300+20+35+
런타임V8 IsolateV8 IsolateDeno Runtime
언어JS, TS, Rust, C++ (WASM)JS, TSJS, TS
실행 시간30초 (무료: 10ms)30초30초
메모리128MB128MB512MB
무료 요청100K/일100K/월100K/일
가격$5/1000만 요청$20/100만 요청$10/100만 요청
DB 통합D1, KV, Durable ObjectsVercel Postgres, KVDeno KV
WebSocketDurable Objects제한적지원
특징가장 빠름, 저렴Next.js 통합 우수표준 API, TypeScript 네이티브

선택 가이드

Cloudflare Workers 선택:

  • 최저 지연 시간 필요
  • 대규모 트래픽 (수백만 요청/일)
  • 비용 최적화 중요 Vercel Edge 선택:
  • Next.js 프로젝트
  • 빠른 배포 및 개발 경험
  • Vercel 생태계 활용 Deno Deploy 선택:
  • TypeScript 네이티브 개발
  • 표준 Web API 선호
  • Deno 생태계 활용

Cloudflare Workers

기본 예제

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// worker.js
export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        
        if (url.pathname === '/api/hello') {
            return new Response(JSON.stringify({
                message: 'Hello from Edge!',
                location: request.cf.city,  // 사용자 위치
                timestamp: Date.now()
            }), {
                headers: { 'Content-Type': 'application/json' }
            });
        }
        
        return new Response('Not Found', { status: 404 });
    }
};

KV 스토리지 사용

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        const key = url.pathname.slice(1);  // /key → key
        
        if (request.method === 'GET') {
            // KV에서 읽기
            const value = await env.MY_KV.get(key);
            
            if (value === null) {
                return new Response('Not found', { status: 404 });
            }
            
            return new Response(value);
        }
        
        if (request.method === 'PUT') {
            // KV에 쓰기
            const value = await request.text();
            await env.MY_KV.put(key, value, {
                expirationTtl: 3600  // 1시간 후 만료
            });
            
            return new Response('Stored');
        }
        
        return new Response('Method not allowed', { status: 405 });
    }
};

D1 데이터베이스

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        if (request.method === 'GET') {
            // 사용자 목록 조회
            const { results } = await env.DB.prepare(
                'SELECT * FROM users LIMIT 10'
            ).all();
            
            return Response.json(results);
        }
        
        if (request.method === 'POST') {
            // 사용자 생성
            const { name, email } = await request.json();
            
            await env.DB.prepare(
                'INSERT INTO users (name, email) VALUES (?, ?)'
            ).bind(name, email).run();
            
            return Response.json({ success: true });
        }
    }
};

캐싱 전략

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        const cacheKey = new Request(url.toString(), request);
        const cache = caches.default;
        
        // 캐시 확인
        let response = await cache.match(cacheKey);
        
        if (!response) {
            // 캐시 미스: 원본 서버에서 가져오기
            response = await fetch(request);
            
            // 캐시 저장 (1시간)
            response = new Response(response.body, response);
            response.headers.set('Cache-Control', 'max-age=3600');
            
            ctx.waitUntil(cache.put(cacheKey, response.clone()));
        }
        
        return response;
    }
};

Vercel Edge Functions

기본 예제

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// app/api/hello/route.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';  // Edge Runtime 사용
export async function GET(request: NextRequest) {
    const { searchParams } = new URL(request.url);
    const name = searchParams.get('name') || 'World';
    
    return NextResponse.json({
        message: `Hello, ${name}!`,
        location: request.geo?.city,
        country: request.geo?.country
    });
}

Middleware (Edge에서 실행)

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
    // A/B 테스트
    const bucket = Math.random() < 0.5 ? 'a' : 'b';
    const response = NextResponse.next();
    response.cookies.set('bucket', bucket);
    
    // 지역별 리다이렉트
    const country = request.geo?.country;
    if (country === 'KR' && !request.nextUrl.pathname.startsWith('/ko')) {
        return NextResponse.redirect(new URL('/ko', request.url));
    }
    
    // 인증 확인
    const token = request.cookies.get('auth_token');
    if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
        return NextResponse.redirect(new URL('/login', request.url));
    }
    
    return response;
}
export const config = {
    matcher: ['/((?!_next/static|_next/image|favicon.ico).*)']
};

Vercel KV (Redis)

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import { kv } from '@vercel/kv';
export const runtime = 'edge';
export async function GET(request: Request) {
    const url = new URL(request.url);
    const key = url.searchParams.get('key');
    
    if (!key) {
        return new Response('Key required', { status: 400 });
    }
    
    // KV에서 읽기
    const value = await kv.get(key);
    
    if (value === null) {
        return new Response('Not found', { status: 404 });
    }
    
    return Response.json({ key, value });
}
export async function POST(request: Request) {
    const { key, value, ttl } = await request.json();
    
    // KV에 쓰기
    if (ttl) {
        await kv.setex(key, ttl, value);
    } else {
        await kv.set(key, value);
    }
    
    return Response.json({ success: true });
}

Deno Deploy

기본 예제

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// main.ts
Deno.serve(async (req) => {
    const url = new URL(req.url);
    
    if (url.pathname === '/api/hello') {
        return new Response(JSON.stringify({
            message: 'Hello from Deno Deploy!',
            timestamp: new Date().toISOString()
        }), {
            headers: { 'Content-Type': 'application/json' }
        });
    }
    
    return new Response('Not Found', { status: 404 });
});

Deno KV 사용

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

const kv = await Deno.openKv();
Deno.serve(async (req) => {
    const url = new URL(req.url);
    
    if (req.method === 'GET') {
        const key = url.searchParams.get('key');
        if (!key) {
            return new Response('Key required', { status: 400 });
        }
        
        // KV에서 읽기
        const entry = await kv.get([key]);
        
        if (entry.value === null) {
            return new Response('Not found', { status: 404 });
        }
        
        return Response.json({ key, value: entry.value });
    }
    
    if (req.method === 'POST') {
        const { key, value } = await req.json();
        
        // KV에 쓰기
        await kv.set([key], value);
        
        return Response.json({ success: true });
    }
});

표준 Web API 활용

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Fetch API
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Web Crypto API
const encoder = new TextEncoder();
const data = encoder.encode('hello');
const hash = await crypto.subtle.digest('SHA-256', data);
// Streams API
Deno.serve(async (req) => {
    const stream = new ReadableStream({
        start(controller) {
            controller.enqueue('chunk 1\n');
            controller.enqueue('chunk 2\n');
            controller.close();
        }
    });
    
    return new Response(stream, {
        headers: { 'Content-Type': 'text/plain' }
    });
});

Edge 데이터베이스

Cloudflare D1 (SQLite)

다음은 javascript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        // 쿼리 실행
        const { results } = await env.DB.prepare(
            'SELECT * FROM posts WHERE published = ? ORDER BY created_at DESC LIMIT 10'
        ).bind(true).all();
        
        return Response.json(results);
    }
};
// 트랜잭션
async function createUser(db, name, email) {
    const batch = [
        db.prepare('INSERT INTO users (name, email) VALUES (?, ?)').bind(name, email),
        db.prepare('INSERT INTO audit_log (action, timestamp) VALUES (?, ?)').bind('user_created', Date.now())
    ];
    
    await db.batch(batch);
}

PlanetScale (MySQL)

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Vercel Edge Function
import { connect } from '@planetscale/database';
export const runtime = 'edge';
export async function GET() {
    const conn = connect({
        url: process.env.DATABASE_URL
    });
    
    const results = await conn.execute(
        'SELECT * FROM posts WHERE published = true LIMIT 10'
    );
    
    return Response.json(results.rows);
}

Upstash Redis

다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import { Redis } from '@upstash/redis';
export const runtime = 'edge';
const redis = new Redis({
    url: process.env.UPSTASH_REDIS_REST_URL,
    token: process.env.UPSTASH_REDIS_REST_TOKEN
});
export async function GET(request: Request) {
    const url = new URL(request.url);
    const key = url.searchParams.get('key');
    
    // 캐시 확인
    const cached = await redis.get(key);
    if (cached) {
        return Response.json({ value: cached, cached: true });
    }
    
    // 원본 데이터 가져오기
    const data = await fetchFromOrigin(key);
    
    // 캐시 저장 (1시간)
    await redis.setex(key, 3600, data);
    
    return Response.json({ value: data, cached: false });
}

제약사항 및 해결

1. Node.js API 제한

문제: fs, path, crypto (Node.js) 사용 불가 해결: 아래 코드는 typescript를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ❌ Node.js API
import fs from 'fs';
import crypto from 'crypto';
// ✅ Web API
const hash = await crypto.subtle.digest('SHA-256', data);
// ✅ Cloudflare Workers API
const file = await env.BUCKET.get('file.txt');

2. 실행 시간 제한

문제: 30초 초과 시 타임아웃 해결: 다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 긴 연산
export default {
    async fetch(request) {
        const result = await longComputation();  // 1분 소요
        return Response.json(result);
    }
};
// ✅ 백그라운드 작업으로 분리
export default {
    async fetch(request, env, ctx) {
        // 즉시 응답
        const jobId = crypto.randomUUID();
        
        // 백그라운드 작업 (ctx.waitUntil)
        ctx.waitUntil(
            env.QUEUE.send({ jobId, data: await request.json() })
        );
        
        return Response.json({ jobId, status: 'processing' });
    }
};

3. 상태 유지 불가

문제: 요청 간 메모리 공유 불가 해결: 다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 전역 변수 (요청 간 공유 안 됨)
let counter = 0;
export default {
    async fetch(request) {
        counter++;  // 매번 0에서 시작
        return Response.json({ counter });
    }
};
// ✅ KV 스토리지 사용
export default {
    async fetch(request, env, ctx) {
        const counter = await env.KV.get('counter') || 0;
        await env.KV.put('counter', counter + 1);
        
        return Response.json({ counter: counter + 1 });
    }
};

4. 패키지 크기 제한

문제: 번들 크기 1MB 제한 해결: 아래 코드는 typescript를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 코드를 직접 실행해보면서 동작을 확인해보세요.

// ❌ 큰 라이브러리
import moment from 'moment';  // 200KB+
// ✅ 작은 대안
import { format } from 'date-fns';  // 10KB (트리 셰이킹)
// ✅ 네이티브 API
const date = new Date().toISOString();

성능 최적화

1. 캐싱 전략

Edge 캐시 + KV 조합: 다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        const cacheKey = new Request(url.toString());
        const cache = caches.default;
        
        // 1. Edge 캐시 확인 (가장 빠름)
        let response = await cache.match(cacheKey);
        if (response) {
            return response;
        }
        
        // 2. KV 확인 (중간)
        const cached = await env.KV.get(url.pathname);
        if (cached) {
            response = new Response(cached, {
                headers: { 'Cache-Control': 'max-age=3600' }
            });
            ctx.waitUntil(cache.put(cacheKey, response.clone()));
            return response;
        }
        
        // 3. 원본 서버 (가장 느림)
        const data = await fetchFromOrigin(url.pathname);
        response = Response.json(data, {
            headers: { 'Cache-Control': 'max-age=3600' }
        });
        
        // 비동기로 캐시 저장
        ctx.waitUntil(Promise.all([
            cache.put(cacheKey, response.clone()),
            env.KV.put(url.pathname, JSON.stringify(data), { expirationTtl: 3600 })
        ]));
        
        return response;
    }
};

2. 조건부 요청

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        const key = url.pathname;
        
        // ETag 생성
        const data = await env.KV.get(key);
        const etag = `"${await hashData(data)}"`;
        
        // 클라이언트 ETag 확인
        const clientETag = request.headers.get('If-None-Match');
        if (clientETag === etag) {
            return new Response(null, { status: 304 });  // Not Modified
        }
        
        return new Response(data, {
            headers: {
                'ETag': etag,
                'Cache-Control': 'max-age=3600'
            }
        });
    }
};
async function hashData(data: string): Promise<string> {
    const encoder = new TextEncoder();
    const hash = await crypto.subtle.digest('SHA-256', encoder.encode(data));
    return Array.from(new Uint8Array(hash))
        .map(b => b.toString(16).padStart(2, '0'))
        .join(');
}

3. 스트리밍 응답

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request) {
        const stream = new ReadableStream({
            async start(controller) {
                // 대용량 데이터를 청크로 전송
                for (let i = 0; i < 100; i++) {
                    const chunk = await fetchChunk(i);
                    controller.enqueue(new TextEncoder().encode(chunk + '\n'));
                    
                    // 백프레셔 처리
                    if (controller.desiredSize <= 0) {
                        await new Promise(resolve => setTimeout(resolve, 100));
                    }
                }
                
                controller.close();
            }
        });
        
        return new Response(stream, {
            headers: { 'Content-Type': 'text/plain' }
        });
    }
};

실무 사례

1. API 게이트웨이

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        
        // 인증 확인
        const token = request.headers.get('Authorization');
        if (!token) {
            return new Response('Unauthorized', { status: 401 });
        }
        
        // Rate limiting
        const clientIP = request.headers.get('CF-Connecting-IP');
        const rateLimitKey = `rate:${clientIP}`;
        const count = await env.KV.get(rateLimitKey) || 0;
        
        if (count > 100) {
            return new Response('Too Many Requests', { status: 429 });
        }
        
        await env.KV.put(rateLimitKey, count + 1, { expirationTtl: 60 });
        
        // 백엔드로 프록시
        const backendUrl = `https://api.backend.com${url.pathname}`;
        const response = await fetch(backendUrl, {
            method: request.method,
            headers: request.headers,
            body: request.body
        });
        
        return response;
    }
};

2. 이미지 최적화

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        const imageUrl = url.searchParams.get('url');
        
        if (!imageUrl) {
            return new Response('URL required', { status: 400 });
        }
        
        // Accept 헤더로 포맷 결정
        const accept = request.headers.get('Accept') || ';
        const format = accept.includes('image/webp') ? 'webp' : 
                      accept.includes('image/avif') ? 'avif' : 'jpeg';
        
        // Cloudflare Image Resizing
        const imageRequest = new Request(imageUrl, {
            cf: {
                image: {
                    width: 800,
                    quality: 85,
                    format: format
                }
            }
        });
        
        const response = await fetch(imageRequest);
        
        return new Response(response.body, {
            headers: {
                'Content-Type': `image/${format}`,
                'Cache-Control': 'max-age=86400'
            }
        });
    }
};

3. 개인화 콘텐츠

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        
        // 사용자 정보 (쿠키 또는 헤더)
        const userId = request.headers.get('X-User-ID');
        const country = request.cf.country;
        
        // 개인화 캐시 키
        const cacheKey = `content:${url.pathname}:${userId}:${country}`;
        
        // KV에서 개인화 콘텐츠 확인
        const cached = await env.KV.get(cacheKey);
        if (cached) {
            return new Response(cached, {
                headers: { 'Content-Type': 'text/html' }
            });
        }
        
        // 개인화 콘텐츠 생성
        const content = await generatePersonalizedContent(userId, country);
        
        // 캐시 저장 (10분)
        ctx.waitUntil(
            env.KV.put(cacheKey, content, { expirationTtl: 600 })
        );
        
        return new Response(content, {
            headers: { 'Content-Type': 'text/html' }
        });
    }
};

4. 서버사이드 A/B 테스트

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        // 사용자 ID로 일관된 버킷 할당
        const userId = request.headers.get('X-User-ID') || 
                      request.headers.get('CF-Connecting-IP');
        
        const hash = await hashString(userId);
        const bucket = hash % 100 < 50 ? 'A' : 'B';
        
        // 버킷별 다른 응답
        const content = bucket === 'A' 
            ? await fetchVariantA() 
            : await fetchVariantB();
        
        // 분석 이벤트 전송
        ctx.waitUntil(
            env.ANALYTICS.writeDataPoint({
                userId,
                bucket,
                timestamp: Date.now()
            })
        );
        
        return new Response(content, {
            headers: { 'X-AB-Bucket': bucket }
        });
    }
};
async function hashString(str: string): Promise<number> {
    const encoder = new TextEncoder();
    const data = encoder.encode(str);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.reduce((acc, byte) => acc + byte, 0);
}

트러블슈팅

문제 1: CPU 시간 초과

증상:

Error: CPU time limit exceeded

해결: 다음은 typescript를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ CPU 집약적 작업
function heavyComputation(n: number) {
    let sum = 0;
    for (let i = 0; i < n * 1000000; i++) {
        sum += Math.sqrt(i);
    }
    return sum;
}
// ✅ 작업 분할 또는 백엔드로 이동
export default {
    async fetch(request, env, ctx) {
        // Edge에서는 간단한 작업만
        const params = await request.json();
        
        // 무거운 작업은 백엔드로
        const response = await fetch('https://backend.com/compute', {
            method: 'POST',
            body: JSON.stringify(params)
        });
        
        return response;
    }
};

문제 2: 패키지 호환성

증상:

Error: Module "fs" is not available in Workers

해결: 아래 코드는 typescript를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ Node.js 전용 패키지
import fs from 'fs';
// ✅ Edge 호환 패키지 찾기
// 또는 필요한 기능만 직접 구현
// ✅ 조건부 import
let parser;
if (typeof Deno !== 'undefined') {
    parser = await import('./deno-parser.ts');
} else {
    parser = await import('./edge-parser.ts');
}

문제 3: 콜드 스타트 느림

증상: 첫 요청이 느림 해결: 다음은 typescript를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 1. 번들 크기 최소화
// ❌ 전체 라이브러리 import
import _ from 'lodash';
// ✅ 필요한 함수만
import { debounce } from 'lodash-es';
// 2. 동적 import 사용
export default {
    async fetch(request) {
        const url = new URL(request.url);
        
        if (url.pathname === '/heavy') {
            // 필요할 때만 로드
            const { processHeavy } = await import('./heavy.js');
            return await processHeavy(request);
        }
        
        return new Response('OK');
    }
};
// 3. 워밍업 요청
// 정기적으로 더미 요청 전송하여 콜드 스타트 방지

비용 최적화

요청 수 절감

다음은 typescript를 활용한 상세한 구현 코드입니다. 비동기 처리를 통해 효율적으로 작업을 수행합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

export default {
    async fetch(request, env, ctx) {
        const url = new URL(request.url);
        
        // 1. 정적 파일은 CDN 캐시 활용
        if (url.pathname.match(/\.(js|css|png|jpg)$/)) {
            return fetch(request);  // 오리진으로 (CDN 캐시됨)
        }
        
        // 2. API 응답 캐싱
        const cacheKey = new Request(url.toString());
        const cache = caches.default;
        
        let response = await cache.match(cacheKey);
        if (response) {
            return response;  // Edge 함수 실행 안 함 (무료)
        }
        
        // 3. 실제 처리
        response = await processRequest(request, env);
        
        // 4. 캐시 저장
        response = new Response(response.body, response);
        response.headers.set('Cache-Control', 'max-age=3600');
        ctx.waitUntil(cache.put(cacheKey, response.clone()));
        
        return response;
    }
};

마무리

Edge Computing은 글로벌 저지연 서비스를 구축하는 핵심 기술입니다: 핵심 장점:

  • 낮은 지연: 30-50ms (전통적 서버: 200-300ms)
  • 자동 스케일링: 트래픽 급증에 자동 대응
  • 글로벌 분산: 전 세계 동일한 성능
  • 비용 효율: 사용량 기반 과금 적합한 사용 사례:
  • API 게이트웨이, 인증/인가
  • 개인화 콘텐츠, A/B 테스트
  • 이미지 최적화, 리사이징
  • 지역별 리다이렉트, 라우팅 부적합한 사용 사례:
  • 긴 연산 (30초 이상)
  • 대용량 파일 처리
  • 레거시 Node.js 패키지 의존성
  • 상태 유지 연결 (WebSocket 제한적) 시작 가이드:
  1. 프로토타입: Vercel Edge (Next.js 통합)
  2. 프로덕션: Cloudflare Workers (비용, 성능)
  3. TypeScript 중심: Deno Deploy (표준 API) 다음 학습:
... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3