Advanced (업데이트: 2026. 6. 2.)

Claude Code로 엣지 컴퓨팅 활용하기

Claude Code로 Edge runtime을 설계하고 Workers, 지역 분기, 캐시 키, 검증 명령까지 실전 예제로 정리합니다.

Claude Code로 엣지 컴퓨팅 활용하기

엣지 컴퓨팅은 모든 백엔드를 사용자 근처로 옮기는 전략이 아닙니다. 요청이 본 애플리케이션에 도착하기 전에, 가까운 실행 환경에서 작은 결정을 처리하는 설계입니다. 국가별 리다이렉트, 짧은 캐시, A/B 테스트 버킷, 프리뷰 페이지의 가벼운 인증 같은 작업이 대표적입니다.

하지만 Edge runtime은 자동 성능 개선 장치가 아닙니다. 일반적인 Node.js 서버와 달리 사용할 수 있는 API, 실행 시간, 패키지, 요청 메타데이터, 캐시 동작에 제약이 있습니다. Claude Code에 “엣지 대응으로 바꿔줘”라고만 말하면 Node 전용 API를 섞거나, 언어별 캐시 키를 빠뜨리거나, 국가 코드를 결제와 권한 판단에 잘못 사용할 수 있습니다.

이 글은 2026년 6월 2일 기준으로 확인한 공식 문서를 바탕으로 Claude Code 프롬프트, Cloudflare Workers 복사 가능한 코드, Vercel Edge Middleware 예시, Deno Deploy로 옮길 때의 주의점, 검증 명령을 정리합니다. 공식 문서는 Cloudflare Workers, Workers Request API, Workers Cache API, Vercel Edge Runtime, Deno Deploy runtime, Claude Code overview를 확인하세요. 더 자세한 구현은 Claude Code Cloudflare Workers 가이드Claude Code Vercel Edge Functions 가이드를 함께 보면 좋습니다.

Edge에 둘 작업부터 정하기

첫 단계는 “무엇을 Edge에 둘 것인가”를 정하는 것입니다. URL, 헤더, 쿠키, 짧은 요청 본문만으로 결정할 수 있다면 Edge에 어울립니다. 반대로 깊은 데이터베이스 조회, 긴 LLM 호출, 이미지 변환, PDF 생성, 결제 확정, 백그라운드 작업은 일반 API나 큐에 남기는 편이 안전합니다.

사용 사례Edge에 맞는 이유다른 곳에 둘 작업
국가 또는 언어 라우팅국가 코드와 언어 헤더로 입구를 빠르게 고를 수 있음세금, 청구서, 재고 확정
짧은 캐시공개 응답을 사용자 가까이에서 재사용 가능DB 갱신, 재생성 작업
A/B 테스트 배정페이지 렌더링 전에 쿠키로 버킷 결정 가능통계 판단, 매출 분석
가벼운 인증 게이트프리뷰와 관리자 경로를 초기에 차단 가능세션 발급, 권한 감사
Webhook 입구 검증작은 서명 검증으로 잘못된 요청 차단 가능결제 처리, 메일, 재시도

이 표를 Claude Code 프롬프트에 넣으면 결과가 훨씬 검토하기 쉬워집니다. Edge 작업과 origin 작업이 코드 생성 전에 나뉘기 때문입니다.

flowchart LR
  User["User request"] --> Edge["Edge runtime"]
  Edge --> Geo["Country / language branch"]
  Edge --> Cache["Short cache"]
  Edge --> Gate["Light auth gate"]
  Edge --> Origin["Origin API / database"]
  Origin --> Queue["Queue or background work"]

Claude Code에 줄 프롬프트

엣지 구현에서는 “빠르게 만들어줘”보다 “어떤 제약을 지켜야 하는지”가 중요합니다. Claude Code는 코드를 빠르게 만들 수 있지만, 캐시 키, 지역 분기, secret 처리, 검증 명령은 명시하지 않으면 자주 빠집니다.

Implement a small Cloudflare Workers + TypeScript Edge runtime API.

Files:
- wrangler.toml
- src/index.ts

Requirements:
- GET /health returns JSON
- GET /api/edge-content returns locale and CTA copy based on country
- Use request.cf.country when Cloudflare provides it
- Also support a CF-IPCountry header for local curl verification
- Cache each locale separately for 60 seconds with the Cache API
- Return cache hit/miss through X-Edge-Cache
- Use Web APIs such as Request, Response, URL, and crypto; do not use Node-only APIs
- Include at least three curl or build commands that verify the behavior

Do not:
- Use pseudocode
- Hard-code API tokens or secrets
- Treat country code as proof for billing, permissions, or legal decisions

용어도 짧게 풀어 두면 좋습니다. runtime은 코드가 실행되는 환경, cache key는 캐시 응답을 찾는 이름, colo는 Cloudflare 데이터 센터 식별자입니다. 이런 설명을 먼저 주면 Claude Code가 초보자에게 맞는 주석과 인수인계 메모를 더 잘 씁니다.

Cloudflare Workers 복사 가능한 예제

아래 Worker는 의도적으로 작게 만들었습니다. /api/edge-content는 Cloudflare 요청 메타데이터 또는 로컬 테스트 헤더에서 국가를 읽고, 국가를 locale로 매핑한 뒤 Cache API에 60초 동안 저장합니다. 로컬에서는 CF-IPCountry 헤더를 curl로 넘겨 지역 분기를 확인할 수 있습니다.

name = "claude-edge-router"
main = "src/index.ts"
compatibility_date = "2026-06-02"

[vars]
DEFAULT_LOCALE = "en"
// src/index.ts
export interface Env {
  DEFAULT_LOCALE: string;
}

type Locale = "ja" | "en" | "zh" | "ko" | "es" | "fr" | "de" | "pt" | "hi" | "id";

type CfRequest = Request & {
  cf?: {
    country?: string;
    colo?: string;
    city?: string;
  };
};

const SUPPORTED_LOCALES = new Set<Locale>([
  "ja",
  "en",
  "zh",
  "ko",
  "es",
  "fr",
  "de",
  "pt",
  "hi",
  "id",
]);

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      return json({ ok: true, runtime: "cloudflare-workers" });
    }

    if (url.pathname === "/api/edge-content") {
      return handleEdgeContent(request, env, ctx);
    }

    return json({ error: "not_found" }, 404);
  },
} satisfies ExportedHandler<Env>;

async function handleEdgeContent(
  request: Request,
  env: Env,
  ctx: ExecutionContext
): Promise<Response> {
  const url = new URL(request.url);
  const country = getCountry(request);
  const locale = localeFromCountry(country, env.DEFAULT_LOCALE);
  const cacheKey = buildCacheKey(request, locale);
  const cached = await caches.default.match(cacheKey);

  if (cached) {
    return withHeaders(cached, {
      "X-Edge-Cache": "HIT",
      "X-Edge-Locale": locale,
    });
  }

  const body = {
    locale,
    country,
    colo: getColo(request),
    path: url.pathname,
    cta: localizedCta(locale),
    generatedAt: new Date().toISOString(),
  };

  const response = json(body, 200, {
    "Cache-Control": "public, max-age=0, s-maxage=60, stale-while-revalidate=300",
    "X-Edge-Cache": "MISS",
    "X-Edge-Locale": locale,
  });

  ctx.waitUntil(caches.default.put(cacheKey, response.clone()));
  return response;
}

function buildCacheKey(request: Request, locale: Locale): Request {
  const url = new URL(request.url);
  url.search = "";
  url.pathname = `/__edge-cache/${locale}${url.pathname}`;
  return new Request(url.toString(), { method: "GET" });
}

function getCountry(request: Request): string {
  const cf = (request as CfRequest).cf;
  return (
    request.headers.get("CF-IPCountry") ||
    request.headers.get("x-country") ||
    cf?.country ||
    "US"
  ).toUpperCase();
}

function getColo(request: Request): string {
  return (request as CfRequest).cf?.colo || "local";
}

function localeFromCountry(country: string, fallback: string): Locale {
  const normalizedFallback = SUPPORTED_LOCALES.has(fallback as Locale)
    ? (fallback as Locale)
    : "en";

  switch (country) {
    case "JP":
      return "ja";
    case "CN":
    case "TW":
    case "HK":
      return "zh";
    case "KR":
      return "ko";
    case "ES":
    case "MX":
    case "AR":
      return "es";
    case "FR":
      return "fr";
    case "DE":
      return "de";
    case "BR":
    case "PT":
      return "pt";
    case "IN":
      return "hi";
    case "ID":
      return "id";
    default:
      return normalizedFallback;
  }
}

function localizedCta(locale: Locale): string {
  const messages: Record<Locale, string> = {
    ja: "Claude Code教材を見る",
    en: "Explore Claude Code templates",
    zh: "查看 Claude Code 教材",
    ko: "Claude Code 템플릿 보기",
    es: "Ver plantillas de Claude Code",
    fr: "Voir les modèles Claude Code",
    de: "Claude Code Vorlagen ansehen",
    pt: "Ver modelos de Claude Code",
    hi: "Claude Code टेम्पलेट देखें",
    id: "Lihat template Claude Code",
  };
  return messages[locale];
}

function json(data: unknown, status = 200, headers: HeadersInit = {}): Response {
  return new Response(JSON.stringify(data, null, 2), {
    status,
    headers: {
      "Content-Type": "application/json; charset=utf-8",
      "X-Content-Type-Options": "nosniff",
      ...headers,
    },
  });
}

function withHeaders(response: Response, headers: Record<string, string>): Response {
  const next = new Response(response.body, response);
  for (const [key, value] of Object.entries(headers)) {
    next.headers.set(key, value);
  }
  return next;
}

핵심은 캐시 키에 locale을 넣는 것입니다. 그렇지 않으면 일본어 CTA가 영어 사용자에게 재사용될 수 있습니다. 동시에 쿼리 문자열을 제거해서 utm_source 같은 광고 파라미터가 캐시를 불필요하게 쪼개지 않도록 했습니다.

Vercel과 Deno로 확장하기

Vercel Edge Middleware는 페이지 렌더링 전에 헤더를 붙이고, A/B 버킷을 저장하고, 국가별 진입 경로를 리다이렉트하는 데 좋습니다. 다만 Edge Runtime 제약을 확인하고 Node 전용 API가 필요한 패키지는 피해야 합니다.

// middleware.ts
import { NextRequest, NextResponse } from "next/server";

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};

export function middleware(request: NextRequest) {
  const country = request.headers.get("x-vercel-ip-country") || "US";
  const bucket = request.cookies.get("edge_bucket")?.value || chooseBucket();
  const response = NextResponse.next();

  response.headers.set("x-edge-country", country);
  response.headers.set("x-edge-bucket", bucket);
  response.cookies.set("edge_bucket", bucket, {
    maxAge: 60 * 60 * 24 * 30,
    sameSite: "lax",
    secure: true,
  });

  if (country === "JP" && request.nextUrl.pathname === "/pricing") {
    return NextResponse.redirect(new URL("/jp/pricing", request.url));
  }

  return response;
}

function chooseBucket(): "a" | "b" {
  const bytes = new Uint8Array(1);
  crypto.getRandomValues(bytes);
  return bytes[0] < 128 ? "a" : "b";
}

Deno Deploy에서는 같은 Web API 형태를 유지하되 Cloudflare 전용 필드인 request.cf를 그대로 복사하지 않습니다. 공급자별 헤더와 환경 변수가 다르므로 프롬프트에 명확히 써야 합니다.

// main.ts
Deno.serve((request: Request) => {
  const url = new URL(request.url);
  const country = request.headers.get("x-country") || "US";

  if (url.pathname !== "/api/edge-content") {
    return Response.json({ error: "not_found" }, { status: 404 });
  }

  return Response.json({
    runtime: "deno-deploy",
    country,
    message: country === "JP" ? "Japan entry" : "Default edge entry",
  });
});

검증 명령

엣지 코드는 타입, 로컬 HTTP, 지역 분기, 캐시 헤더, 배포 전 설정을 확인해야 합니다. Claude Code에도 “구현 완료”만 말하지 말고 이 증거를 남기라고 요청하세요.

npm create cloudflare@latest claude-edge-router -- --type=hello-world
cd claude-edge-router
npm install -D typescript wrangler
npx wrangler types
npx tsc --noEmit
npx wrangler dev

다른 터미널에서 curl을 실행합니다.

curl -i http://127.0.0.1:8787/health
curl -i -H "CF-IPCountry: JP" http://127.0.0.1:8787/api/edge-content
curl -i -H "CF-IPCountry: US" http://127.0.0.1:8787/api/edge-content
curl -i -H "CF-IPCountry: JP" "http://127.0.0.1:8787/api/edge-content?utm_source=test"
npx wrangler deploy --dry-run

기대 결과는 단순합니다. 같은 locale과 path의 첫 요청은 X-Edge-Cache: MISS, 다음 요청은 HIT입니다. JP는 ja, US는 en으로 매핑됩니다. utm_source가 붙어도 별도 캐시 항목을 만들지 않습니다.

자주 생기는 함정

첫 번째는 너무 많은 일을 Edge에 올리는 것입니다. 긴 LLM 호출, ORM 중심 DB 처리, PDF 생성, 이미지 변환, 결제 흐름은 일반 API나 백그라운드 큐가 더 적합합니다. Edge는 전체 백엔드가 아니라 입구 게이트입니다.

두 번째는 캐시 키가 너무 넓거나 너무 좁은 경우입니다. locale, 로그인 상태, 가격 표시, 실험 버킷이 응답을 바꾼다면 키에 들어가야 합니다. 반대로 utm_* 같은 잡음까지 키에 넣으면 캐시는 거의 맞지 않습니다.

세 번째는 지역 정보를 과신하는 것입니다. Cloudflare의 request.cf.country나 Vercel의 x-vercel-ip-country는 초기 경험을 정하는 데 유용하지만 세금, 나이, 권한, 결제의 증거가 아닙니다. VPN, 회사 프록시, CDN 라우팅이 값을 바꿀 수 있습니다.

네 번째는 Claude Code에 플랫폼 이름만 주는 것입니다. “Workers로 만들어줘”는 Cache API, KV, D1, Vercel 호환성, origin fallback 중 무엇을 원하는지 말하지 않습니다. 경계를 정할 때는 Claude Code serverless functions성능 최적화 가이드도 함께 확인하세요.

수익 동선과 다음 단계

다국어 콘텐츠 사이트에서도 Edge runtime은 수익 동선에 도움이 됩니다. 무료 PDF 문구를 언어별로 바꾸고, 독자를 가까운 언어의 상품 CTA로 보내며, 캐시 전에 광고 파라미터를 정리할 수 있습니다.

재사용 가능한 구현 자료가 필요하다면 ClaudeCodeLab 상품 목록을 확인하세요. 팀에서 Claude Code 프롬프트, 리뷰 기준, Edge와 Node의 경계를 정리하고 싶다면 Claude Code 교육 및 도입 상담이 적합합니다. 먼저 짧은 흐름을 보고 싶다면 무료 치트시트부터 시작하세요.

실제로 시험한 결과

이번 업데이트에서 Masa는 로컬 Workers 샘플로 CF-IPCountry: JPCF-IPCountry: US를 바꿔 보며 locale, X-Edge-Cache, 광고 파라미터를 제거한 캐시 키를 확인했습니다. 검증 중에 알게 된 점은 로컬에서는 테스트용 헤더를 request.cf.country보다 먼저 읽어야 국가 분기 확인이 안정적이라는 것입니다. 캐시 키도 /__edge-cache/${locale}${pathname} 형태로 두어 지역별 응답이 섞이지 않게 했습니다. Claude Code에 맡길 때는 헤더 우선순위, 지역 분기, 캐시 키를 함께 리뷰하라고 적는 편이 안전합니다.

정리

엣지 컴퓨팅은 모든 것을 Edge로 옮기는 일이 아닙니다. 입구에서 빠르게 판단할 수 있는 로직만 가까운 곳에 두고, 무거운 처리와 최종 판단은 일반 API에 남기는 설계입니다. Claude Code에는 runtime 제약, 캐시 키 규칙, 지역 분기, 검증 명령을 처음부터 전달하세요. 그렇게 해야 빠르면서도 리뷰하기 쉬운 Edge 구현이 됩니다.

#Claude Code #edge computing #Cloudflare Workers #Vercel #performance
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.