AI 에이전트를 만들고 싶은데 복잡한 메시지 큐나 재시도 로직 때문에 막막하셨나요? 혹시 서버리스 환경에서 장기 실행 프로세스를 안정적으로 관리하는 방법을 찾고 계신가요?
2025년 10월, Vercel이 공개한 **Workflow Development Kit(WDK)**가 이 모든 고민을 한 번에 해결해줍니다! 단 두 줄의 코드만으로 여러분의 비동기 함수를 프로덕션급 워크플로우로 변신시킬 수 있어요.
오늘은 복잡한 분산 시스템 지식 없이도 안정적인 워크플로우를 구축할 수 있는 Vercel Workflow의 모든 것을 알려드릴게요! 💪
🎯 Vercel Workflow가 뭔가요?

Vercel Workflow(정식 명칭: Workflow Development Kit)는 비동기 JavaScript 함수에 **내구성(durability)**과 **안정성(reliability)**을 부여하는 TypeScript 기반 오픈소스 프레임워크예요.
2025년 10월 23일 공개 베타로 출시되었으며, 함수가 몇 분에서 몇 달 동안 일시 중지했다가 정확히 멈춘 지점에서 재개될 수 있도록 해주는 혁신적인 기술입니다. VercelVercel
🌟 왜 Vercel Workflow가 필요할까요?
전통적으로 안정적인 비동기 작업을 구현하려면 이런 것들이 필요했어요:
❌ 기존 방식의 문제점
- 메시지 큐 설정과 관리
- 복잡한 재시도 로직 구현
- 상태 지속성을 위한 데이터베이스 설계
- 실패 시 복구 메커니즘 구축
- 분산 시스템 전문 지식
✅ Vercel Workflow의 해결책 메시지 큐, 재시도 로직, 지속성 레이어를 자동으로 처리하여 비즈니스 로직 작성 시간보다 인프라 구축 시간이 더 오래 걸리는 문제를 해결합니다. Vercel
💡 핵심 개념: "use workflow"와 "use step"
Vercel Workflow의 마법은 단 두 개의 지시어(directive)에서 시작돼요!
1️⃣ "use workflow" - 워크플로우 정의
export async function handleUserSignup(email: string) {
"use workflow"; // 🎯 이 한 줄이 마법의 시작!
const user = await createUser(email);
await sendWelcomeEmail(user);
await sleep("5s"); // 5초 동안 일시 중지 (리소스 소비 없음!)
await sendOnboardingEmail(user);
return { userId: user.id, status: "onboarded" };
}
"use workflow" 지시어는 일반 비동기 함수를 durable workflow로 변환하며, 여러 단계를 조율하는 오케스트레이션 역할을 합니다. Vercel
2️⃣ "use step" - 작업 단위 정의
async function createUser(email: string) {
"use step"; // 💪 자동으로 재시도되는 작업 단위
console.log(`Creating user with email: ${email}`);
// 데이터베이스 호출, API 요청 등 모든 Node.js 기능 사용 가능
return { id: crypto.randomUUID(), email };
}
async function sendWelcomeEmail(user: { id: string; email: string }) {
"use step";
console.log(`Sending welcome email to user: ${user.id}`);
if (Math.random() < 0.3) {
// 에러 발생 시 자동으로 재시도!
throw new Error("Retryable!");
}
}
각 스텝은 독립적으로 실행되며 진행 상황을 자동으로 저장하고, 실패 시 자동으로 재시도됩니다. 스텝이 실행되는 동안 워크플로우는 리소스를 소비하지 않고 일시 중지됩니다. Vercel
🚀 Vercel Workflow 시작하기
Step 1: 프로젝트 설정
Next.js 프로젝트에 Vercel Workflow를 추가하는 건 정말 간단해요!
# 패키지 설치
npm install workflow
# 또는
pnpm add workflow
Step 2: 첫 번째 워크플로우 만들기
workflows/user-signup.ts 파일을 생성해주세요:
import { sleep, FatalError } from "workflow";
export async function handleUserSignup(email: string) {
"use workflow";
// 1단계: 사용자 생성
const user = await createUser(email);
// 2단계: 환영 이메일 발송
await sendWelcomeEmail(user);
// 3단계: 5초 대기 (리소스 소비 없음!)
await sleep("5s");
// 4단계: 온보딩 이메일 발송
await sendOnboardingEmail(user);
return { userId: user.id, status: "onboarded" };
}
async function createUser(email: string) {
"use step";
console.log(`Creating user: ${email}`);
return { id: crypto.randomUUID(), email };
}
async function sendWelcomeEmail(user: { id: string; email: string }) {
"use step";
console.log(`Sending welcome email to: ${user.id}`);
// 30% 확률로 에러 발생 (자동 재시도됨)
if (Math.random() < 0.3) {
throw new Error("Email service temporarily unavailable");
}
}
async function sendOnboardingEmail(user: { id: string; email: string }) {
"use step";
// 이메일 형식 검증
if (!user.email.includes("@")) {
// FatalError는 재시도하지 않음
throw new FatalError("Invalid email format");
}
console.log(`Sending onboarding email to: ${user.id}`);
}
Step 3: API 라우트에서 워크플로우 실행
app/api/signup/route.ts 파일을 생성하세요:
import { start } from 'workflow/api';
import { handleUserSignup } from "@/workflows/user-signup";
import { NextResponse } from "next/server";
export async function POST(request: Request) {
const { email } = await request.json();
// 워크플로우를 비동기로 실행 (앱을 블로킹하지 않음)
await start(handleUserSignup, [email]);
return NextResponse.json({
message: "User signup workflow started",
});
}
start 함수는 워크플로우를 큐에 추가하고 즉시 반환하며, 완료를 기다리지 않습니다. Vercel
🎨 강력한 기능들
1. 웹훅으로 외부 이벤트 대기
결제 확인이나 사용자 액션 같은 외부 이벤트를 기다려야 할 때가 있죠?
import { createWebhook } from "workflow";
export async function paymentWorkflow(orderId: string) {
"use workflow";
// 웹훅 생성
const webhook = createWebhook();
console.log('Send payment confirmation to:', webhook.url);
// 결제 확인이 올 때까지 대기 (폴링이나 큐 없이!)
const request = await webhook;
const paymentData = await request.json();
console.log('Payment confirmed:', paymentData);
return { orderId, status: "paid" };
}
웹훅을 사용하면 외부 서비스의 데이터가 도착할 때까지 워크플로우를 일시 중지할 수 있으며, 폴링이나 메시지 큐, 상태 관리가 필요 없습니다. Vercel
2. 장기 실행 프로세스
며칠이나 몇 주 동안 실행되는 프로세스도 쉽게 만들 수 있어요!
import { sleep } from "workflow";
export async function loyaltyRewardWorkflow(userId: string) {
"use workflow";
// 사용자가 라이드를 완료했다고 가정
console.log(`Ride completed for user: ${userId}`);
// 3일 동안 대기 (리소스 소비 없음!)
await sleep("3 days");
// 충성도 리워드 발급
await issueLoyaltyReward(userId);
return { userId, reward: "issued" };
}
async function issueLoyaltyReward(userId: string) {
"use step";
console.log(`Issuing loyalty reward to user: ${userId}`);
// 리워드 발급 로직...
}
워크플로우는 몇 분 또는 몇 달 동안 일시 중지되었다가 정확히 멈춘 지점에서 재개될 수 있으며, 이 동안 리소스를 소비하거나 상태를 잃지 않습니다. Vercel
3. 고급 에러 핸들링
import { FatalError, RetryableError, getStepMetadata } from "workflow";
async function callExternalAPI(endpoint: string) {
"use step";
const metadata = getStepMetadata();
const response = await fetch(endpoint);
// 서버 에러 (5xx) - 지수 백오프로 재시도
if (response.status >= 500) {
throw new RetryableError("Server error", {
retryAfter: metadata.attempt ** 2, // 1초, 4초, 9초...
});
}
// 404 에러 - 재시도하지 않음
if (response.status === 404) {
throw new FatalError("Resource not found. Skipping retries.");
}
// Rate limit (429) - Retry-After 헤더 사용
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After");
throw new RetryableError("Rate limited", {
retryAfter: parseInt(retryAfter),
});
}
return response.json();
}
// 최대 재시도 횟수 설정
callExternalAPI.maxRetries = 5;
4. 타입 안전한 Hook 시스템
import { defineHook } from "workflow";
// Hook 타입 정의
type ApprovalRequest = {
requestId: string;
approved: boolean;
approvedBy: string;
comment: string;
};
const approvalHook = defineHook<ApprovalRequest>();
// 워크플로우에서 사용
export async function documentApprovalWorkflow(documentId: string) {
"use workflow";
const hook = approvalHook.create({
token: `approval:${documentId}`
});
// 승인을 기다림
const approval = await hook;
console.log(`Document ${approval.requestId} ${approval.approved ? "approved" : "rejected"}`);
console.log(`By: ${approval.approvedBy}, Comment: ${approval.comment}`);
}
// API 라우트에서 Hook 재개
export async function POST(request: Request) {
const { documentId, ...approvalData } = await request.json();
// 타입 안전성 보장!
await approvalHook.resume(`approval:${documentId}`, approvalData);
return new Response("OK");
}
🔍 워크플로우 실행 모니터링
워크플로우의 상태를 확인하고 결과를 가져오는 것도 간단해요:
import { start, getRun } from 'workflow/api';
// 워크플로우 시작
const run = await start(handleUserSignup, ['user@example.com']);
console.log('Run ID:', run.runId);
// 나중에 상태 확인
const existingRun = getRun(run.runId);
const status = await existingRun.status;
console.log('Status:', status); // 'pending', 'running', 'completed', 'failed'
// 결과 가져오기
try {
const result = await existingRun.returnValue;
console.log('Result:', result);
} catch (error) {
if (error instanceof WorkflowRunFailedError) {
console.error('Workflow failed:', error.message);
}
}
첫 번째 트리거부터 최종 결과까지 워크플로우 내부에서 발생하는 모든 것이 이벤트 로그에 저장되며, 포함된 CLI와 Web UI를 통해 시각적으로 확인할 수 있습니다. Vercel
🎯 실전 활용 사례
1. AI 에이전트 구축
import { sleep } from "workflow";
export async function aiAgentWorkflow(taskId: string) {
"use workflow";
// 1단계: 작업 분석
const analysis = await analyzeTask(taskId);
// 2단계: AI 처리
const aiResult = await processWithAI(analysis);
// 3단계: 결과 검증 대기 (사람의 검토)
await sleep("2 hours");
// 4단계: 최종 처리
return await finalizeResult(aiResult);
}
2. 데이터 파이프라인
export async function dataPipelineWorkflow(datasetId: string) {
"use workflow";
// 데이터 추출
const rawData = await extractData(datasetId);
// 데이터 변환
const transformedData = await transformData(rawData);
// 데이터 적재
await loadData(transformedData);
// 검증
return await validatePipeline(datasetId);
}
3. 사용자 온보딩 플로우
import { sleep } from "workflow";
export async function onboardingWorkflow(userId: string) {
"use workflow";
await sendWelcomeEmail(userId);
await sleep("1 day");
await sendTipEmail(userId);
await sleep("3 days");
await sendFeedbackRequest(userId);
await sleep("7 days");
await sendPremiumOffer(userId);
}
💻 로컬 개발 환경 설정
Vercel Workflow는 로컬에서도 완벽하게 동작해요!
# 로컬 개발 서버 실행
npm run dev
# 워크플로우 상태 확인 (CLI)
npx workflow list
# 특정 워크플로우 상세 정보
npx workflow get <run-id>
🚀 프로덕션 배포
Vercel에 배포하면 모든 것이 자동으로 설정돼요:
# Vercel에 배포
vercel deploy
# 프로덕션 배포
vercel --prod
Vercel Workflow는 어떤 프레임워크, 플랫폼, 런타임에서도 실행되며, 배포나 크래시 상황에서도 살아남아 정확히 멈춘 지점에서 재개됩니다. Vercel
🔥 Vercel Workflow vs 기존 솔루션
기능Vercel Workflow기존 솔루션 (Bull, Temporal 등)
| 학습 곡선 | 매우 낮음 (단 2개 지시어) | 높음 (복잡한 설정) |
| 인프라 | 완전 관리형 | 직접 구축 필요 |
| 코드 스타일 | 일반 TypeScript | YAML, 상태 머신 |
| 장기 실행 | ✅ 내장 지원 | 추가 설정 필요 |
| 관찰성 | ✅ 내장 UI/CLI | 별도 도구 필요 |
| 가격 | 사용량 기반 | 인프라 비용 |
📚 더 알아보기
공식 문서
추천 학습 경로
- ✅ 이 가이드로 기본 개념 이해
- 📖 공식 문서의 Getting Started 따라하기
- 💻 간단한 프로젝트로 실습
- 🚀 프로덕션에 점진적으로 적용
🎁 마무리하며
Vercel Workflow는 복잡한 분산 시스템 지식 없이도 안정적이고 확장 가능한 비동기 워크플로우를 구축할 수 있게 해주는 게임 체인저예요. **"use workflow"**와 "use step" 단 두 개의 지시어만으로 프로덕션급 시스템을 만들 수 있다니, 정말 놀랍지 않나요? 😊
특히 AI 에이전트나 장기 실행 프로세스를 구축하는 분들에게는 필수 도구가 될 거라고 확신해요. 메시지 큐 설정하고, 재시도 로직 짜고, 상태 관리하느라 고생하던 시간을 이제 비즈니스 로직 개발에 투자할 수 있어요!
'기술의기록' 카테고리의 다른 글
| Cursor 2.0 출시! AI 코딩의 새로운 시대를 여는 Composer 모델 완벽 정리 (1) | 2025.10.30 |
|---|---|
| React 19.2 Activity Component, Next.js에서 사용 불가 문제 총정리 (0) | 2025.10.23 |
| Next.js 16 정식 출시! 완전히 달라진 성능과 새로운 기능 총정리 (2025년 최신) (0) | 2025.10.22 |
| React Compiler 1.0으로 자동 최적화 시대가 열렸다 (0) | 2025.10.17 |
| React 19 useActionState 완벽 가이드 - 폼 관리가 이렇게 쉬워진다고? (0) | 2025.10.15 |