AICosmus

Where tech meets the everyday — AI, fintech, swimming, and cars.
AI 에이전트 클라우드 인프라 개념 일러스트

[Cloudflare 완전 정복: 입문부터 2026 AI 에이전트까지] 13/16화: Agents Week 2026 총정리: Dynamic Workers·Sandboxes·Artifacts 완전 가이드

이 글은 「Cloudflare 완전 정복」 시리즈 13회입니다. 지난 12회에서 Workers AI를 통해 엣지에서 LLM을 호출하는 방법을 다뤘다면, 이번 회부터는 시리즈의 하이라이트인 Phase 4 — 2026 AI 에이전트 시대에 진입합니다. Cloudflare가 2026년 5월에 개최한 Agents Week에서 쏟아진 신기능들을 한 회에 총정리합니다.

Agents Week 2026, 무엇이 달라졌나

Cloudflare는 매년 테마 주간(Theme Week)을 통해 대규모 신기능을 발표해왔습니다. Developer Week, Birthday Week, Security Week 등이 대표적이죠. 그런데 2026년 5월, Cloudflare는 전례 없는 주간을 열었습니다. 바로 Agents Week입니다.

이름에서 느껴지듯, 이번 주간은 AI 에이전트를 위한 인프라에 올인한 발표의 연속이었습니다. 단순히 LLM API를 호출하는 수준을 넘어, AI 에이전트가 스스로 코드를 작성하고, 파일을 생성하고, 프로세스를 실행하고, 네트워크를 구성하고, 결과물을 버전 관리까지 하는 — 그야말로 “에이전트가 살 수 있는 집”을 만드는 데 초점을 맞췄습니다.

일주일 동안 발표된 주요 내용을 먼저 한눈에 정리해보겠습니다.

발표일 기능 핵심 요약 상태
월요일 Dynamic Workers 런타임에 Workers를 동적 생성·삭제 GA
월요일 Artifacts Git 호환 버전 스토리지 Open Beta
화요일 Sandboxes 셸·파일시스템·영속 환경 GA
수요일 Workflows v2 5만 동시 워크플로우, 장기 실행 GA
수요일 Cloudflare Mesh 에이전트용 사설 네트워크 Open Beta
목요일 Workers VPC Workers 간 사설망 통신 Open Beta
목요일 Agent Readiness Score 사이트의 AI 에이전트 대응도 점수 GA
금요일 Anthropic 통합 Claude Managed Agents on Cloudflare Preview

발표 하나하나가 독립 블로그 포스트 분량인데, 이번 13회에서는 가장 핵심인 Dynamic Workers, Sandboxes, Artifacts 세 가지를 집중 해부하고, 나머지는 맥락과 함께 훑겠습니다. MCP Server Portals와 Zero Trust 에이전트 보안은 다음 14회에서 깊이 다룹니다.

Agents Week 2026 핵심 아키텍처 다이어그램

Dynamic Workers — 코드가 코드를 배포하는 시대

왜 Dynamic Workers가 필요한가

기존 Workers는 사람이 wrangler deploy를 실행해 배포하는 구조였습니다. 개발자가 코드를 작성하고, CI/CD 파이프라인이 돌고, 정적으로 배포된 Worker가 요청을 처리합니다. 11회에서 Wrangler CLI를 다룰 때 이 흐름을 자세히 살펴봤죠.

그런데 AI 에이전트 시대에는 이 패러다임이 근본적으로 바뀝니다. 에이전트가 사용자 요청에 따라 런타임에 새로운 코드를 생성하고, 그 코드를 즉시 배포해 실행할 수 있어야 합니다. 예를 들어:

  • 사용자: “매일 오전 9시에 경쟁사 가격을 크롤링해서 슬랙으로 알려줘”
  • 에이전트: 크롤링 로직을 Workers 코드로 생성 → 배포 → Cron Trigger 연결 → 슬랙 웹훅 설정

이 전체 과정을 사람의 개입 없이, 에이전트가 API 호출 한 번으로 처리하는 것. 이것이 Dynamic Workers의 존재 이유입니다.

핵심 개념: Dispatch Namespace + 동적 생성

Dynamic Workers는 기존의 Workers for Platforms(구 Dispatch Workers)를 기반으로 확장된 기능입니다. 핵심 개념을 정리하면:

  • Dispatch Namespace: 동적으로 생성되는 Workers가 소속되는 논리적 컨테이너입니다. 하나의 네임스페이스 안에 수천 개의 Worker를 동적으로 생성·삭제할 수 있습니다.
  • Dispatcher Worker: 들어오는 요청을 분석해 적절한 동적 Worker로 라우팅하는 게이트웨이 역할의 Worker입니다. 이것만 정적으로 배포하면 됩니다.
  • User Worker: Dispatch Namespace 안에 동적으로 생성되는 개별 Worker입니다. 에이전트가 만든 코드가 여기에 배포됩니다.

이를 코드로 보면 더 명확합니다. 먼저, Dispatcher Worker의 구조:

// dispatcher-worker/src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // 요청 URL에서 동적 Worker 이름 추출
    const url = new URL(request.url);
    const workerName = url.pathname.split('/')[1]; // e.g., /my-agent-task/...

    if (!workerName) {
      return new Response('Worker name required', { status: 400 });
    }

    // Dispatch Namespace에서 해당 Worker를 가져와 실행
    const userWorker = env.DISPATCH_NS.get(workerName);
    return userWorker.fetch(request);
  }
} satisfies ExportedHandler<Env>;

interface Env {
  DISPATCH_NS: DispatchNamespace;
}

그리고 wrangler.toml에서 Dispatch Namespace를 바인딩합니다:

# dispatcher-worker/wrangler.toml
name = "dispatcher"
main = "src/index.ts"
compatibility_date = "2026-05-15"

[[dispatch_namespaces]]
binding = "DISPATCH_NS"
namespace = "agent-workers"

동적 Worker 생성 API

이제 핵심입니다. 에이전트가 새 Worker를 동적으로 만드는 API 호출:

# Dynamic Worker 생성 — Cloudflare API
curl -X PUT \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/agent-workers/scripts/price-crawler-001" \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/javascript" \
  --data '
export default {
  async fetch(request) {
    const prices = await crawlCompetitorPrices();
    await sendToSlack(prices);
    return new Response(JSON.stringify(prices), {
      headers: { "Content-Type": "application/json" }
    });
  },

  async scheduled(event, env, ctx) {
    const prices = await crawlCompetitorPrices();
    await sendToSlack(prices);
  }
};

async function crawlCompetitorPrices() {
  // 크롤링 로직
  const response = await fetch("https://competitor.example.com/api/prices");
  return response.json();
}

async function sendToSlack(data) {
  await fetch("https://hooks.slack.com/services/...", {
    method: "POST",
    body: JSON.stringify({ text: JSON.stringify(data, null, 2) })
  });
}
'

이 API 한 번으로 Worker가 즉시 배포됩니다. wrangler deploy도 필요 없고, CI/CD 파이프라인도 필요 없습니다. 에이전트가 코드를 생성하고, API를 호출하면, 수 초 내에 전 세계 330개 이상의 엣지 PoP에서 실행 가능한 상태가 됩니다.

동적 Worker 관리: 조회·삭제·업데이트

생성된 동적 Worker를 관리하는 것도 간단합니다:

# 네임스페이스 내 모든 동적 Worker 목록 조회
curl -X GET \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/agent-workers/scripts" \
  -H "Authorization: Bearer {api_token}"

# 특정 동적 Worker 삭제
curl -X DELETE \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/agent-workers/scripts/price-crawler-001" \
  -H "Authorization: Bearer {api_token}"

# 동적 Worker 코드 업데이트 (PUT으로 덮어쓰기)
curl -X PUT \
  "https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/dispatch/namespaces/agent-workers/scripts/price-crawler-001" \
  -H "Authorization: Bearer {api_token}" \
  -H "Content-Type: application/javascript" \
  --data 'export default { async fetch(req) { return new Response("updated v2"); } }'

보안 격리: Outbound Worker 패턴

에이전트가 생성한 코드를 그대로 실행한다? 보안 관점에서 당연히 우려됩니다. Cloudflare는 이를 위해 Outbound Worker 패턴을 제공합니다:

// outbound-worker/src/index.ts — 동적 Worker의 외부 요청을 감시·제한
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // 허용된 도메인만 외부 요청 허용
    const allowedDomains = ['api.slack.com', 'hooks.slack.com', 'competitor.example.com'];
    if (!allowedDomains.some(d => url.hostname.endsWith(d))) {
      return new Response('Blocked: unauthorized external request', { status: 403 });
    }

    // 허용된 요청은 그대로 전달
    return fetch(request);
  }
} satisfies ExportedHandler<Env>;

이 패턴을 적용하면 동적 Worker가 아무 외부 서비스나 호출하는 것을 막을 수 있습니다. Dispatch Namespace 설정에서 Outbound Worker를 지정하면, 해당 네임스페이스의 모든 동적 Worker의 fetch() 호출이 Outbound Worker를 경유합니다.

실전: Wrangler로 Dispatch Namespace 설정하기

홈랩 환경에서 직접 따라 해보겠습니다. Mac Studio 또는 Synology NAS의 터미널에서:

# 1. Dispatch Namespace 생성
npx wrangler dispatch-namespace create agent-workers

# 2. Dispatcher Worker 프로젝트 초기화
mkdir dispatcher-worker && cd dispatcher-worker
npx wrangler init --yes

# 3. wrangler.toml에 Dispatch Namespace 바인딩 추가
cat > wrangler.toml <<'EOF'
name = "dispatcher"
main = "src/index.ts"
compatibility_date = "2026-05-15"

[[dispatch_namespaces]]
binding = "DISPATCH_NS"
namespace = "agent-workers"
EOF

# 4. Dispatcher Worker 코드 작성 (위의 예제 코드 사용)

# 5. 배포
npx wrangler deploy

# 6. 동적 Worker 업로드 테스트
npx wrangler dispatch-namespace upload agent-workers hello-world \
  --script "export default { async fetch() { return new Response('Hello from dynamic worker!'); } }"

# 7. 테스트 호출
curl https://dispatcher.{your-subdomain}.workers.dev/hello-world

마지막 curl에서 “Hello from dynamic worker!”가 반환되면 성공입니다. 에이전트가 6번 단계를 API로 자동화하면, 코드가 코드를 배포하는 파이프라인이 완성됩니다.

Cloudflare Sandboxes 생명주기 흐름도

Sandboxes GA — 에이전트에게 컴퓨터를 통째로 주다

Workers의 한계, 그리고 Sandboxes의 등장

Workers는 빠르고 가볍지만, 근본적인 제약이 있습니다. V8 isolate 기반이라 셸 명령을 실행할 수 없고, 파일시스템이 없고, 장기 실행 프로세스를 띄울 수 없습니다. “5초 안에 HTTP 응답을 돌려주는” 용도에는 최적이지만, AI 에이전트가 하는 작업 — 코드를 작성하고, 빌드하고, 테스트하고, 결과를 파일로 저장하는 — 에는 턱없이 부족합니다.

Cloudflare Sandboxes는 이 간극을 메웁니다. 한마디로 정의하면:

Sandboxes = 에이전트 전용 격리 컴퓨팅 환경. 셸, 파일시스템, 백그라운드 프로세스, 네트워크 스택을 모두 갖춘 일시적(또는 영속적) 컨테이너로, Workers에서 API 호출 한 번으로 생성·접속·폐기할 수 있다.

기존 클라우드 서비스로 비유하면 AWS Fargate나 Google Cloud Run과 비슷하지만, 결정적 차이가 있습니다:

  • 콜드 스타트 없음: Cloudflare의 글로벌 네트워크에서 수 밀리초 내에 Sandbox가 준비됩니다.
  • Workers와 네이티브 통합: Worker 코드에서 env.SANDBOX.create() 한 줄이면 Sandbox가 뜹니다.
  • 자동 정리: TTL(Time-To-Live) 설정으로 사용이 끝난 Sandbox를 자동 폐기합니다.
  • WebSocket 셸 접속: 에이전트가 Sandbox의 셸에 직접 명령을 보내고 출력을 실시간으로 받습니다.

Sandboxes 아키텍처

Sandboxes의 내부 구조를 이해하면 활용 범위가 넓어집니다:

┌─────────────────────────────────────────────────┐
│              Worker (Orchestrator)                │
│                                                   │
│  const sandbox = await env.SANDBOX.create({      │
│    template: "node-20",                           │
│    ttl: 3600,                                     │
│    memory: 512                                    │
│  });                                              │
│                                                   │
│  // WebSocket으로 셸 명령 실행                      │
│  const result = await sandbox.exec("npm install");│
│  const output = await sandbox.exec("npm test");   │
│                                                   │
│  // 파일 읽기/쓰기                                  │
│  await sandbox.writeFile("/app/index.js", code);  │
│  const logs = await sandbox.readFile("/app/out"); │
│                                                   │
│  // 백그라운드 프로세스                              │
│  await sandbox.exec("node server.js &");          │
│                                                   │
│  // 정리                                           │
│  await sandbox.destroy();                          │
└─────────────┬───────────────────────────────────┘
              │ Internal API (Cloudflare edge)
              ▼
┌─────────────────────────────────────────────────┐
│            Sandbox Instance (gVisor)             │
│  ┌─────────────────────────────────────────────┐ │
│  │  /bin/sh  ← WebSocket shell                 │ │
│  │  /app/    ← 에이전트 작업 디렉토리            │ │
│  │  Node.js 20 / Python 3.12 / Go 1.22         │ │
│  │  네트워크: egress 허용, ingress 차단          │ │
│  │  메모리: 256MB ~ 2GB (설정 가능)              │ │
│  │  TTL: 자동 만료 후 폐기                       │ │
│  └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘

보안 격리는 Google이 개발한 gVisor 커널을 사용합니다. 각 Sandbox는 독립된 커널 시스콜 인터셉션 레이어를 가지므로, 하나의 Sandbox가 뚫리더라도 호스트나 다른 Sandbox에 영향을 줄 수 없습니다.

Sandboxes API 상세

Worker에서 Sandbox를 사용하는 전체 API를 살펴보겠습니다:

// sandbox-demo/src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // 1. Sandbox 생성
    const sandbox = await env.SANDBOX.create({
      template: "node-20",        // 사전 구성된 템플릿
      ttl: 1800,                  // 30분 후 자동 폐기
      memory: 512,                // 512MB 메모리
      metadata: {                 // 사용자 정의 메타데이터
        agent: "code-review-bot",
        taskId: "task-abc-123"
      }
    });

    try {
      // 2. 코드 파일 작성
      const userCode = await request.text();
      await sandbox.writeFile("/app/src/solution.js", userCode);

      // 3. package.json 작성
      await sandbox.writeFile("/app/package.json", JSON.stringify({
        name: "agent-task",
        scripts: { test: "jest", lint: "eslint src/" },
        dependencies: { jest: "^29.0.0", eslint: "^9.0.0" }
      }));

      // 4. 의존성 설치
      const installResult = await sandbox.exec("cd /app && npm install", {
        timeout: 60000  // 60초 타임아웃
      });

      if (installResult.exitCode !== 0) {
        return Response.json({
          success: false,
          error: "npm install failed",
          stderr: installResult.stderr
        }, { status: 500 });
      }

      // 5. 린트 실행
      const lintResult = await sandbox.exec("cd /app && npm run lint", {
        timeout: 30000
      });

      // 6. 테스트 실행
      const testResult = await sandbox.exec("cd /app && npm test", {
        timeout: 60000
      });

      // 7. 결과 파일 읽기 (테스트 커버리지 등)
      let coverage = null;
      try {
        coverage = await sandbox.readFile("/app/coverage/coverage-summary.json");
      } catch {
        // 커버리지 파일이 없을 수 있음
      }

      return Response.json({
        success: testResult.exitCode === 0,
        lint: {
          passed: lintResult.exitCode === 0,
          output: lintResult.stdout
        },
        test: {
          passed: testResult.exitCode === 0,
          output: testResult.stdout,
          coverage: coverage ? JSON.parse(coverage) : null
        }
      });

    } finally {
      // 8. Sandbox 명시적 폐기 (TTL 전에 정리)
      await sandbox.destroy();
    }
  }
} satisfies ExportedHandler<Env>;

interface Env {
  SANDBOX: SandboxNamespace;
}

wrangler.toml 바인딩 설정:

# sandbox-demo/wrangler.toml
name = "code-review-agent"
main = "src/index.ts"
compatibility_date = "2026-05-15"

[sandbox]
binding = "SANDBOX"

# 사용할 템플릿 정의 (선택: 사전 정의 템플릿 사용 시 생략 가능)
[[sandbox.templates]]
name = "node-20"
image = "node:20-slim"
default_ttl = 1800
max_memory = 1024

사용 가능한 Sandbox 템플릿

Agents Week 시점에 제공되는 공식 템플릿은 다음과 같습니다:

템플릿 이름 포함 런타임 기본 메모리 주 용도
node-20 Node.js 20 LTS, npm, yarn 256MB 프론트엔드 빌드, API 프로토타이핑
node-22 Node.js 22, npm, pnpm 256MB 최신 Node.js 기능 활용
python-312 Python 3.12, pip, venv 256MB 데이터 분석, 스크립팅
python-ml Python 3.12 + numpy, pandas, scikit-learn 512MB ML 전처리, 분석
go-122 Go 1.22 256MB 시스템 도구, CLI 빌드
rust-nightly Rust nightly, cargo 512MB Wasm 컴파일, 시스템 프로그래밍
base Alpine Linux, sh, curl, git 128MB 범용 스크립팅

커스텀 템플릿도 정의할 수 있지만, 대부분의 에이전트 작업은 공식 템플릿으로 충분합니다.

Sandboxes vs 기존 솔루션 비교

AI 에이전트용 격리 환경은 Cloudflare만 제공하는 것이 아닙니다. 기존 솔루션과의 차이를 비교해보겠습니다:

항목 Cloudflare Sandboxes AWS Fargate Fly Machines E2B (전문 샌드박스)
콜드 스타트 ~50ms ~30초 ~300ms ~150ms
Workers 통합 네이티브 별도 SDK 별도 API 별도 SDK
글로벌 엣지 330+ PoP 리전 단위 30+ 리전 US/EU 한정
WebSocket 셸 기본 제공 ECS Exec fly ssh 기본 제공
자동 정리(TTL) 기본 제공 수동 설정 자동 중지 기본 제공
과금 단위 벽시계 초 vCPU·메모리/초 머신/초 크레딧/분
무료 티어 있음 없음 있음 있음

Cloudflare Sandboxes의 가장 큰 장점은 Workers 에코시스템과의 네이티브 통합입니다. Worker에서 Sandbox를 만들고, Sandbox 안에서 작업한 결과를 R2에 저장하고, D1에 기록하고, 다시 Worker로 응답을 돌려보내는 — 이 모든 것이 하나의 플랫폼 안에서 일관된 API로 이뤄집니다.

Artifacts — 에이전트 산출물의 Git

에이전트는 무엇을 “만드는가”

AI 에이전트가 코드를 작성하고, 문서를 생성하고, 이미지를 만들고, 데이터를 분석할 때, 그 결과물(artifact)을 어디에 저장할까요? 기존에는 R2(오브젝트 스토리지)에 파일로 던져놓거나, KV에 텍스트를 저장하거나, D1 테이블에 끼워 넣는 식이었습니다. 하지만 에이전트 산출물에는 고유한 요구사항이 있습니다:

  • 버전 관리: 에이전트가 코드를 반복 수정할 때, 이전 버전으로 돌아갈 수 있어야 합니다.
  • 차이 비교: v2와 v3 사이에 뭐가 바뀌었는지 사람이 검토할 수 있어야 합니다.
  • 분기(branching): 여러 접근법을 동시에 시도하고, 가장 좋은 것을 선택할 수 있어야 합니다.
  • 메타데이터: 누가(어떤 에이전트가), 언제, 왜 이 산출물을 만들었는지 추적해야 합니다.

이 모든 요구사항을 하나로 정리하면? Git이 됩니다. Cloudflare Artifacts는 정확히 이 발상에서 출발합니다.

Artifacts Git 호환 버전 관리 워크플로우

Artifacts 핵심 개념

Artifacts는 Workers 바인딩으로 사용하는 Git 호환 버전 스토리지입니다. 핵심 특징:

  • Git 프로토콜 호환: git clone, git push, git pull로 직접 접근 가능합니다.
  • Workers 네이티브 API: Worker 코드 안에서 프로그래밍 방식으로 파일 생성·수정·커밋·분기를 다룹니다.
  • Cloudflare 엣지 저장: 데이터가 Cloudflare의 글로벌 스토리지에 저장되므로 별도 Git 서버가 필요 없습니다.
  • R2 백엔드: 내부적으로 R2 위에 Git 오브젝트 포맷을 구현했으므로, R2의 무료 송신 혜택을 그대로 누립니다.

Artifacts API 사용법

Worker에서 Artifacts를 사용하는 코드를 살펴보겠습니다:

// artifact-demo/src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const action = url.pathname.split('/')[1];

    switch (action) {
      case 'create': return handleCreate(request, env);
      case 'update': return handleUpdate(request, env);
      case 'history': return handleHistory(request, env);
      case 'diff': return handleDiff(request, env);
      default: return new Response('Not found', { status: 404 });
    }
  }
} satisfies ExportedHandler<Env>;

// 1. 새 Artifact 생성 (초기 커밋)
async function handleCreate(request: Request, env: Env): Promise<Response> {
  const { name, content, message } = await request.json() as {
    name: string; content: string; message: string;
  };

  const repo = await env.ARTIFACTS.get(name);

  // 파일 추가 + 커밋
  await repo.writeFile("main.py", content);
  await repo.commit({
    message: message || "Initial creation by agent",
    author: {
      name: "code-agent",
      email: "[email protected]"
    }
  });

  return Response.json({
    success: true,
    artifact: name,
    commitHash: await repo.head()
  });
}

// 2. 기존 Artifact 업데이트 (새 커밋)
async function handleUpdate(request: Request, env: Env): Promise<Response> {
  const { name, content, message } = await request.json() as {
    name: string; content: string; message: string;
  };

  const repo = await env.ARTIFACTS.get(name);

  // 분기 생성 (선택적)
  // await repo.createBranch("experiment-v2");
  // await repo.checkout("experiment-v2");

  await repo.writeFile("main.py", content);
  await repo.commit({
    message: message || "Updated by agent",
    author: { name: "code-agent", email: "[email protected]" }
  });

  return Response.json({
    success: true,
    artifact: name,
    commitHash: await repo.head()
  });
}

// 3. 커밋 히스토리 조회
async function handleHistory(request: Request, env: Env): Promise<Response> {
  const url = new URL(request.url);
  const name = url.searchParams.get("name");
  if (!name) return new Response("name required", { status: 400 });

  const repo = await env.ARTIFACTS.get(name);
  const log = await repo.log({ limit: 20 });

  return Response.json({
    artifact: name,
    commits: log.map(entry => ({
      hash: entry.hash,
      message: entry.message,
      author: entry.author,
      date: entry.date
    }))
  });
}

// 4. 두 커밋 간 차이 비교
async function handleDiff(request: Request, env: Env): Promise<Response> {
  const url = new URL(request.url);
  const name = url.searchParams.get("name");
  const from = url.searchParams.get("from");
  const to = url.searchParams.get("to");

  if (!name || !from || !to) {
    return new Response("name, from, to required", { status: 400 });
  }

  const repo = await env.ARTIFACTS.get(name);
  const diff = await repo.diff(from, to);

  return Response.json({ artifact: name, from, to, diff });
}

interface Env {
  ARTIFACTS: ArtifactNamespace;
}

wrangler.toml 설정:

# artifact-demo/wrangler.toml
name = "artifact-manager"
main = "src/index.ts"
compatibility_date = "2026-05-15"

[artifacts]
binding = "ARTIFACTS"

Artifacts + Sandboxes 연동: 코드 생성 → 실행 → 버전 관리 파이프라인

Artifacts의 진짜 위력은 Sandboxes와 결합할 때 드러납니다. 에이전트가 코드를 생성하고, Sandbox에서 테스트하고, 결과를 Artifact로 버전 관리하는 전체 파이프라인을 구현해보겠습니다:

// agent-pipeline/src/index.ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { prompt, artifactName } = await request.json() as {
      prompt: string;
      artifactName: string;
    };

    // Step 1: Workers AI로 코드 생성
    const aiResponse = await env.AI.run("@cf/meta/llama-3.3-70b-instruct-fp8-fast", {
      messages: [
        { role: "system", content: "You are a Python developer. Write clean, tested code." },
        { role: "user", content: prompt }
      ]
    });
    const generatedCode = aiResponse.response;

    // Step 2: Sandbox에서 코드 실행·테스트
    const sandbox = await env.SANDBOX.create({
      template: "python-312",
      ttl: 300  // 5분
    });

    try {
      // 코드 파일 쓰기
      await sandbox.writeFile("/app/solution.py", generatedCode);
      await sandbox.writeFile("/app/test_solution.py",
        `import pytest\nfrom solution import *\n# auto-generated tests`
      );

      // 실행
      const runResult = await sandbox.exec("cd /app && python solution.py", {
        timeout: 30000
      });

      // 린트
      await sandbox.exec("pip install ruff", { timeout: 30000 });
      const lintResult = await sandbox.exec("cd /app && ruff check solution.py", {
        timeout: 10000
      });

      // Step 3: 성공 시 Artifact에 버전 저장
      const repo = await env.ARTIFACTS.get(artifactName);
      await repo.writeFile("solution.py", generatedCode);
      await repo.writeFile("output.txt", runResult.stdout);
      await repo.writeFile("metadata.json", JSON.stringify({
        prompt,
        model: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
        lintPassed: lintResult.exitCode === 0,
        runExitCode: runResult.exitCode,
        timestamp: new Date().toISOString()
      }));

      await repo.commit({
        message: `Agent: ${prompt.slice(0, 72)}`,
        author: { name: "pipeline-agent", email: "[email protected]" }
      });

      return Response.json({
        success: true,
        artifactName,
        commitHash: await repo.head(),
        output: runResult.stdout,
        lint: lintResult.exitCode === 0 ? "passed" : lintResult.stdout
      });

    } finally {
      await sandbox.destroy();
    }
  }
} satisfies ExportedHandler<Env>;

interface Env {
  AI: Ai;
  SANDBOX: SandboxNamespace;
  ARTIFACTS: ArtifactNamespace;
}

이 코드 하나로 LLM 코드 생성 → 격리 환경 실행 → 버전 관리 저장이라는 에이전트 핵심 파이프라인이 완성됩니다. Workers AI(12회), Sandboxes, Artifacts를 모두 결합한 종합 예제입니다.

Git 클라이언트로 직접 접근

Artifacts는 Git 프로토콜 호환이므로, 에이전트가 만든 산출물을 개발자가 로컬에서 직접 확인할 수도 있습니다:

# Mac Studio 또는 Synology NAS 터미널에서
git clone https://artifacts.{your-account}.workers.dev/my-agent-project
cd my-agent-project

# 에이전트의 커밋 히스토리 확인
git log --oneline
# a3f2c1b Agent: Implement CSV parser with error handling
# 8d4e5a2 Agent: Add data validation module
# 1b2c3d4 Agent: Initial project scaffolding

# 특정 버전 간 차이 확인
git diff 8d4e5a2 a3f2c1b

# 에이전트 코드를 로컬에서 수정 후 다시 푸시
git push origin main

이 양방향성이 Artifacts의 핵심 강점입니다. 에이전트가 만들고, 사람이 검토하고, 필요하면 사람이 수정해서 다시 넣을 수 있습니다. 에이전트와 인간의 협업 루프가 Git이라는 익숙한 인터페이스 위에서 자연스럽게 이뤄집니다.

Workflows v2 — 5만 개의 동시 오케스트레이션

왜 v2인가

Workflows는 Workers 위에서 장기 실행 작업을 관리하는 오케스트레이션 엔진입니다. 기존 v1은 Durable Objects 기반으로 구현되어 있었는데, 에이전트 워크로드에서 몇 가지 병목이 드러났습니다:

  • 동시 실행 워크플로우 수가 제한적 (수천 개 수준)
  • 단일 워크플로우의 최대 실행 시간 제한
  • 스텝 간 데이터 전달 시 직렬화/역직렬화 오버헤드

Workflows v2는 이를 대폭 개선했습니다:

항목 Workflows v1 Workflows v2
동시 실행 ~5,000 50,000
최대 실행 시간 15분 24시간 (유료 플랜)
스텝 간 지연 ~50ms ~5ms
자동 재시도 3회 고정 커스텀 재시도 정책
조건부 분기 제한적 if/switch/parallel 네이티브
외부 이벤트 대기 폴링 waitForEvent() 네이티브

에이전트 오케스트레이션 예제

Workflows v2로 멀티스텝 에이전트 작업을 오케스트레이션하는 예제:

// agent-workflow/src/index.ts
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from "cloudflare:workers";

interface AgentTask {
  userPrompt: string;
  projectName: string;
  maxIterations: number;
}

export class CodeAgentWorkflow extends WorkflowEntrypoint<Env, AgentTask> {
  async run(event: WorkflowEvent<AgentTask>, step: WorkflowStep) {
    const { userPrompt, projectName, maxIterations } = event.payload;

    // Step 1: 작업 계획 수립
    const plan = await step.do("plan", async () => {
      const response = await this.env.AI.run(
        "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
        {
          messages: [{
            role: "user",
            content: `Plan the implementation for: ${userPrompt}. 
                      Return a JSON array of steps.`
          }]
        }
      );
      return JSON.parse(response.response) as string[];
    });

    // Step 2: 각 단계를 병렬로 코드 생성
    const codeResults = await step.do("generate-code", async () => {
      const results = await Promise.all(
        plan.map(async (taskStep: string, i: number) => {
          const response = await this.env.AI.run(
            "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
            {
              messages: [{
                role: "user",
                content: `Implement step ${i + 1}: ${taskStep}`
              }]
            }
          );
          return { step: i, code: response.response };
        })
      );
      return results;
    });

    // Step 3: Sandbox에서 통합 테스트
    const testResult = await step.do("test", async () => {
      const sandbox = await this.env.SANDBOX.create({
        template: "python-312",
        ttl: 600
      });

      try {
        for (const result of codeResults) {
          await sandbox.writeFile(
            `/app/step_${result.step}.py`,
            result.code
          );
        }

        await sandbox.exec("cd /app && python -m pytest", {
          timeout: 120000
        });

        return { passed: true };
      } catch (e) {
        return { passed: false, error: String(e) };
      } finally {
        await sandbox.destroy();
      }
    });

    // Step 4: 테스트 실패 시 → 인간 검토 대기
    if (!testResult.passed) {
      // waitForEvent: 외부 이벤트(인간 승인 등)를 최대 24시간 대기
      const humanReview = await step.waitForEvent("human-review", {
        timeout: "24h"
      });

      if (humanReview.action === "reject") {
        return { status: "rejected", reason: humanReview.reason };
      }
      // approved → 계속 진행
    }

    // Step 5: Artifact에 최종 저장
    await step.do("save-artifact", async () => {
      const repo = await this.env.ARTIFACTS.get(projectName);

      for (const result of codeResults) {
        await repo.writeFile(`step_${result.step}.py`, result.code);
      }

      await repo.commit({
        message: `Workflow: ${userPrompt.slice(0, 60)}`,
        author: { name: "workflow-agent", email: "[email protected]" }
      });
    });

    return {
      status: "completed",
      project: projectName,
      steps: plan.length,
      testPassed: testResult.passed
    };
  }
}

// HTTP 트리거
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const payload = await request.json() as AgentTask;
    const instance = await env.AGENT_WORKFLOW.create({ params: payload });

    return Response.json({
      workflowId: instance.id,
      status: "started"
    });
  }
} satisfies ExportedHandler<Env>;

interface Env {
  AI: Ai;
  SANDBOX: SandboxNamespace;
  ARTIFACTS: ArtifactNamespace;
  AGENT_WORKFLOW: Workflow;
}

주목할 부분은 step.waitForEvent("human-review")입니다. 워크플로우가 외부 이벤트(인간의 승인, 다른 에이전트의 결과 등)를 최대 24시간까지 비동기로 기다릴 수 있습니다. 이 기능 하나만으로 Human-in-the-loop 패턴을 서버리스로 구현할 수 있게 됩니다.

그 밖의 Agents Week 발표

Cloudflare Mesh + Workers VPC

에이전트가 여러 Workers와 서비스 사이에서 통신할 때, 공개 인터넷을 경유하면 보안·지연 양쪽에서 문제가 됩니다. Cloudflare Mesh는 Workers 간의 사설 네트워크를 구성합니다:

  • Workers VPC: 같은 계정 내 Workers를 논리적 사설망으로 묶어, 내부 DNS 이름으로 통신합니다. 예: agent-orchestrator.internalcode-generator.internal.
  • Service Binding의 확장: 기존 Service Binding이 1:1 직접 연결이었다면, Mesh는 N:N 메시 토폴로지를 지원합니다.
  • Zero Trust 정책 적용: Mesh 내부 통신에도 Zero Trust Access 정책을 걸 수 있어, 에이전트 A는 에이전트 B만 호출 가능하도록 세분화된 접근 제어가 가능합니다.
# wrangler.toml — Workers VPC 설정 예시
name = "agent-orchestrator"
main = "src/index.ts"

[vpc]
name = "agent-network"

[[vpc.peers]]
service = "code-generator"
alias = "code-gen"

[[vpc.peers]]
service = "test-runner"
alias = "tester"

Agent Readiness Score

이 기능은 다른 발표들과 성격이 다릅니다. 자신의 웹사이트가 AI 에이전트를 얼마나 잘 수용하는지를 점수로 보여주는 대시보드 기능입니다:

  • robots.txt 분석: AI 크롤러(GPTBot, ClaudeBot 등)를 허용/차단하고 있는지
  • 구조화된 데이터: schema.org, JSON-LD 등 에이전트가 파싱하기 쉬운 형태인지
  • API 엔드포인트: 에이전트가 호출할 수 있는 API가 문서화되어 있는지
  • MCP 서버: Model Context Protocol 엔드포인트가 노출되어 있는지

Cloudflare 대시보드에서 도메인을 선택하면 0~100점으로 Agent Readiness Score가 표시됩니다. 점수가 높을수록 AI 에이전트가 해당 사이트와 효과적으로 상호작용할 수 있다는 뜻입니다.

Anthropic Claude Managed Agents 통합

Agents Week의 금요일 피날레로 발표된 이 기능은 특히 주목할 만합니다. Anthropic의 Claude가 Cloudflare 인프라 위에서 Managed Agent로 실행됩니다:

  • Cloudflare Workers가 Claude 에이전트의 런타임 환경 역할을 합니다.
  • 에이전트의 도구(tool) 호출이 Workers·R2·D1·KV 등 Cloudflare 서비스로 네이티브 라우팅됩니다.
  • Sandboxes가 Claude 에이전트의 코드 실행 환경을 제공합니다.
  • Zero Trust가 에이전트의 접근 범위를 제어합니다.

이는 Cloudflare가 단순한 CDN/서버리스 플랫폼을 넘어, AI 에이전트 호스팅 플랫폼으로 본격적으로 포지셔닝하겠다는 선언입니다.

그 외 발표 요약

기능 설명 상세 회차
MCP Server Portals Zero Trust 기반 에이전트 게이트웨이 14회에서 심층 분석
Email Service 이메일 발송·수신·처리 네이티브 지원 (Public Beta) 15회
Registrar API 도메인 등록·관리를 API로 자동화 16회
PlanetScale 통합 PlanetScale MySQL을 Workers에서 직접 연결 16회
Agents Week 2026 기능별 월 비용 비교표

실습: 에이전트 파이프라인 전체 구축

사전 준비

Mac Studio 또는 Synology NAS의 터미널에서 따라 합니다. Node.js 20+와 Wrangler 최신 버전이 필요합니다:

# Wrangler 최신 버전 확인 및 업데이트
npx wrangler --version
# 3.x 이상이면 OK. 아니라면:
npm install -g wrangler@latest

# Cloudflare 계정 로그인
npx wrangler login

Step 1: 프로젝트 스캐폴딩

# 프로젝트 디렉토리 생성
mkdir agent-pipeline-demo && cd agent-pipeline-demo

# Wrangler 프로젝트 초기화
npx wrangler init --yes --type javascript

# 디렉토리 구조 확인
ls -la
# src/index.ts  wrangler.toml  package.json  tsconfig.json

Step 2: wrangler.toml 설정

cat > wrangler.toml <<'EOF'
name = "agent-pipeline-demo"
main = "src/index.ts"
compatibility_date = "2026-05-15"

# Workers AI 바인딩
[ai]
binding = "AI"

# Sandbox 바인딩
[sandbox]
binding = "SANDBOX"

# Artifacts 바인딩
[artifacts]
binding = "ARTIFACTS"

# KV 네임스페이스 (에이전트 상태 관리용)
[[kv_namespaces]]
binding = "AGENT_STATE"
id = "agent-state-kv-id"  # wrangler kv namespace create 후 교체
EOF

Step 3: KV 네임스페이스 생성

# KV 네임스페이스 생성
npx wrangler kv namespace create "AGENT_STATE"
# ⛅ Successfully created KV namespace "agent-pipeline-demo-AGENT_STATE"
# Add the following to your wrangler.toml:
# [[kv_namespaces]]
# binding = "AGENT_STATE"
# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# 출력된 id를 wrangler.toml에 반영
# (위 출력의 id 값으로 교체)

Step 4: 에이전트 코드 작성

cat > src/index.ts <<'TSEOF'
interface Env {
  AI: Ai;
  SANDBOX: SandboxNamespace;
  ARTIFACTS: ArtifactNamespace;
  AGENT_STATE: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("POST /generate — Send a coding task", { status: 405 });
    }

    const { task, projectName = "demo-project" } = await request.json() as {
      task: string;
      projectName?: string;
    };

    // 1. AI로 Python 코드 생성
    const aiResult = await env.AI.run(
      "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
      {
        messages: [
          {
            role: "system",
            content: "You are a Python expert. Return ONLY executable Python code, no markdown."
          },
          { role: "user", content: task }
        ]
      }
    );
    const code = aiResult.response;

    // 2. Sandbox에서 실행
    const sandbox = await env.SANDBOX.create({
      template: "python-312",
      ttl: 120
    });

    let execOutput: string;
    let execSuccess: boolean;

    try {
      await sandbox.writeFile("/app/main.py", code);
      const result = await sandbox.exec("cd /app && python main.py", {
        timeout: 30000
      });
      execOutput = result.stdout;
      execSuccess = result.exitCode === 0;

      if (!execSuccess) {
        execOutput = `STDERR: ${result.stderr}\nSTDOUT: ${result.stdout}`;
      }
    } finally {
      await sandbox.destroy();
    }

    // 3. Artifact에 저장
    const repo = await env.ARTIFACTS.get(projectName);
    await repo.writeFile("main.py", code);
    await repo.writeFile("output.txt", execOutput);
    const commitHash = await repo.commit({
      message: `Task: ${task.slice(0, 60)}`,
      author: { name: "demo-agent", email: "[email protected]" }
    });

    // 4. 상태 기록
    await env.AGENT_STATE.put(`last-task:${projectName}`, JSON.stringify({
      task,
      commitHash: await repo.head(),
      success: execSuccess,
      timestamp: new Date().toISOString()
    }));

    return Response.json({
      success: execSuccess,
      code,
      output: execOutput,
      artifact: {
        project: projectName,
        commit: await repo.head()
      }
    });
  }
} satisfies ExportedHandler<Env>;
TSEOF

Step 5: 로컬 테스트 및 배포

# 로컬 개발 서버 (Sandbox/Artifacts는 원격 모드 필요)
npx wrangler dev --remote

# 다른 터미널에서 테스트
curl -X POST http://localhost:8787 \
  -H "Content-Type: application/json" \
  -d '{
    "task": "Write a Python function that calculates the Fibonacci sequence up to n terms and prints each term",
    "projectName": "fibonacci-demo"
  }'

# 배포
npx wrangler deploy

# 배포 후 프로덕션 테스트
curl -X POST https://agent-pipeline-demo.{your-subdomain}.workers.dev \
  -H "Content-Type: application/json" \
  -d '{
    "task": "Write a Python script that generates a random password with uppercase, lowercase, digits, and symbols",
    "projectName": "password-gen"
  }'

Step 6: Artifact 확인 (Git 클라이언트로)

# 에이전트가 만든 산출물을 Git으로 확인
git clone https://artifacts.{your-account}.workers.dev/fibonacci-demo
cd fibonacci-demo
cat main.py      # AI가 생성한 코드
cat output.txt   # Sandbox 실행 결과
git log --oneline
# abc1234 Task: Write a Python function that calculates the Fibonacci s

월 비용 명세

이번 회에서 다룬 기능들의 비용을 정리합니다. Agents Week 발표 시점(2026년 5월) 기준입니다.

기능 무료 플랜 Workers Paid ($5/월~) 과금 단위
Dynamic Workers Workers for Platforms 무료 티어
(Dispatch Namespace 1개, Worker 100개)
Worker 수 무제한
요청당 과금 (일반 Workers와 동일)
요청 수 + CPU 시간
Sandboxes 월 1,000 Sandbox-초
(약 16분 분량)
월 100,000 Sandbox-초 포함
초과: $0.00005/초
벽시계 초 (Wall-clock seconds)
Artifacts 1GB 스토리지
10,000 API 호출/월
10GB 스토리지 포함
100,000 API 호출/월
초과: R2 과금 기준
스토리지 + API 호출 수
Workflows v2 월 10,000 스텝
동시 실행 10개
월 1,000,000 스텝 포함
동시 실행 50,000
초과: $0.00001/스텝
워크플로우 스텝 수
Workers AI
(12회 복습)
일 10,000 뉴런
(소형 모델 수백 회)
뉴런당 과금
모델별 상이
뉴런 (추론 단위)
Workers (기본) 일 100,000 요청
CPU 10ms/요청
월 1,000만 요청 포함
CPU 30ms/요청
요청 수 + CPU 시간

홈랩 예상 비용 시나리오: 개인 에이전트를 하루 50회 호출, 회당 Sandbox 30초 사용, Artifact 커밋 1회를 가정하면:

  • Sandbox: 50 × 30 = 1,500초/일 × 30일 = 45,000초/월 → Workers Paid 포함분 내
  • Artifacts: 50 × 30 = 1,500 API 호출/월 → 무료 범위 내
  • Workers AI: 모델에 따라 다르지만 Llama 70B 기준 약 50 × 30일 = 1,500 호출 → 무료 티어로 충분
  • 총 예상 비용: $5/월 (Workers Paid 기본료만)

Agents Week이 의미하는 것

플랫폼 전쟁의 새로운 전장

2024~2025년의 클라우드 전쟁이 “LLM API 호출 비용”에서 벌어졌다면, 2026년의 전쟁은 “에이전트가 살 수 있는 인프라”에서 벌어지고 있습니다. AWS는 Bedrock Agents를, Google은 Vertex AI Agent Builder를, Azure는 Copilot Studio를 내세우고 있지만, 이들은 모두 리전 기반의 전통적 클라우드 아키텍처 위에 에이전트를 올린 형태입니다.

Cloudflare의 접근은 근본적으로 다릅니다:

  • 에이전트가 엣지에서 실행: 330개 이상의 PoP에서 사용자에게 가장 가까운 곳에서 에이전트가 동작합니다.
  • 모든 것이 서버리스: VM을 프로비저닝하거나 컨테이너 클러스터를 관리할 필요가 없습니다.
  • 네이티브 통합: 스토리지(R2), 데이터베이스(D1), 키-값(KV), 상태(DO), 실행(Workers), 격리(Sandboxes), 버전관리(Artifacts), 네트워크(Mesh), 보안(Zero Trust)이 하나의 플랫폼에서 일관된 API로 제공됩니다.

1인 개발자에게 열린 기회

이 시리즈를 처음부터 따라오신 분이라면, 패턴이 보일 겁니다. Cloudflare는 일관되게 “대기업이 수십 명의 인프라 팀으로 운영하던 것을 1인 개발자가 무료 또는 월 $5로 할 수 있게 만든다”는 방향으로 움직이고 있습니다.

  • 1회: CDN → 무료
  • 5회: VPN/터널 → 무료
  • 8회: 오브젝트 스토리지 → 송신료 0원
  • 9회: 서버리스 컴퓨팅 → 일 10만 요청 무료
  • 12회: AI 추론 → 일 1만 뉴런 무료
  • 13회(지금): AI 에이전트 인프라 → $5/월에 전체 스택

AI 에이전트 SaaS를 만들고 싶은 1인 개발자에게, Cloudflare는 현존하는 가장 낮은 진입 장벽을 제공합니다.

정리: 한눈에 보는 Agents Week 2026

기능 한 줄 요약 에이전트 관점 역할
Dynamic Workers 런타임에 Worker 생성·삭제 에이전트가 코드를 배포
Sandboxes 격리된 셸·파일시스템 환경 에이전트의 “컴퓨터”
Artifacts Git 호환 버전 스토리지 에이전트 산출물의 “저장소”
Workflows v2 5만 동시 오케스트레이션 에이전트 작업의 “지휘자”
Mesh + VPC Workers 간 사설 네트워크 에이전트 간 “내부 통신망”
Agent Readiness 사이트 AI 대응도 점수 에이전트가 상호작용할 “외부 세계” 평가
Anthropic 통합 Claude Managed Agents 에이전트의 “두뇌”

Cloudflare는 Agents Week을 통해 분명한 메시지를 보냈습니다: “우리는 더 이상 CDN 회사가 아니다. AI 에이전트가 태어나고, 실행되고, 소통하고, 기억하는 플랫폼이다.”

다음 14회에서는 이 에이전트 인프라의 보안 계층을 다룹니다. 에이전트가 외부 도구에 접근할 때 사용하는 MCP(Model Context Protocol) Server와, 이를 Zero Trust로 보호하는 MCP Server Portals를 집중 분석합니다. 특히 실제 보안 사고 사례인 NeighborJack(0.0.0.0 바인딩 → OS 명령 주입 → 호스트 탈취)을 빌드업으로 다루며, 왜 에이전트 보안이 기존 웹 보안과 근본적으로 다른지를 파헤칩니다.

이미지는 Leonardo AI 로 생성되었습니다.

이미지는 Claude AI 로 생성되었습니다.


📚 시리즈: Cloudflare 완전 정복: 입문부터 2026 AI 에이전트까지 (총 16화 중 13화)
이전 12화  (다음 차수는 아직 게시되지 않았습니다)

답글 남기기

Your email address will not be published. Required fields are marked *.

Warning: Undefined array key "cookies" in /var/www/html/wp-content/themes/personal-cv-resume/class/class-post-related.php on line 212
*
*

최신 댓글