8 min read

Vercel Workflow 30분 Step 운영 가이드: 실행 시간, 재시도, 비용 경계

Vercel Workflow step의 최대 실행 시간이 30분으로 늘어난 뒤 max duration, 재시도, idempotency, 취소와 관측성을 어떻게 설계해야 하는지 설명합니다.

Vercel Workflow의 step이 30분까지 실행될 수 있게 됐다고 해서 모든 긴 작업을 하나의 step에 넣어도 되는 것은 아닙니다. 이번 변경은 긴 LLM 호출, 문서 처리, 브라우저 자동화처럼 800초 안에 끝나지 않던 단일 작업의 여유를 늘린 것입니다. 워크플로 전체를 오래 유지하는 기능과 한 step이 오래 실행되는 기능은 구분해야 합니다.

Vercel은 2026년 7월 24일 Pro와 Enterprise 플랜의 Workflow step이 최대 1,800초까지 실행될 수 있다고 발표했습니다. 기존 상한은 800초였습니다. 기능은 beta이며 Fluid compute와 지원되는 Node.js 또는 Python 런타임이 필요합니다. Hobby 플랜은 300초 제한을 유지합니다.

이 글에서는 설정 방법보다 다음 운영 문제를 다룹니다.

  • 30분 step이 필요한 작업과 여러 step으로 나눌 작업
  • 재시도 시 외부 변경이 중복되지 않게 하는 방법
  • 긴 step의 취소와 애플리케이션 timeout
  • 실행 시간이 늘어날 때의 비용과 관측성

워크플로 수명과 step 실행 시간은 다릅니다

Vercel Workflows는 대기, 승인과 재시작을 포함해 오랜 시간 상태를 유지할 수 있습니다. 반면 실제 코드를 실행하는 각 step은 Vercel Function 호출입니다.

시간의미적합한 처리
워크플로 전체 수명여러 step과 대기 구간을 포함한 전체 흐름승인 대기, 예약, webhook 대기, 장기 상태 유지
step 실행 시간한 번의 함수 호출에서 코드가 실행되는 시간LLM 추론, OCR, 브라우저 작업, 대용량 변환

사용자 승인을 하루 동안 기다리는 작업은 30분 step이 필요하지 않습니다. 워크플로를 suspend한 뒤 응답이 오면 다음 step을 실행하면 됩니다. 반대로 하나의 외부 API가 15분 동안 스트리밍하고 결과를 한 번에 반환한다면 extended duration이 유용할 수 있습니다.

AI SDK 7 프로덕션 에이전트 가이드에서 설명한 WorkflowAgent도 같은 기준을 따릅니다. 에이전트 루프 전체를 한 함수에 가두는 것이 아니라 도구 호출을 durable step으로 나누는 것이 핵심입니다.

extended duration을 활성화합니다

Workflow step의 30분 상한을 사용하려면 프로젝트 환경 변수에 다음 값을 추가하고 다시 배포합니다.

VERCEL_ENABLE_WORKFLOW_EXTENDED_MAX_DURATION=1

필요 조건은 다음과 같습니다.

  • Pro 또는 Enterprise 플랜
  • Fluid compute 활성화
  • 지원되는 Node.js 또는 Python 런타임
  • 환경 변수 추가 후 재배포

일반 Vercel Function은 함수 코드나 vercel.jsonmaxDuration으로 실행 시간을 설정할 수 있습니다.

// app/api/long-task/route.ts
export const maxDuration = 1800;

export async function POST() {
  return Response.json({ ok: true });
}
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "functions": {
    "api/long-task.py": {
      "maxDuration": 1800
    }
  }
}

Workflow step에는 Vercel이 안내한 환경 변수를 적용하고, 일반 route나 handler에는 필요한 경우에만 maxDuration을 명시하세요.

하나의 긴 step을 사용할 때

긴 step은 중간 상태를 나누기 어렵고 한 번의 외부 작업으로 완료되는 경우에 적합합니다.

  • 긴 LLM reasoning과 여러 tool call이 하나의 provider session에 묶인 작업
  • 하나의 큰 PDF에 대한 OCR과 구조 추출
  • 로그인된 browser session을 유지해야 하는 자동화
  • 영상·오디오 한 파일의 변환 또는 분석
  • 외부 SaaS의 동기식 job 완료 대기

다음 작업은 여러 step으로 나누는 편이 좋습니다.

  • 파일 수백 개를 같은 방식으로 처리
  • 독립적인 URL을 순회하는 scraping
  • 여러 고객이나 tenant의 배치 작업
  • 각 단계 결과를 검토하거나 재사용하는 파이프라인
  • 실패 시 처음부터 다시 실행하면 비용이 큰 작업

판단 기준은 실행 시간이 아니라 재시도 단위와 복구 지점입니다.

비싼 작업 전후에 checkpoint를 둡니다

문서 처리 파이프라인은 다운로드, 추출, 인덱싱을 분리할 수 있습니다.

export async function processDocumentWorkflow(documentId: string) {
  'use workflow';

  const source = await downloadDocument(documentId);
  const extracted = await extractDocument(source);
  const indexed = await indexDocument(documentId, extracted);

  return { documentId, indexed };
}

async function downloadDocument(documentId: string) {
  'use step';
  return fetchSource(documentId);
}

async function extractDocument(source: SourceDocument) {
  'use step';
  return runExtraction(source);
}

async function indexDocument(
  documentId: string,
  extracted: ExtractedDocument,
) {
  'use step';
  return writeIndex({ documentId, extracted });
}

extractDocument가 20분 걸리더라도 전후를 분리하면 다운로드 성공 결과를 재사용하고, 인덱싱 실패 때문에 비싼 OCR을 반복하지 않을 수 있습니다.

stepId를 idempotency key로 사용합니다

Workflow step은 실패하면 재시도될 수 있습니다. 네트워크 오류 뒤 외부 변경은 성공했지만 응답만 유실되면 같은 쓰기가 두 번 실행될 수 있습니다.

getStepMetadata()가 제공하는 stepId는 재시도 사이에 안정적으로 유지되므로 외부 API의 idempotency key로 사용할 수 있습니다.

import { getStepMetadata } from 'workflow';

async function publishRelease(releaseId: string) {
  'use step';

  const { stepId } = getStepMetadata();

  const response = await fetch('https://api.example.com/releases', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Idempotency-Key': stepId,
    },
    body: JSON.stringify({ releaseId }),
  });

  if (!response.ok) {
    throw new Error(`publish failed: ${response.status}`);
  }

  return response.json();
}

외부 API가 idempotency key를 지원하지 않는다면 애플리케이션 데이터베이스에 stepId와 결과를 저장하고, 같은 key가 들어오면 기존 결과를 반환하세요.

오류를 분류하고 재시도 한도를 정합니다

모든 오류를 같은 방식으로 반복하면 안 됩니다.

  • 429·5xx·일시적인 네트워크 오류: 지연 후 재시도
  • 잘못된 입력과 권한 오류: 즉시 실패
  • 외부 쓰기: idempotency가 보장될 때만 재시도
  • 비용이 큰 LLM·OCR: 이미 소비한 비용과 오류 유형을 확인한 뒤 제한적으로 재시도

Workflow step은 maxRetries를 조정할 수 있습니다.

async function callFlakyProvider(input: ProviderInput) {
  'use step';
  return provider.run(input);
}

callFlakyProvider.maxRetries = 5;

중복 실행 위험이 있고 idempotency를 구현하지 못한 step은 재시도를 끄는 편이 낫습니다.

unsafeExternalWrite.maxRetries = 0;

30분 step에 여러 번의 재시도를 허용하면 최악의 실행 시간과 외부 API 비용이 크게 늘어납니다. duration과 retry budget을 함께 계산하세요.

취소는 플랫폼 timeout과 별도로 설계합니다

Workflow SDK 5 beta는 workflow와 step 경계를 지나는 표준 AbortControllerAbortSignal을 지원합니다.

import { sleep } from 'workflow';

export async function cancellableWorkflow() {
  'use workflow';

  const controller = new AbortController();

  const result = await Promise.race([
    runLongAnalysis(controller.signal),
    sleep('20m').then(() => null),
  ]);

  if (result === null) {
    controller.abort('analysis exceeded workflow budget');
    return { status: 'cancelled' };
  }

  return { status: 'completed', result };
}

async function runLongAnalysis(signal: AbortSignal) {
  'use step';

  const response = await fetch('https://api.example.com/analyze', { signal });
  return response.json();
}

취소는 cooperative입니다. 외부 SDK가 AbortSignal을 확인하지 않으면 실제 작업이 멈추지 않습니다. provider가 비동기 job API를 사용한다면 job ID를 저장하고 별도의 cancel endpoint를 호출해야 합니다.

30분을 애플리케이션 timeout으로 그대로 쓰지 않습니다

플랫폼 상한이 1,800초라면 정상 작업 제한은 그보다 짧아야 합니다. 종료 직전에는 결과 저장, 로그 flush와 cleanup 시간이 필요합니다.

예시 예산은 다음과 같습니다.

구간예시 예산
외부 작업20분
제한적 재시도5분
결과 저장과 cleanup2분
안전 여유3분

정상 작업이 매번 29분을 사용한다면 timeout을 늘린 것이 아니라 장애를 뒤로 미룬 것입니다. 입력 크기를 제한하거나 chunk로 나누세요.

비용은 경과 시간과 active CPU를 분리합니다

Vercel은 Fluid compute에서 코드가 실제 CPU를 사용하는 시간과 provisioned memory를 기준으로 비용을 계산하며, 외부 모델이나 데이터베이스를 기다리는 I/O 구간에는 active CPU 과금이 멈춘다고 설명합니다.

운영 지표에는 다음 항목을 분리하세요.

  • 전체 경과 시간
  • active CPU 시간과 memory
  • 모델·외부 API 비용
  • 재시도 횟수와 누적 실행 시간
  • 처리한 입력 크기

20분 동안 모델 응답을 기다리는 step과 20분 동안 CPU로 영상을 변환하는 step은 비용 성격이 다릅니다. 실제 workload로 측정하세요.

run ID와 step ID를 관측성에 연결합니다

Workflow dashboard는 step progression, payload, output, retry와 timing을 보여줍니다. Vercel Logs에서는 Workflow Run ID와 Workflow Step ID로 로그를 필터링할 수 있습니다.

workflow 함수에서 run ID가 필요하면 getWorkflowMetadata()를 사용하고, step 함수에서는 getStepMetadata()stepId와 현재 attempt를 읽습니다.

import { getStepMetadata, getWorkflowMetadata } from 'workflow';

export async function observedWorkflow(documentId: string) {
  'use workflow';

  const { workflowRunId } = getWorkflowMetadata();
  return extractDocument(documentId, workflowRunId);
}

async function extractDocument(documentId: string, workflowRunId: string) {
  'use step';

  const { stepId, attempt } = getStepMetadata();
  const startedAt = Date.now();

  try {
    const result = await runExtraction(documentId);

    console.info('document extraction completed', {
      workflowRunId,
      stepId,
      attempt,
      elapsedMs: Date.now() - startedAt,
    });

    return result;
  } catch (error) {
    console.error('document extraction failed', {
      workflowRunId,
      stepId,
      attempt,
      elapsedMs: Date.now() - startedAt,
      error: error instanceof Error ? error.message : String(error),
    });

    throw error;
  }
}

민감한 문서 본문이나 모델 응답 전체를 로그에 저장하지 마세요. 식별자, 크기, 상태와 오류 유형만으로도 대부분의 운영 문제를 분석할 수 있습니다.

배포 전 실패 시나리오를 테스트합니다

최소한 다음 상황을 검증하세요.

  1. 800초를 넘지만 정상적으로 끝나는 step
  2. 애플리케이션 timeout으로 중단되는 step
  3. Vercel Function 상한에 도달하는 step
  4. 외부 변경 성공 뒤 응답이 유실되어 재시도되는 step
  5. 배포 또는 프로세스 재시작 뒤 checkpoint에서 복구되는 workflow
  6. 사용자 취소가 진행 중인 외부 요청까지 중단하는지
  7. 동일 입력이 두 번 들어와도 결과가 중복되지 않는지

30분을 실제 테스트에서 기다리기 어렵다면 시간 의존 코드를 주입 가능한 clock이나 짧은 test budget으로 구성하세요. 플랫폼의 실제 duration 제한은 별도의 staging 테스트로 확인해야 합니다.

운영 체크리스트

  • Pro 또는 Enterprise 플랜과 Fluid compute 사용 여부를 확인했습니다.
  • VERCEL_ENABLE_WORKFLOW_EXTENDED_MAX_DURATION=1을 설정하고 재배포했습니다.
  • 긴 step이 하나의 원자적 작업인지 검토했습니다.
  • 비싼 단계 전후에 복구 가능한 checkpoint가 있습니다.
  • 외부 쓰기에 안정적인 idempotency key가 있습니다.
  • 오류 유형별 재시도 횟수와 지연을 정했습니다.
  • application timeout이 플랫폼 상한보다 짧습니다.
  • 취소 신호가 실제 외부 작업까지 전달됩니다.
  • run ID와 step ID로 로그를 연결할 수 있습니다.
  • 재시도를 포함한 최악의 실행 시간과 비용을 계산했습니다.
  • beta 기능 변경에 대비해 검증 날짜를 기록했습니다.

결론

Workflow step의 30분 지원은 긴 에이전트 작업을 억지로 여러 서버에 나누지 않아도 되는 범위를 넓혔습니다. 하지만 가장 좋은 step은 오래 실행되는 step이 아니라 실패했을 때 안전하게 다시 실행할 수 있는 단위입니다.

긴 단일 외부 작업에는 extended duration을 사용하되, 여러 독립 작업은 durable step으로 나누세요. 쓰기 작업에는 idempotency key를 붙이고, 재시도·취소·application timeout을 플랫폼 상한과 별도로 관리해야 합니다. 마지막으로 30분 상한을 활성화하기 전에 staging에서 실제 비용과 복구 동작을 측정하세요.

공식 자료