소개
애플리케이션은 일반적으로 코드에 직접 작성된 규칙에 따라 동작합니다. 인공지능 (AI) 추론은 다른 종류의 동작을 추가합니다. 애플리케이션이 학습된 모델에 입력을 보내면 모델이 결과를 생성합니다. 모델에 보내는 지시와 컨텍스트를 프롬프트라고 합니다. 생성되는 문장은 요청마다 달라질 수 있으므로, 안정적인 애플리케이션은 항상 동일한 문장을 기대하는 대신 입력을 제어하고 결과를 확인합니다.
Cloudflare Workers AI를 사용하면 Worker 에서 Cloudflare 플랫폼을 통해 지원되는 AI 모델을 실행할 수 있습니다. Worker 는 Cloudflare 네트워크에서 요청에 응답하는 애플리케이션 코드입니다. AI 바인딩은 Workers AI 를 해당 코드에서 env.AI로 사용할 수 있도록 설정하는 연결입니다. 프로젝트에 별도의 공급자 API 키를 저장하지 않아도 됩니다.
이 실습에서는 상담원이 전체 설명을 열기 전에 지원 티켓의 짧은 요약을 확인해야 하는 지원 애플리케이션을 만듭니다. AI 바인딩을 설정하고, POST /summaries 엔드포인트를 구현하며, 추론을 실행하기 전에 적절하지 않은 입력을 거부합니다. 그런 다음 동일한 Worker 를 로컬에서 테스트하고 배포한 후, Cloudflare Dashboard 에서 실제 Worker 와 AI 활동을 확인합니다. 이 실습에서는 표준 Workers AI 무료 할당량으로 사용할 수 있는 Cloudflare 호스팅 모델 @cf/meta/llama-3.3-70b-instruct-fp8-fast를 사용합니다. 응답 문구 자체는 채점하지 않으며, 애플리케이션 계약을 평가합니다.
이 과정을 시작하기 전에 LabEx 를 Cloudflare 계정에 연결을 완료하세요. 이 실습에서는 LabEx VM 터미널, 디바이스 인증, 계정 확인 및 실제 계정 ID 저장 방법을 설명합니다. 또한 간단한 JavaScript Worker 가 HTTP 요청을 처리하는 방식도 알고 있어야 합니다. 머신러닝 지식은 필요하지 않습니다.
현재 Workers AI 는 Workers Free 계정에 Cloudflare 의 모델 연산 단위인 Neuron을 하루 10,000 개씩 공유 할당합니다. 이 실습은 프롬프트와 출력을 작게 유지하므로 유료 플랜이 필요하지 않지만, 같은 계정에서 발생하는 다른 활동도 동일한 할당량을 사용합니다. 시작하기 전에 최신 Llama 3.3 모델 페이지와 Workers AI 요금을 확인하세요. 일일 할당량을 이미 모두 사용했다면 한도가 초기화될 때까지 추론이 실패합니다. 이를 우회하기 위해 반복해서 호출하지 마세요. 로컬 Workers AI 개발도 클라우드 모델을 사용하며 할당량에 포함됩니다. 오프라인 시뮬레이션이 아닙니다.
설정 과정에서는 /home/labex/project/ticket-summary에 Node.js 22.22.0 과 프로젝트 로컬 Wrangler 4.132.0 을 설치합니다. 또한 모델을 호출하지 않고 AI 응답을 모방하는 결정적 테스트도 제공합니다. 설정 과정에서는 로그인, 플랜 변경, 배포 또는 추론을 실행하지 않습니다. 일회성 Worker 를 삭제하고 로그아웃이 확인될 때까지 이 VM 을 종료하지 마세요.
VM 인증 및 계정 선택
이 단계에서는 새 LabEx VM 을 Cloudflare 학습 계정에 연결하고 고유한 Worker 설정을 만듭니다. Dashboard 의 브라우저 세션만으로는 VM 의 터미널 명령이 자동으로 인증되지 않습니다.
준비된 프로젝트 디렉터리로 이동한 후 고정된 Wrangler 버전을 확인합니다.
cd /home/labex/project/ticket-summary
npx wrangler --version
4.132.0이 출력되어야 합니다. 이 실습에 필요한 권한만 사용하여 디바이스 인증을 시작합니다. workers_scripts:write는 일회성 Worker 의 배포, 조회 및 삭제에 필요합니다. ai:write는 Worker 가 Workers AI 를 호출하도록 허용합니다. Wrangler 4.132.0 은 Worker 를 삭제할 때 KV 바인딩 참조도 확인하므로, 이 실습에서 KV 네임스페이스를 만들지 않더라도 정리 확인을 완료하려면 workers_kv:write가 필요합니다. 계정 및 사용자 조회 권한은 올바른 계정을 확인하는 데 사용합니다.
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write ai:write
브라우저에서 표시된 링크를 열고 현재 디바이스 코드를 입력한 다음 권한을 확인하고 학습 계정을 선택합니다. 브라우저 인증 과정이 끝난 후에도 Wrangler 가 계속 작업해야 하므로 Background Access 가 표시될 수도 있습니다. 계정과 권한 목록이 이 실습에 맞는지 확인한 후에만 인증하고, 터미널로 돌아와 성공 메시지가 나타날 때까지 기다립니다.
npx wrangler whoami --json
loggedIn: true인지 확인한 후, 사용할 계정의 name과 id를 읽습니다. 계정이 하나만 표시되더라도 확인해야 합니다. 이름은 잘못된 계정을 사용하는 것을 방지하고, ID 는 Wrangler 가 설정에 저장하는 안정적인 값입니다.
고유한 Worker 이름을 생성합니다. openssl rand -hex 6은 12 개의 무작위 16 진수 문자를 만들고, $(...)은 그 값을 셸 변수에 삽입합니다.
RUN="labex-c07-a01-$(openssl rand -hex 6)"
printf '%s\n' "$RUN"
선택한 계정 ID 를 아래 설정에 입력하려면 YOUR_ACCOUNT_ID를 해당 ID 로 바꿉니다. here-document 는 두 JSON 마커 사이의 줄을 wrangler.jsonc에 기록합니다. 따옴표가 없는 마커는 $RUN을 확장하고, 백슬래시는 $schema 키를 문자 그대로 유지합니다.
cat > wrangler.jsonc <<JSON
{
"\$schema": "./node_modules/wrangler/config-schema.json",
"name": "$RUN",
"account_id": "YOUR_ACCOUNT_ID",
"main": "src/index.js",
"compatibility_date": "2026-09-16",
"compatibility_flags": ["nodejs_compat"],
"workers_dev": true,
"preview_urls": false,
"observability": {
"enabled": true,
"head_sampling_rate": 1
},
"ai": {
"binding": "AI",
"remote": true
}
}
JSON
compatibility_date는 이 실습에서 테스트하는 런타임 동작을 고정합니다. observability는 이후 Dashboard 확인 단계에서 사용할 호출 및 애플리케이션 로그를 유지합니다. 이 파일을 작성하는 것만으로는 Worker 가 배포되거나 모델 호출이 발생하지 않습니다.
Workers AI 바인딩 확인
이 단계에서는 설정을 Worker 환경의 타입 설명으로 변환하고, 다음에 작성할 코드에서 바인딩 이름을 사용하도록 연결합니다.
바인딩은 Workers 런타임이 제공하는 이름이 지정된 기능입니다. wrangler.jsonc의 AI 이름은 Worker 가 env.AI를 사용해 모델을 실행한다는 뜻입니다. 소스 코드에는 API 토큰이 없습니다. Cloudflare 가 배포된 Worker 를 선택한 계정에 연결합니다. remote: true 설정은 wrangler dev 실행 중에도 중요합니다. 요청 핸들러 자체는 이 VM 에서 실행되지만 모델 추론은 항상 Cloudflare 에서 수행되기 때문입니다.
프로젝트 설정에서 환경 타입 설명을 생성합니다.
npx wrangler types
Wrangler 가 worker-configuration.d.ts를 생성합니다. 파일 전체를 읽는 대신 생성된 Env 항목을 검색합니다.
grep -A4 'interface __BaseEnv_Env' worker-configuration.d.ts
다음과 비슷한 AI 바인딩이 출력됩니다.
interface __BaseEnv_Env {
AI: Ai;
}
Wrangler 는 생성된 바인딩을 기본 인터페이스에 배치한 다음 Env가 이를 확장하도록 구성합니다. AI: Ai 줄이 중요한 일관성 확인 항목입니다. 설정에서 바인딩 이름을 변경하고 코드를 업데이트하지 않으면 배포는 완료되더라도 런타임에서 실패할 수 있습니다. 바인딩을 변경할 때마다 타입을 다시 생성하세요. 이후 전체 배포 드라이런에서 이 설정과 Worker 번들을 함께 검증합니다.
입력 범위를 제한한 요약 엔드포인트 구축
이 단계에서는 요청 경계와 모델 호출을 구현합니다. 언어 모델은 간결한 설명을 생성하는 데 적합하지만, 임의의 요청을 안전하게 처리할 수 있는지 스스로 판단하게 해서는 안 됩니다. 일반 애플리케이션 코드는 추론을 실행하기 전에 잘못된 콘텐츠 유형, 형식이 잘못된 JSON, 누락된 세부 정보 및 너무 큰 입력을 거부해야 합니다.
엔드포인트는 모델에 두 개의 메시지를 보냅니다. 시스템 메시지는 모델의 역할과 응답 제약을 정의합니다. 사용자 메시지에는 생성한 티켓이 들어갑니다. 모델은 단어, 단어 일부 또는 구두점일 수 있는 작은 텍스트 조각인 토큰을 읽고 생성합니다. max_tokens는 생성되는 출력의 크기를 제한하고, 애플리케이션은 들어오는 문자의 수를 별도로 제한합니다. 두 설정은 서로 다른 제어 기능입니다. 하나는 모델에 보내는 입력을 제한하고, 다른 하나는 모델이 생성할 수 있는 출력을 제한합니다. temperature는 모델이 사용할 수 있는 표현의 변화를 조절합니다. 여기서는 낮은 값을 사용하여 문장이 완전히 동일하다고 보장하지 않으면서도 일관된 요약을 유도합니다.
Worker 진입점을 생성합니다.
cat > src/index.js <<'JS'
const MODEL = "@cf/meta/llama-3.3-70b-instruct-fp8-fast";
const MAX_DETAILS = 2000;
function json(data, status = 200) {
return Response.json(data, { status });
}
async function readTicket(request) {
const contentType = request.headers.get("content-type") || "";
if (!contentType.toLowerCase().includes("application/json")) {
return { error: json({ error: "json_required" }, 415) };
}
const raw = await request.text();
if (raw.length > 4096) {
return { error: json({ error: "ticket_too_large" }, 413) };
}
let body;
try {
body = JSON.parse(raw);
} catch {
return { error: json({ error: "invalid_json" }, 400) };
}
const subject = typeof body?.subject === "string" ? body.subject.trim() : "";
const details = typeof body?.details === "string" ? body.details.trim() : "";
if (!details) {
return { error: json({ error: "invalid_ticket" }, 400) };
}
if (subject.length > 120 || details.length > MAX_DETAILS) {
return { error: json({ error: "ticket_too_large" }, 413) };
}
return { ticket: { subject, details } };
}
async function summarize(request, env) {
const requestId = crypto.randomUUID();
const parsed = await readTicket(request);
if (parsed.error) return parsed.error;
try {
const result = await env.AI.run(MODEL, {
messages: [
{
role: "system",
content: "Summarize this support ticket in one plain sentence. Do not invent facts."
},
{
role: "user",
content: `Subject: ${parsed.ticket.subject || "(none)"}\nDetails: ${parsed.ticket.details}`
}
],
max_tokens: 120,
temperature: 0.2
});
const summary = result.response?.trim();
if (!summary) throw new Error("empty model response");
console.log(JSON.stringify({
event: "ticket_summarized",
requestId,
model: MODEL,
inputCharacters: parsed.ticket.details.length,
totalTokens: result.usage?.total_tokens ?? null
}));
return json({ summary, model: MODEL, requestId });
} catch (error) {
console.error(JSON.stringify({
event: "ticket_summary_failed",
requestId,
model: MODEL,
reason: error instanceof Error ? error.message : "unknown"
}));
return json({ error: "model_unavailable", requestId }, 502);
}
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (request.method === "GET" && url.pathname === "/health") {
return json({ status: "ok" });
}
if (request.method === "POST" && url.pathname === "/summaries") {
return summarize(request, env);
}
return json({ error: "not_found" }, 404);
}
};
JS
각 요청에는 무작위 요청 ID가 부여됩니다. 이 ID 는 응답과 로그에 모두 표시되므로 티켓 내용을 로그에 기록하지 않고도 특정 요청을 추적할 수 있습니다. 코드는 티켓 텍스트를 기록하지 않고 ID, 모델 선택 및 개수를 로그에 남깁니다. 이를 통해 이후 observability를 유용하게 사용할 수 있습니다. observability 는 Worker 가 수행한 작업을 이해하는 데 도움이 되는 기록을 뜻하며, 고객 콘텐츠를 모니터링 데이터에 복사하지 않도록 합니다. 또한 모든 Workers AI 모델이 동일한 객체를 반환한다고 가정하지 않고, 이 모델이 반환하는 response 문자열을 확인합니다.
제공된 결정적 테스트를 실행합니다. 테스트에서는 env.AI를 작은 픽스처로 대체하므로 모델 사용량을 소비하지 않습니다.
node --test test/worker.test.mjs
4 개의 테스트가 모두 통과해야 합니다. 그런 다음 배포하지 않고 Worker 를 빌드하도록 Wrangler 에 요청합니다.
npx wrangler deploy --dry-run
테스트는 제어된 모델 데이터를 사용해 입력 및 출력 계약을 검증합니다. 드라이런은 Wrangler 가 실제 Worker 를 번들링할 수 있는지 검증합니다. 하지만 어느 것도 현재 모델을 사용할 수 있는지 또는 이 계정에 일일 무료 할당량이 남아 있는지는 확인하지 않습니다. 다음 단계에서 실제 요청 한 번으로 확인합니다.
로컬에서 추론 한 번 실행
이 단계에서는 VM 에서 요청 핸들러를 실행하면서 AI 바인딩이 실제 Cloudflare 호스팅 모델을 호출하도록 합니다. 이를 로컬 개발이라고 부르지만, 로컬에서 실행되는 것은 Worker 프로세스뿐입니다. 추론은 원격으로 수행되며 사용량이 계산됩니다.
Wrangler 를 백그라운드에서 포트 8787로 시작합니다. >는 로그를 파일에 저장하고, 2>&1은 오류를 같은 파일로 보내며, &는 서버가 계속 실행되는 동안 터미널 프롬프트를 반환합니다. $!을 저장하면 프로세스 ID 를 정리할 때 사용할 수 있습니다.
npx wrangler dev --port 8787 > .labex/dev.log 2>&1 &
echo $! > .labex/dev.pid
상태 확인 라우트가 응답할 때까지 기다립니다.
for attempt in $(seq 1 30); do
if curl --silent --fail http://127.0.0.1:8787/health; then
break
fi
sleep 1
done
상태 확인 응답은 {"status":"ok"}여야 하며 모델을 호출하지 않습니다. 이제 작은 테스트용 티켓을 하나 보냅니다. --data는 POST 요청을 만들고, 헤더는 Worker 가 JSON 으로 파싱하도록 지정합니다.
curl --silent --show-error http://127.0.0.1:8787/summaries \
--header 'Content-Type: application/json' \
--data '{"subject":"Invoice upload fails","details":"After signing in, the customer selects a PDF invoice. The upload stops before completion and no confirmation appears."}' | jq
비어 있지 않은 summary, 정확한 모델 ID 및 요청마다 달라지는 requestId가 출력되어야 합니다. 문장은 다음 예시와 다를 수 있습니다.
{
"summary": "The customer cannot complete a PDF invoice upload after signing in.",
"model": "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
"requestId": "..."
}
추론을 실행하기 전에 일반 코드가 잘못된 입력을 거부하는지 확인합니다.
curl --silent --show-error --write-out '\nHTTP %{http_code}\n' \
http://127.0.0.1:8787/summaries \
--header 'Content-Type: application/json' \
--data '{"details":""}'
{"error":"invalid_ticket"}와 HTTP 400이 출력되어야 합니다. 애플리케이션은 이 요청을 모델에 보내지 않습니다. 유효한 요청에서 model_unavailable이 반환되면 .labex/dev.log를 확인하세요. 무료 할당량 소진, 모델 용량 또는 인증 오류가 발생했다고 해서 엔드포인트 계약이 통과한 것은 아닙니다.
AI Worker 배포 및 확인
이 단계에서는 로컬 프로세스를 중지하고 동일한 코드를 Cloudflare 에 배포한 후, 명령줄 결과를 Dashboard 에서 확인할 수 있는 상태와 연결합니다.
저장해 둔 개발 프로세스만 중지하고 종료될 때까지 기다립니다.
kill "$(cat .labex/dev.pid)"
wait "$(cat .labex/dev.pid)" 2>/dev/null || true
Worker 를 배포합니다.
npx wrangler deploy
Wrangler 가 공개 workers.dev URL 을 출력합니다. 아래 예시 값을 실제 URL 로 바꾸고 정확한 URL 을 저장합니다.
WORKER_URL="https://YOUR_WORKER_URL"
배포된 엔드포인트에 새 테스트용 티켓을 보냅니다.
curl --silent --show-error "$WORKER_URL/summaries" \
--header 'Content-Type: application/json' \
--data '{"subject":"Password reset loop","details":"The customer opens the reset email, chooses a new password, and returns to the sign-in page, but the old password remains active."}' | jq
생성된 문장은 달라질 수 있지만, model에는 Llama 3.3 이 표시되고 requestId가 있어야 합니다. 이 결과로 공개 Worker 가 설정된 AI 바인딩에 연결되었음을 확인할 수 있습니다.
Cloudflare Dashboard 를 열고 Workers & Pages → Overview → labex-c07-a01-... Worker → Settings → Bindings로 이동합니다. AI Workers AI 바인딩을 찾습니다. 이것은 wrangler.jsonc와 코드의 env.AI를 연결하는 실제 설정입니다.

예시에서는 바인딩 이름이 AI이며, env.AI에서 사용하는 이름과 일치합니다. 실제 일회성 Worker 의 이름은 다릅니다.
다음으로 동일한 Worker 의 Observability → Logs를 엽니다. 최근 성공한 호출을 찾고 구조화된 ticket_summarized 로그를 펼칩니다. 로그의 요청 ID 가 배포된 응답의 요청 ID 와 일치하는지 확인합니다. 로그에는 모델과 개수가 표시되지만 티켓 텍스트는 표시되지 않아야 합니다. 저장된 로그가 아직 도착하지 않았다면 Real-time logs를 사용하고 작은 테스트용 요청을 하나 더 보낸 다음 해당 호출을 확인합니다.

개요 화면에서는 먼저 오류 없이 요청이 Worker 에 도달했는지 확인합니다. 요청 하나를 열면 구조화된 애플리케이션 이벤트가 표시됩니다.

로그에 모델, 토큰 수, 요청 ID 와 같은 운영 정보는 있지만 지원 티켓의 제목이나 세부 정보는 없다는 점에 주목하세요. 이것이 작성한 로깅 코드가 만든 개인정보 보호 경계입니다.
마지막으로 Developer Platform 탐색 메뉴에서 Workers AI를 열고 사용량 화면을 확인합니다. 이 입력 범위를 제한한 테스트와 관련된 최근 모델 활동 또는 Neuron 사용량을 찾습니다. 사용량 데이터는 요청보다 늦게 도착할 수 있습니다. 즉시 차트가 비어 있어도 결론을 내릴 수 없으며, 반복적인 추론 호출로 이를“해결”하지 마세요.

여기서 20.32/10k는 이 승인 테스트가 해당 계정의 일일 Free 할당량 중 일부만 사용했다는 뜻입니다. 사용자의 학습 계정에서 발생한 다른 Workers AI 활동도 총량에 포함되므로 스크린샷과 값이 일치하지 않습니다.
이 실습의 Dashboard 스크린샷은 하나의 일회성 승인 테스트에서 나온 예시 값입니다. 실제 Worker 이름, 요청 ID, 타임스탬프, 토큰 수 및 사용량 합계는 달라집니다.
Worker 삭제 및 로그아웃
이 단계에서는 일회성 클라우드 애플리케이션을 삭제한 다음 이 VM 의 Wrangler 세션을 취소합니다. Worker 를 삭제하면 공개 엔드포인트가 중지됩니다. 하지만 Workers 플랜이 변경되거나 계정 수준의 사용량 기록이 삭제되지는 않습니다.
wrangler.jsonc에 지정된 Worker 를 삭제합니다.
npx wrangler delete
Wrangler 가 이 실습의 고유한 이름을 표시하면 삭제를 확인합니다. 다른 애플리케이션은 삭제하지 마세요. Dashboard 에서 Workers & Pages → Overview로 돌아가 정확한 labex-c07-a01-... Worker 가 사라졌는지 확인합니다. 스크립트가 삭제된 후에도 과거 로그나 사용량 기록은 남을 수 있습니다.
Wrangler 는 삭제를 완료하기 전에 다른 Worker 가 이 Worker 에 의존하는지 확인합니다. 애플리케이션에서 KV 를 사용하지 않았는데도 앞 단계의 로그인에 KV 정리 권한이 포함된 이유가 이것입니다. 삭제가 성공하면 인증 오류 없이 프롬프트로 돌아와야 합니다.
VM 이 아직 인증된 상태에서 삭제 확인을 실행합니다.
python3 .labex/verify.py deleted
PASS: deleted가 출력된 후에만 저장된 인증 정보를 삭제합니다.
npx wrangler logout
npx wrangler whoami --json
최종 출력은 loggedIn: false여야 합니다. 네트워크 오류가 발생했다고 해서 로그아웃이 완료된 것은 아닙니다.
요약
AI 바인딩을 통해 Worker 를 Cloudflare 호스팅 모델에 연결하고, 입력과 생성 출력을 제한했습니다. 모델 사용량을 소비하기 전에 결정적 동작을 테스트하고, 로컬 및 배포 환경에서 실제 추론을 실행했으며, 응답을 Dashboard 의 바인딩, 로그 및 사용량 정보와 연결해 확인했습니다. 마지막으로 일회성 Worker 를 삭제하고 새 VM 에서 안전하게 로그아웃했습니다.



