
## 단일 파일 워크플로

워크플로 전체를 하나의 PHP 파일에 담을 수 있습니다. 단계 클래스와 빌더를 함께 정의하고 파일을 디렉터리에 넣은 다음, `Queuety::load_workflows()`로 해당 디렉터리를 불러오세요(아래 참조). Queuety는 자체적으로 디렉터리를 검색하지 않습니다.

```php
// workflows/onboard-user.php
use Queuety\Queuety;

class CreateAccount implements \Queuety\Step {
    public function handle(array $state): array {
        $user = create_user($state['email']);
        return ['user_id' => $user->id, 'user_name' => $user->name];
    }
    public function config(): array {
        return ['max_attempts' => 3];
    }
}

class SendWelcome implements \Queuety\Step {
    public function handle(array $state): array {
        wp_mail($state['email'], 'Welcome ' . $state['user_name'], '...');
        return ['welcomed' => true];
    }
    public function config(): array {
        return [];
    }
}

return Queuety::define_workflow('onboard_user')
    ->then(CreateAccount::class, 'create_account')
    ->then(SendWelcome::class, 'send_welcome');
```

파일은 `WorkflowBuilder`을 반환합니다. 각 단계 클래스는 인라인으로 정의됩니다. 파일을 불러오면 클래스를 사용할 수 있게 되고 워크플로가 템플릿으로 등록됩니다.

## 워크플로 불러오기

디렉터리에서 모든 워크플로 파일을 불러오세요.

```php
// Load all .php files from the workflows directory
$count = Queuety::load_workflows(__DIR__ . '/workflows/');
```

또는 파일 하나를 불러오세요.

```php
$template = Queuety::load_workflow_file(__DIR__ . '/workflows/onboard-user.php');
```

그런 다음 이름으로 디스패치하세요.

```php
$workflow_id = Queuety::run_workflow('onboard_user', ['email' => 'user@example.com']);
```

## 재귀적으로 불러오기

하위 디렉터리를 검색하려면 두 번째 인수로 `true`을 전달하세요.

```php
Queuety::load_workflows(__DIR__ . '/workflows/', recursive: true);
```

이렇게 하면 워크플로를 폴더별로 정리할 수 있습니다.

```
workflows/
├── onboarding/
│   ├── new-user.php
│   └── trial-upgrade.php
├── reports/
│   ├── daily-summary.php
│   └── weekly-digest.php
└── notifications/
    └── digest-email.php
```

## 모든 워크플로 기능 사용

파일 기반 워크플로는 템플릿에 저장되지 않는 `idempotency_key()`을 제외하고 빌더의 모든 기능을 지원합니다. 해당 옵션은 `Queuety::run_workflow( $name, $payload, [ 'idempotency_key' => '...' ] )`을 사용해 디스패치별로 전달하세요. 대기 지연, 신호, 병렬 단계, 조건 분기, 워크플로 실행, 취소 핸들러 및 상태 가지치기를 모두 사용할 수 있습니다.

```php
// workflows/approval-flow.php
use Queuety\Queuety;

class SubmitRequest implements \Queuety\Step {
    public function handle(array $state): array {
        return ['submitted_at' => date('Y-m-d H:i:s')];
    }
    public function config(): array { return []; }
}

class ProcessApproval implements \Queuety\Step {
    public function handle(array $state): array {
        return [
            'approved_by' => $state['approved_by'],
            'processed' => true,
        ];
    }
    public function config(): array { return []; }
}

class CleanupRequest implements \Queuety\Handler {
    public function handle(array $payload): void {
        delete_pending_request($payload['request_id']);
    }
    public function config(): array { return []; }
}

return Queuety::define_workflow('approval_flow')
    ->then(SubmitRequest::class)
    ->delay(hours: 24)
    ->wait_for_signal('approved')
    ->then(ProcessApproval::class)
    ->on_cancel(CleanupRequest::class)
    ->prune_state_after(2);
```

## 파일 기반 워크플로를 사용할 때

파일 기반 워크플로는 다음과 같은 경우에 적합합니다.

- LLM 또는 코드 생성기가 프로그래밍 방식으로 워크플로를 생성하는 경우
- 워크플로가 파일 하나에 들어갈 만큼 단순한 경우
- 단계 로직을 워크플로 정의 가까이에 두려는 경우
- 빠른 반복 작업을 위해 프로토타이핑하는 경우

공유 단계 클래스, 복잡한 미들웨어 또는 여러 워크플로에서 재사용하는 단계가 있는 대규모 워크플로에는 PSR-4 자동 로딩을 사용해 클래스를 별도 파일에 두는 표준 방식이 유지 관리에 더 적합합니다.

## 작동 방식

내부적으로 파일 워크플로우는 빌더 API와 정확히 동일한 엔진을 사용합니다. 각 단계는 데이터베이스에서 별도의 작업입니다. 상태는 `queuety_workflows` 테이블을 통해 전달됩니다. 재생이나 결정론 요구 사항이 없으며 성능 차이도 없습니다.

각 load 호출은 파일을 `require`합니다(`require_once`가 아님). 따라서 요청당 각 파일을 한 번만 로드하세요. 두 번 로드하면 클래스가 다시 선언되어 실패합니다. 반환된 `WorkflowBuilder`는 `WorkflowTemplate`로 변환되어 `WorkflowRegistry`에 저장됩니다. 디스패치와 실행은 다른 모든 워크플로우와 동일합니다.
