
# PHP API

Queuety의 모든 기능은 `Queuety\Queuety` 정적 클래스를 통해 사용할 수 있습니다. 이 클래스는 작업 디스패치, 워크플로 구성, 상태 머신 실행, 큐 관리 및 내부 하위 시스템 액세스를 위한 단일 진입점입니다.

```php
use Queuety\Queuety;
```

## 독립 실행형 코어 액세스

Queuety의 DB 기반 런타임은 WordPress 외부에서도 초기화할 수 있습니다.

이 독립 실행 경로는 오케스트레이션 코어를 위한 것입니다:

- 작업 및 워커(워커 풀에는 아래 설명된 `DB_HOST`, `DB_NAME`, `DB_USER` 및 `DB_PASSWORD` 상수도 필요합니다)
- 워크플로 및 신호
- 상태 머신 및 이벤트
- 아티팩트, 로그, 메트릭 및 일정

이는 WordPress 어댑터를 대체하지 않습니다. `Queuety::on_action()`, `replace_wp_cron()`, 플러그인 부트스트랩 및 실제 `wp queuety ...` 등록 경로에는 여전히 WordPress가 필요합니다.

### `Queuety::init( Connection $conn )`

직접 DB 연결을 사용해 Queuety를 초기화합니다.

```php
use Queuety\Connection;
use Queuety\Queuety;

$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
);

Queuety::init( $conn );
```

선택적 `table_prefix` 인수는 연결별로 런타임 테이블 이름의 Queuety 부분을 변경합니다:

```php
$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
    table_prefix: 'themequeue_',
);
```

그러면 `queuety_jobs` 같은 테이블 이름이 `wp_themequeue_jobs`로 변환됩니다.

### `Queuety::ensure_schema()`

활성 연결에 Queuety 런타임 테이블이 존재하는지 확인합니다.

```php
Queuety::init( $conn );
Queuety::ensure_schema();
```

전체 모델과 WordPress 경계는 [독립 실행형 사용](https://queuety.inline0.dev/ko/docs/features/standalone-use)을 참조하세요.

## 작업

### `Queuety::dispatch( $handler, $payload )`

작업을 디스패치합니다. 핸들러 이름 문자열이나 `Contracts\Job` 인스턴스를 받습니다. 옵션을 연결해 설정할 수 있는 `PendingJob` 빌더를 반환합니다.

```php
// Handler name + payload (original API)
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

// Contracts\Job instance (v0.6.0+)
Queuety::dispatch( new SendEmailJob( to: 'user@example.com' ) );

// With options
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 )
    ->rate_limit( 10, 60 )
    ->unique()
    ->after( $other_job_id );

// Get the job ID
$job_id = Queuety::dispatch( 'send_email', $payload )->id();
```

`Contracts\Job` 인스턴스를 전달하면 직렬화기가 FQCN을 핸들러 이름으로, 공개 속성을 페이로드로 추출합니다. `$payload` 매개변수는 `$handler`가 `Job` 인스턴스인 경우 무시됩니다.

### `PendingJob` 메서드

| 메서드 | 설명 |
|---|---|
| `on_queue( string $queue )` | 대상 큐 설정 |
| `with_priority( Priority $priority )` | 우선순위 설정 |
| `delay( int $seconds )` | 작업을 사용할 수 있게 되기까지의 지연 시간 설정 |
| `max_attempts( int $max )` | 최대 재시도 횟수 설정 |
| `rate_limit( int $max, int $window )` | 속도 제한: 시간 구간당 최대 실행 횟수 설정 |
| `unique()` | 중복 디스패치 방지 |
| `after( int $job_id )` | 다른 작업이 먼저 완료될 때까지 대기 |
| `concurrency_group( string $group, int $limit )` | 작업을 공유 워커 승인 그룹에 추가 |
| `cost_units( int $units )` | 디스패치에 상대 실행 비용 메타데이터 첨부 |
| `id()` | 강제 디스패치 후 작업 ID 반환 |

`concurrency_group()` 및 `cost_units()`은 더 광범위한 [리소스 관리](https://queuety.inline0.dev/ko/docs/features/resource-management) 모델에 포함되므로, 동일한 디스패치에서 개수 기반 동시성 제한, 가중치가 적용된 큐 또는 제공자별 예산, 워커 승인 휴리스틱을 사용할 수 있습니다.

### `Queuety::dispatch_sync( $handler, $payload )`

큐에 추가하지 않고 현재 프로세스에서 작업을 동기적으로 실행합니다.

```php
// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hi', body: 'Hello' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );
```

작업의 `handle()` 메서드를 직접 호출합니다. 데이터베이스 행을 만들지 않고, 워커를 사용하지 않으며, 미들웨어를 적용하지 않습니다.

### `Queuety::batch( $jobs )`

여러 작업을 하나의 다중 행 INSERT로 디스패치합니다.

```php
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ], 'queue' => 'emails' ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ], 'priority' => 2 ],
] );
```

### `Queuety::create_batch( $jobs )`

콜백과 진행률 추적 기능을 사용해 작업 그룹을 디스패치하는 `BatchBuilder`를 만듭니다. 자세한 내용은 [일괄 처리](https://queuety.inline0.dev/ko/docs/features/batching)를 참조하세요.

```php
$batch = Queuety::create_batch( [
    new ImportUsersJob( $chunk_1 ),
    new ImportUsersJob( $chunk_2 ),
] )
    ->name( 'Import users' )
    ->then( ImportCompleteHandler::class )
    ->catch( ImportFailedHandler::class )
    ->finally( ImportCleanupHandler::class )
    ->allow_failures()
    ->on_queue( 'imports' )
    ->dispatch();
```

### `BatchBuilder` 메서드

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `name( string $name )` | `self` | 일괄 작업 이름 설정 |
| `on_queue( string $queue )` | `self` | 일괄 작업의 모든 작업에 사용할 큐 설정 |
| `then( string $handler_class )` | `self` | 일괄 작업이 성공적으로 완료될 때 호출되는 핸들러 설정 |
| `catch( string $handler_class )` | `self` | 일괄 작업에서 첫 작업이 실패할 때 호출되는 핸들러 설정 |
| `finally( string $handler_class )` | `self` | 일괄 작업이 완료될 때(성공 또는 실패) 호출되는 핸들러 설정 |
| `allow_failures()` | `self` | 실패가 있어도 `then` 콜백이 실행되도록 허용 |
| `dispatch()` | `Batch` | 일괄 작업 행과 모든 작업을 만들고, `Batch` 값 객체 반환 |

### `Queuety::find_batch( $id )`

ID로 일괄 작업을 찾습니다. `Batch` 값 객체 또는 `null`를 반환합니다.

```php
$batch = Queuety::find_batch( $batch_id );

if ( null !== $batch ) {
    echo $batch->progress() . '% complete';
}
```

## 워커 제어

### `Queuety::run_worker( $queue = 'default', $once = false )`

워커 루프 하나를 직접 실행합니다.

```php
Queuety::run_worker( 'default' );
Queuety::run_worker( 'critical,default', true );
```

### `Queuety::run_worker_pool( $workers, $queue = 'default' )`

고정 크기 워커 풀을 직접 실행합니다. `pcntl` 및 `posix` 확장이 필요합니다. 풀은 자식 연결에 WordPress `DB_HOST`, `DB_NAME`, `DB_USER` 및 `DB_PASSWORD` 상수를 사용합니다(테이블 접두사는 초기화된 연결에서 가져옴). 따라서 WordPress 외부에서 풀을 실행할 때는 해당 상수를 정의해야 합니다([#5](https://github.com/inline0/queuety/issues/5)).

```php
Queuety::run_worker_pool( 4, 'providers' );
```

### `Queuety::run_auto_scaling_worker_pool( $min_workers, $max_workers, $queue = 'default' )`

적응형 워커 풀을 직접 실행합니다. 요구 사항은 `run_worker_pool()`과 동일합니다.

```php
Queuety::run_auto_scaling_worker_pool( 2, 6, 'providers' );
```

### `Batch` 값 객체

| 메서드/속성 | 유형 | 설명 |
|---|---|---|
| `$id` | `int` | 일괄 작업 ID |
| `$name` | `string\|null` | 선택적 일괄 작업 이름 |
| `$total_jobs` | `int` | 일괄 작업의 전체 작업 수 |
| `$pending_jobs` | `int` | 아직 대기 중인 작업 수 |
| `$failed_jobs` | `int` | 실패한 작업 수 |
| `$failed_job_ids` | `array` | 실패한 작업의 ID |
| `$created_at` | `DateTimeImmutable` | 일괄 작업을 생성한 시점 |
| `$finished_at` | `DateTimeImmutable\|null` | 일괄 작업이 완료된 시점 |
| `$cancelled_at` | `DateTimeImmutable\|null` | 일괄 작업이 취소된 시점 |
| `progress()` | `int` | 완료 비율(0-100) |
| `finished()` | `bool` | 일괄 작업 완료 여부 |
| `has_failures()` | `bool` | 실패한 작업이 있는지 여부 |
| `cancelled()` | `bool` | 일괄 작업 취소 여부 |

### `Queuety::chain( $jobs )`

순차 작업 실행을 위한 `ChainBuilder`를 만듭니다. 각 작업은 이전 작업이 성공적으로 완료되어야 실행됩니다.

```php
$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();
```

### `ChainBuilder` 메서드

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `on_queue( string $queue )` | `self` | 체인의 모든 작업에 사용할 큐 설정 |
| `catch( string $handler_class )` | `self` | 체인의 작업이 영구적으로 묻혔을 때 호출되는 핸들러 설정 |
| `dispatch()` | `int` | 순차적 종속성이 있는 모든 작업을 만들고 첫 작업 ID 반환 |

### `Queuety::retry( $job_id )`

특정 작업을 재시도하고 상태를 `pending`로 초기화합니다.

```php
Queuety::retry( 42 );
```

### `Queuety::retry_buried()`

묻힌 모든 작업을 재시도합니다. 재시도한 작업 수를 반환합니다.

```php
$count = Queuety::retry_buried();
```

### `Queuety::purge( $older_than_days )`

완료된 작업을 삭제합니다. 삭제한 작업 수를 반환합니다.

```php
$count = Queuety::purge();     // uses QUEUETY_RETENTION_DAYS
$count = Queuety::purge( 30 ); // purge jobs older than 30 days
```

## 큐

### `Queuety::stats( $queue )`

상태별 작업 수를 확인합니다.

```php
$stats = Queuety::stats();          // all queues
$stats = Queuety::stats( 'emails' ); // specific queue
// ['pending' => 12, 'processing' => 3, 'completed' => 150, 'failed' => 2, 'buried' => 1]
```

### `Queuety::buried( $queue )`

묻힌 작업을 모두 가져옵니다.

```php
$jobs = Queuety::buried();
$jobs = Queuety::buried( 'emails' );
```

### `Queuety::pause( $queue )`

워커가 건너뛰도록 큐를 일시 중지합니다.

```php
Queuety::pause( 'emails' );
```

### `Queuety::resume( $queue )`

일시 중지된 큐를 다시 시작합니다.

```php
Queuety::resume( 'emails' );
```

### `Queuety::is_paused( $queue )`

큐가 일시 중지되었는지 확인합니다.

```php
if ( Queuety::is_paused( 'emails' ) ) {
    // Queue is paused
}
```

## 워크플로

### `Queuety::workflow( $name )`

워크플로 구성을 시작합니다. `WorkflowBuilder`를 반환합니다.

```php
$workflow_id = Queuety::workflow( 'generate_report' )
    ->then( FetchDataHandler::class )
    ->then( CallLLMHandler::class )
    ->then( FormatOutputHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

### `WorkflowBuilder` 메서드

| 메서드 | 설명 |
|---|---|
| `then( string $handler_class, ?string $name = null )` | 순차 단계를 추가합니다. 조건 분기용 이름을 선택적으로 지정할 수 있습니다. |
| `parallel( array $handler_classes )` | 병렬 단계 그룹을 추가합니다. |
| `for_each( string $items_key, string $handler_class, ?string $result_key = null, ForEachMode $mode = ForEachMode::All, ?int $quorum = null, ?string $reducer_class = null, ?string $name = null )` | 워크플로 상태의 각 항목에 대해 분기 작업 하나씩 확장하는 동적 for-each 단계를 추가합니다. |
| `repeat_while( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | 공개 상태 키 또는 `RepeatCondition` 클래스가 일치할 때까지 이전의 이름 있는 단계로 돌아갑니다. |
| `repeat_until( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | 공개 상태 키 또는 `RepeatCondition` 클래스가 일치할 때까지 이전의 이름 있는 단계로 돌아갑니다. |
| `run_workflow( string $workflow_name, WorkflowBuilder $sub_builder )` | 워크플로 실행 단계를 추가합니다. |
| `delay( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | 지속 가능한 지연 단계를 추가합니다. 모든 기간을 합산합니다. |
| `wait_for_signal( string $name, ?string $result_key = null, ?string $step_name = null, array $match_payload = [], ?string $correlation_key = null )` | 신호 대기 단계를 추가합니다. 관련 없는 이벤트를 무시하려면 `match_payload` 및 `correlation_key`을 선택적으로 지정할 수 있습니다. |
| `wait_for_signals( array $signal_names, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 여러 신호 중 모두 또는 하나를 기다립니다. 페이로드 일치 및 상관 관계를 선택적으로 지정할 수 있습니다. |
| `wait_for_approval( string $signal_name = 'approval', ?string $result_key = 'approval', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 사람의 승인 신호를 기다립니다. |
| `wait_for_decision( string $approve_signal = 'approved', string $reject_signal = 'rejected', ?string $result_key = 'decision', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 승인 또는 거부를 기다리고 구조화된 결정 페이로드를 저장합니다. |
| `wait_for_input( string $signal_name = 'input', ?string $result_key = 'input', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 사람의 입력 신호를 기다립니다. |
| `wait_for_workflow( int\|string $workflow, ?string $result_key = null, ?string $name = null )` | 워크플로 ID 하나 또는 공개 상태에 저장된 워크플로 ID의 완료를 기다립니다. |
| `wait_for_workflows( array\|string $workflows, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, ?int $quorum = null )` | 여러 워크플로 중 모두, 하나 또는 정족수의 완료를 기다립니다. |
| `wait_for_workflow_group( string $group_key, WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = null, ?string $name = null )` | 내부 그룹 키로 이전에 시작한 워크플로 그룹을 기다립니다. |
| `start_workflows( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'started_workflow_ids', string $payload_key = 'item', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | 런타임 항목마다 최상위 워크플로 하나를 디스패치하고, 시작된 집합에 지속 가능한 그룹 키를 선택적으로 지정합니다. |
| `start_agents( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'agent_workflow_ids', string $payload_key = 'agent_task', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | 에이전트용 `start_workflows()` 별칭입니다. |
| `wait_for_agents( array\|string $workflows = 'agent_workflow_ids', WaitMode $mode = WaitMode::All, ?string $result_key = 'agent_results', ?string $name = null, ?int $quorum = null )` | 에이전트용 `wait_for_workflows()` 별칭입니다. |
| `wait_for_agent_group( string $group_key = 'agents', WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = 'agent_results', ?string $name = null )` | 에이전트용 `wait_for_workflow_group()` 별칭입니다. |
| `version( string $version )` | 검사 및 내보내기를 위해 워크플로 정의에 애플리케이션 수준 버전을 지정합니다. |
| `idempotency_key( string $key )` | 호출자가 제공한 키로 워크플로 디스패치를 지속 가능하게 합니다. 같은 키로 다시 디스패치하면 원래 워크플로 ID를 반환합니다. |
| `max_transitions( int $max )` | 단계 전환이 `$max`회를 초과해 완료되면 워크플로를 실패 처리합니다. |
| `max_for_each_items( int $max )` | `for_each()` 단계가 `$max`개를 초과하는 항목을 확장하려고 하면 워크플로를 실패 처리합니다. |
| `max_state_bytes( int $bytes )` | 공개 상태가 `$bytes`를 초과해 커지면 워크플로를 실패 처리합니다. |
| `max_cost_units( int $max )` | 완료된 단계가 `$max`비용 단위를 초과해 소비하면 워크플로를 실패 처리합니다. |
| `max_started_workflows( int $max )` | 자식 워크플로를 `$max`개 초과해 디스패치하기 전에 워크플로를 실패 처리합니다. |
| `compensate_with( string $handler_class )` | 가장 최근에 추가한 단계에 보상 핸들러를 연결합니다. |
| `compensate_on_failure()` | 워크플로 실패 시 완료된 단계의 보상 처리를 자동으로 실행합니다. |
| `on_queue( string $queue )` | 모든 단계에 사용할 큐를 설정합니다. |
| `with_priority( Priority $priority )` | 모든 단계의 우선순위를 설정합니다. |
| `max_attempts( int $max )` | 단계별 최대 재시도 횟수를 설정합니다. |
| `on_cancel( string $handler_class )` | 취소 시 실행할 정리 핸들러 클래스를 등록합니다. [취소](https://queuety.inline0.dev/ko/docs/workflows/cancellation)를 참조하세요. |
| `prune_state_after( int $steps = 2 )` | 상태 자동 정리를 활성화합니다. 임계값보다 오래된 단계 출력을 제거합니다. [상태 정리](https://queuety.inline0.dev/ko/docs/features/state-pruning)를 참조하세요. |
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | 워크플로 기한을 설정합니다. 모든 기간을 합산합니다. [기한](https://queuety.inline0.dev/ko/docs/workflows/deadlines)을 참조하세요. |
| `on_deadline( string $handler_class )` | 워크플로 기한 초과 시 호출되는 핸들러를 등록합니다. [기한](https://queuety.inline0.dev/ko/docs/workflows/deadlines)을 참조하세요. |
| `dispatch( array $initial_payload = [] )` | 워크플로를 디스패치하고 워크플로 ID를 반환합니다. |

### `Queuety::workflow_status( $workflow_id )`

워크플로 상태를 가져옵니다. `WorkflowState` 객체 또는 `null`를 반환합니다.

```php
$state = Queuety::workflow_status( $workflow_id );
echo $state->name;          // 'generate_report'
echo $state->status->value; // 'running'
echo $state->current_step;  // 1
echo $state->total_steps;   // 3
print_r( $state->state );   // accumulated public state array
echo $state->wait_type;     // 'signal' or 'workflow' while blocked
echo $state->wait_mode;     // 'all', 'any', or 'quorum'
echo $state->definition_version; // 'research.v2'
echo $state->definition_hash;    // '4c7a...'
echo $state->idempotency_key;    // 'brief:42:research'
echo $state->artifact_count;     // 2
print_r( $state->budget );       // configured workflow budget + current usage
print_r( $state->wait_details ); // matched/remaining blockers, result key, correlation info
print_r( $state->artifact_keys ); // ['draft', 'citations']
```

`WorkflowState`에는 다음 기능도 있습니다:

- `waiting_for`: 현재 실행을 차단하는 신호 이름 또는 워크플로 ID
- `current_step_name`: 현재 단계에 설정된 이름(있는 경우)
- `wait_mode`: 차단 중인 현재 대기 모드(`all`, `any` 또는 `quorum`)
- `wait_details`: 일치한 차단 요소와 남은 차단 요소 등 추가 검사 데이터
- `definition_version`: `version()`에서 설정한 값(있는 경우)
- `definition_hash`: 워크플로 정의의 결정론적 해시
- `idempotency_key`: 영구 디스패치 키(있는 경우)
- `budget`: 가드레일이 활성화된 경우 설정된 한도와 현재 사용량
- `artifact_count`: 워크플로에 저장된 아티팩트 수
- `artifact_keys`: 빠르게 확인할 수 있도록 저장된 아티팩트 키

### `Queuety::retry_workflow( $workflow_id )`

실패한 단계부터 실패한 워크플로를 다시 시도합니다.

```php
Queuety::retry_workflow( $workflow_id );
```

실패 후 워크플로에 이미 보상 처리가 수행된 경우 `RuntimeException`을 발생시킵니다.

### `Queuety::pause_workflow( $workflow_id )`

실행 중인 워크플로를 일시 중지합니다. 현재 단계에서 이미 대기열에 추가된 작업은 계속 완료될 수 있지만, 워크플로를 재개할 때까지 이후 단계는 대기열에 추가되지 않습니다.

```php
Queuety::pause_workflow( $workflow_id );
```

### `Queuety::resume_workflow( $workflow_id )`

일시 중지된 워크플로를 재개합니다.

```php
Queuety::resume_workflow( $workflow_id );
```

### `Queuety::cancel_workflow( $workflow_id )`

워크플로를 취소하고 등록된 정리 핸들러를 실행합니다. 상태를 `cancelled`으로 설정하고 워크플로의 모든 대기 및 처리 중 작업을 묻어 둡니다.

```php
Queuety::cancel_workflow( $workflow_id );
```

워크플로가 이미 `completed` 또는 `cancelled`인 경우 `RuntimeException`을 발생시킵니다. 자세한 내용은 [취소](https://queuety.inline0.dev/ko/docs/workflows/cancellation)를 참조하세요.

### `Queuety::rewind_workflow( $workflow_id, $to_step )`

워크플로를 이전 단계로 되돌려 해당 단계부터 다시 실행합니다. 지정된 단계의 이벤트 로그 `state_after`에서 상태를 복원하고 다음 단계를 대기열에 추가합니다.

```php
Queuety::rewind_workflow( $workflow_id, to_step: 1 );
```

대상 단계에 기록된 상태가 없으면 `RuntimeException`을 발생시킵니다. [워크플로 이벤트 로그](https://queuety.inline0.dev/ko/docs/workflows/event-log)가 필요합니다. 자세한 내용은 [시간 이동](https://queuety.inline0.dev/ko/docs/workflows/time-travel)을 참조하세요.

### `Queuety::fork_workflow( $workflow_id )`

실행 중인 워크플로를 현재 상태에서 독립된 사본으로 분기합니다. 새 워크플로 ID를 반환합니다.

```php
$forked_id = Queuety::fork_workflow( $workflow_id );
```

분기된 워크플로는 자체 ID를 가지며 원본과 독립적으로 작동합니다. 자세한 내용은 [분기](https://queuety.inline0.dev/ko/docs/workflows/forking)를 참조하세요.

### `Queuety::export_workflow( $workflow_id )`

워크플로의 전체 실행 기록을 JSON 직렬화 가능한 배열로 내보냅니다. 워크플로 행, 모든 작업, 워크플로 이벤트 및 로그 항목이 포함됩니다.

```php
$data = Queuety::export_workflow( $workflow_id );
```

[내보내기 및 재생](https://queuety.inline0.dev/ko/docs/workflows/export-replay)에서 JSON 구조와 사용 사례를 확인하세요.

### `Queuety::replay_workflow( $data )`

현재 환경에서 내보낸 워크플로를 재생합니다. 내보내기 데이터로 새 워크플로를 만들고 새 워크플로 ID를 반환합니다.

```php
$new_id = Queuety::replay_workflow( $data );
```

자세한 내용은 [내보내기 및 재생](https://queuety.inline0.dev/ko/docs/workflows/export-replay)을 참조하세요.

### `Queuety::replace_wp_cron()`

기본 제공 WordPress cron 시스템을 Queuety의 스케줄러로 교체합니다. `wp_schedule_event()`, `wp_unschedule_event()` 및 `wp_get_scheduled_event()`를 가로채고 반복 이벤트를 Queuety 워커로 전달합니다.

```php
Queuety::replace_wp_cron();
```

WordPress가 로드된 경우에만 작동합니다. 자세한 내용은 [WP-Cron 교체](https://queuety.inline0.dev/ko/docs/features/wp-cron)를 참조하세요.

### `Queuety::restore_wp_cron()`

WordPress 기본 cron 동작을 복원하려면 Queuety cron 브리지 훅을 제거합니다.

```php
Queuety::restore_wp_cron();
```

제한 사항은 [WP-Cron 교체](https://queuety.inline0.dev/ko/docs/features/wp-cron)를 참조하세요.

### `Queuety::signal( $workflow_id, $name, $data )`

워크플로에 신호를 보냅니다. 워크플로가 현재 이 신호를 기다리고 있으면 즉시 재개됩니다. 그렇지 않으면 신호가 저장되었다가 워크플로가 해당 `wait_for_signal` 단계에 도달하면 처리됩니다.

```php
Queuety::signal( $workflow_id, 'approved' );

// With data merged into workflow state
Queuety::signal( $workflow_id, 'approved', [
    'approved_by' => 'admin@example.com',
] );
```

자세한 내용은 [워크플로 신호](https://queuety.inline0.dev/ko/docs/workflows/signals)를 참조하세요.

### `Queuety::approve_workflow( $workflow_id, $data = [], $signal_name = 'approval' )`

승인 흐름을 위한 `signal()`의 의미상 래퍼입니다.

```php
Queuety::approve_workflow( $workflow_id, [
    'reviewer' => 'editor@example.com',
] );
```

### `Queuety::reject_workflow( $workflow_id, $data = [], $signal_name = 'rejected' )`

거부 흐름을 위한 `signal()`의 의미상 래퍼입니다.

```php
Queuety::reject_workflow( $workflow_id, [
    'reason' => 'needs citations',
] );
```

### `Queuety::submit_workflow_input( $workflow_id, $data = [], $signal_name = 'input' )`

구조화된 사용자 입력을 위한 `signal()`의 의미상 래퍼입니다.

```php
Queuety::submit_workflow_input( $workflow_id, [
    'notes' => 'Ship after legal review',
] );
```

### `Queuety::put_artifact( $workflow_id, $artifact_key, $content, $kind = 'json', $step_index = null, $metadata = [] )`

워크플로에 영구 아티팩트 하나를 저장합니다.

```php
Queuety::put_artifact(
    $workflow_id,
    'research_brief',
    [ 'summary' => 'done' ],
    'json',
    metadata: [ 'source' => 'agent' ],
);
```

### `Queuety::put_current_artifact( $artifact_key, $content, $kind = 'json', $metadata = [] )`

현재 실행 중인 워크플로 단계에 아티팩트 하나를 저장합니다.

```php
Queuety::put_current_artifact(
    'draft',
    [ 'status' => 'ready' ],
    'json',
);
```

### `Queuety::workflow_artifact( $workflow_id, $artifact_key )`

워크플로에 저장된 아티팩트 하나를 가져옵니다.

```php
$artifact = Queuety::workflow_artifact( $workflow_id, 'draft' );
```

### `Queuety::workflow_artifacts( $workflow_id, $include_content = false )`

워크플로의 아티팩트 목록을 표시합니다.

```php
$artifacts = Queuety::workflow_artifacts( $workflow_id );
```

### `Queuety::delete_workflow_artifact( $workflow_id, $artifact_key )`

워크플로의 아티팩트 하나를 삭제합니다.

```php
Queuety::delete_workflow_artifact( $workflow_id, 'draft' );
```

## 상태 머신

### `Queuety::machine( $name )`

상태 머신을 만들기 시작합니다. `StateMachineBuilder`를 반환합니다.

```php
use Queuety\Enums\StateMachineStatus;

$machine_id = Queuety::machine( 'agent_session' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'completed' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );
```

### `StateMachineBuilder` 메서드

| 메서드 | 설명 |
|---|---|
| `initial( string $state_name )` | 초기 상태를 명시적으로 설정합니다. |
| `state( string $state_name, ?StateMachineStatus $terminal_status = null )` | 상태 하나의 구성을 시작합니다. |
| `action( string\|array $action_class )` | 현재 상태에 대기열 방식의 진입 작업을 등록합니다. 클래스 이름 또는 `class`, `payload`, `config`가 포함된 구조화된 정의를 전달합니다. `payload`는 `StateAction::handle()`에 `$payload`로 전달됩니다. |
| `on( string $event, string $target, string\|array\|null $guard_class = null, ?string $name = null )` | 현재 상태에서 이벤트 전환 하나를 등록합니다. 가드는 `action()`과 같은 구조화된 형식을 허용합니다. |
| `on_queue( string $queue )` | 대기열 방식 상태 작업에 사용할 큐를 설정합니다. |
| `with_priority( Priority $priority )` | 대기열 방식 상태 작업의 우선순위를 설정합니다. |
| `max_attempts( int $max_attempts )` | 대기열 방식 상태 작업의 재시도 한도를 설정합니다. |
| `version( string $version )` | 머신 정의에 애플리케이션 수준 버전을 태그로 지정합니다. |
| `idempotency_key( string $key )` | 호출자가 제공한 키를 기준으로 디스패치를 영구적으로 처리합니다. 같은 키로 다시 디스패치하면 원래 머신 ID를 반환합니다. |
| `dispatch( array $initial_state = [] )` | 머신을 디스패치하고 머신 ID를 반환합니다. |

상태 진입 작업 클래스는 워크플로 단계에서 지원하는 것과 동일한 리소스 정책 키인 `concurrency_group`, `concurrency_limit`, `cost_units`를 사용하는 선택적 `config()` 메서드를 노출할 수도 있습니다.

### `Queuety::dispatch_state_machine_definition( $definition, $initial_state = [], $options = [] )`

직렬화된 상태 머신 정의 번들을 디스패치합니다. 대부분의 애플리케이션 코드는 `machine()->dispatch()`을 사용하는 것이 좋습니다. 이 저수준 메서드는 어댑터가 빌더에 직접 의존하지 않고 머신을 시작할 수 있도록 제공됩니다.

```php
$machine_id = Queuety::dispatch_state_machine_definition(
    $definition,
    [ 'thread_id' => 42 ],
    [ 'idempotency_key' => 'thread:42' ],
);
```

### `Queuety::machine_status( $machine_id )`

현재 머신 상태를 가져옵니다. `StateMachineState` 객체 또는 `null`을 반환합니다.

```php
$status = Queuety::machine_status( $machine_id );

echo $status->name;               // 'agent_session'
echo $status->status->value;      // 'waiting_event'
echo $status->current_state;      // 'awaiting_review'
print_r( $status->state );        // public machine state
print_r( $status->available_events ); // valid incoming events in the current state
```

`StateMachineState`에는 다음 항목도 포함됩니다.

- `definition_version`
- `definition_hash`
- `idempotency_key`
- `error_message`
- `current_action`
- `terminal_status`

### `Queuety::machine_event( $machine_id, $event_name, $payload = [] )`

대기 중인 머신에 외부 이벤트 하나를 보냅니다.

```php
Queuety::machine_event(
    $machine_id,
    'approve',
    [
        'approved' => true,
        'reviewer' => 'editor@example.com',
    ]
);
```

페이로드는 전환 가드에서 사용할 수 있으며 이벤트가 수락되면 공개 머신 상태에 병합됩니다.

### `Queuety::list_machines( $limit = 50, $status = null )`

검사할 수 있도록 저장된 머신 행을 나열합니다.

```php
$machines = Queuety::list_machines();
$waiting  = Queuety::list_machines( status: 'waiting_event' );
```

각 행에는 머신 ID, 이름, 상태, 현재 상태, 정의 버전, 정의 해시, 멱등성 키 및 수명 주기 타임스탬프가 포함됩니다.

### `Queuety::machine_timeline( $machine_id, $limit = 100, $offset = 0 )`

머신 하나의 영구 이벤트 타임라인을 가져옵니다.

```php
$timeline = Queuety::machine_timeline( $machine_id );
$page_two = Queuety::machine_timeline( $machine_id, 100, 100 );
```

각 이벤트 행에는 다음이 포함됩니다. `id`, `machine_id`, `job_id`, `state_name`, `handler`, `event`, `event_name`, `transition_name`, `queue`, `attempt`, `input`, `output`, `state_before`, `state_after`, `context`, `artifacts`, `chunks`, `error`, `duration_ms`, `created_at`.

### `Queuety::machine_trace( $machine_id )`

상태 머신 디버거 UI용 정규화된 트레이스 번들을 가져옵니다.

```php
$trace = Queuety::machine_trace( $machine_id );
```

트레이스에는 `machine`, 그룹화된 `states`, 원시 `events`, 연관된 작업 `jobs`, `logs`, `artifacts` 및 `chunks`이 포함됩니다.

### `Queuety::machine_state_at( $machine_id, $event_id )`

특정 머신 트레이스 이벤트 이후의 공개 상태를 가져오거나, 찾지 못하면 `null`을 반환합니다.

```php
$state = Queuety::machine_state_at( $machine_id, $event_id );
```

수명 주기 모델과 사용 패턴은 [상태 머신](https://queuety.inline0.dev/ko/docs/state-machines)에서 확인하세요.

## 워크플로 템플릿

### `Queuety::define_workflow( $name )`

템플릿으로 사용할 워크플로 빌더를 만듭니다.

```php
$builder = Queuety::define_workflow( 'onboarding' )
    ->then( CreateAccountHandler::class )
    ->then( SendWelcomeEmailHandler::class );
```

### `Queuety::register_workflow_template( $builder )`

워크플로 템플릿을 등록합니다.

```php
Queuety::register_workflow_template( $builder );
```

### `Queuety::run_workflow( $name, $payload, $options = [] )`

등록된 워크플로 템플릿을 이름으로 디스패치합니다. 워크플로 ID를 반환합니다.

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

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

템플릿이 등록되지 않은 경우 `RuntimeException`을 발생시킵니다.

### `Queuety::dispatch_workflow_definition( $definition, $payload, $options = [] )`

런타임 정의 번들에서 워크플로를 디스패치합니다.

대부분의 애플리케이션 코드는 `run_workflow()` 또는 `workflow()->dispatch()`을 사용하는 것이 좋습니다. 이 저수준 메서드는 재사용 가능한 어댑터가 내부 워크플로 관리자 메서드에 의존하지 않고 직렬화된 워크플로 정의를 디스패치할 수 있도록 제공됩니다.

```php
$workflow_id = Queuety::dispatch_workflow_definition(
    $definition,
    [ 'email' => 'user@example.com' ],
    [ 'idempotency_key' => 'onboarding:user@example.com' ],
);
```

### `Queuety::on_action( $hook, $workflow, $map = null, $when = null, $idempotency_key = null, $priority = 10, $accepted_args = null )`

워크플로를 디스패치하는 WordPress 작업을 등록합니다.

```php
Queuety::on_action(
    'save_post',
    workflow: 'content_review',
    map: static fn ( int $post_id, object $post, bool $update ): array => [
        'post_id' => $post_id,
        'post_type' => $post->post_type,
        'update' => $update,
    ],
    when: static fn ( int $post_id, object $post ): bool => 'post' === $post->post_type,
    idempotency_key: static fn ( int $post_id ): string => "save_post:{$post_id}",
);
```

`$workflow`은 다음 중 하나일 수 있습니다.

- 등록된 워크플로 템플릿 이름
- 인라인 `WorkflowBuilder`

`map`, `when` 및 `idempotency_key`는 현재 WordPress 요청 내에서 동기적으로 실행됩니다. 디스패치 후 비동기적으로 실행되는 것은 워크플로 단계뿐입니다.

전체 안내는 [액션 트리거](https://queuety.inline0.dev/ko/docs/features/action-triggers)를 참조하세요.

### `Queuety::workflow_templates()`

워크플로 템플릿 레지스트리를 가져옵니다.

```php
$registry = Queuety::workflow_templates();
$template = $registry->get( 'onboarding' ); // WorkflowTemplate or null
```

### `Queuety::load_workflows( $directory, $recursive )`

디렉터리의 모든 워크플로 파일을 로드하고 등록합니다. 각 `.php` 파일은 `WorkflowBuilder`을 반환해야 합니다. 등록된 워크플로 수를 반환합니다.

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

### `Queuety::load_workflow_file( $file_path )`

워크플로 파일 하나를 로드하고 등록합니다. 파일이 빌더를 반환하지 않으면 `WorkflowTemplate` 또는 `null`을 반환합니다.

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

## 예약

### `Queuety::schedule( $handler, $payload )`

반복 일정을 만듭니다. `PendingSchedule` 빌더를 반환합니다.

```php
Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'nightly_report' )
    ->cron( '0 3 * * *' )
    ->on_queue( 'reports' );
```

### `PendingSchedule` 메서드

| 메서드 | 설명 |
|---|---|
| `every( string $interval )` | 간격 표현식 설정(e.g. `1 hour`, `30 minutes`) |
| `cron( string $expression )` | 5개 필드로 된 cron 표현식 설정 |
| `overlap( OverlapPolicy $policy )` | 중복 실행 정책 설정(기본값: `Allow`). [예약](https://queuety.inline0.dev/ko/docs/features/scheduling)을 참조하세요. |
| `on_queue( string $queue )` | 대상 큐 설정(기본값: `default`) |
| `id()` | 강제로 디스패치하고 일정 ID 반환 |

### `Queuety::scheduler()`

내부 `Scheduler` 인스턴스를 가져옵니다.

```php
$scheduler = Queuety::scheduler();
$schedules = $scheduler->list();
$scheduler->remove( 'handler_name' );
$count = $scheduler->tick(); // manually trigger
```

## 핸들러 등록

### `Queuety::register( $name, $class )`

핸들러 클래스를 이름으로 등록합니다.

```php
Queuety::register( 'send_email', SendEmailHandler::class );
```

### `Queuety::discover_handlers( $directory, $namespace )`

디렉터리에서 핸들러 클래스를 자동으로 검색하고 등록합니다. 등록된 핸들러 수를 반환합니다.

```php
$count = Queuety::discover_handlers( __DIR__ . '/handlers', 'MyPlugin\\Handlers' );
```

### `Queuety::registry()`

핸들러 레지스트리를 가져옵니다.

```php
$registry = Queuety::registry();
```

## 관측 가능성

### `Queuety::logger()`

로그 항목을 조회하는 `Logger` 인스턴스를 가져옵니다.

```php
$logger = Queuety::logger();
$logs   = $logger->for_job( 42 );
$logs   = $logger->for_workflow( 7 );
$logs   = $logger->for_handler( 'send_email', 50 );
$logs   = $logger->for_event( LogEvent::Failed, 50 );
$logs   = $logger->since( new \DateTimeImmutable( '-1 hour' ), 100 );
$count  = $logger->purge( 30 );
```

### `Queuety::metrics()`

`Metrics` 인스턴스를 가져옵니다.

```php
$stats = Queuety::metrics()->handler_stats( 60 ); // last 60 minutes
```

### `Queuety::webhook_notifier()`

`WebhookNotifier` 인스턴스를 가져옵니다.

```php
$notifier = Queuety::webhook_notifier();
$id       = $notifier->register( 'job.buried', 'https://hooks.slack.com/...' );
$webhooks = $notifier->list();
$notifier->remove( $id );
```

### `Queuety::rate_limiter()`

`RateLimiter` 인스턴스를 가져옵니다.

```php
$limiter = Queuety::rate_limiter();
$limiter->register( 'call_openai', 60, 60 );
```

## 캐시

### `Queuety::cache()`

활성 캐시 백엔드 인스턴스를 가져옵니다.

```php
$cache = Queuety::cache();
$cache->set( 'my_key', 'my_value', 60 );
$value = $cache->get( 'my_key' );
```

### `Queuety::set_cache( $cache )`

캐시 백엔드를 재정의합니다. 사용자 지정 구현을 사용하려면 `Queuety::init()` 전에 호출하세요.

```php
Queuety::set_cache( new RedisCache() );
Queuety::init( $connection );
```

### `QUEUETY_CACHE_TTL`

캐시의 기본 TTL(초, 기본값: `5`)입니다. 내부 캐시 작업에 사용되는 기본 TTL을 변경하려면 `wp-config.php`에서 이 상수를 설정하세요.

```php
define( 'QUEUETY_CACHE_TTL', 10 );
```

백엔드 및 자동 검색에 관한 자세한 내용은 [캐싱](https://queuety.inline0.dev/ko/docs/features/caching)을 참조하세요.

## 상태 머신 이벤트 로그

### `Queuety::machine_events()`

`StateMachineEventLog` 인스턴스를 가져옵니다.

```php
$log = Queuety::machine_events();
```

## 워크플로 이벤트 로그

### `Queuety::workflow_events()`

`WorkflowEventLog` 인스턴스를 가져옵니다.

```php
$log = Queuety::workflow_events();
```

### `Queuety::workflow_timeline( $workflow_id, $limit = 100, $offset = 0 )`

워크플로 이벤트 전체 타임라인을 가져옵니다. ID 순으로 정렬된 이벤트 행 배열을 반환합니다.

```php
$events = Queuety::workflow_timeline( $workflow_id );
$next   = Queuety::workflow_timeline( $workflow_id, 100, 100 );
```

각 이벤트 행에는 다음이 포함됩니다. `id`, `workflow_id`, `job_id`, `parent_event_id`, `step_index`, `step_name`, `step_type`, `handler`, `event`, `queue`, `attempt`, `input`, `output`, `state_before`, `state_after`, `context`, `artifacts`, `chunks`, `error`, `error_message`(`error.message`에서 복사), `duration_ms` 및 `created_at`.

### `Queuety::workflow_trace( $workflow_id )`

워크플로 디버거 UI용 정규화된 트레이스 번들을 가져옵니다.

```php
$trace = Queuety::workflow_trace( $workflow_id );
```

트레이스에는 `workflow`, 그룹화된 `steps`, 원시 `events`, `jobs`, `logs`, `artifacts`, `chunks`, `signals` 및 `wait_dependencies`이 포함됩니다.

### `Queuety::trace_input( $input )`

현재 실행 중인 워크플로 단계의 애플리케이션 수준 입력을 설정합니다.

### `Queuety::trace_output( $output )`

현재 실행 중인 워크플로 단계의 애플리케이션 수준 출력을 설정합니다.

### `Queuety::trace_context( array $context )`

애플리케이션 수준 컨텍스트를 현재 실행 중인 워크플로 단계에 병합합니다.

### `Queuety::workflow_state_at( $workflow_id, $step_index )`

특정 워크플로 단계가 완료되거나 재개된 후의 공개 상태를 가져오거나, 찾지 못하면 `null`을 반환합니다.

```php
$state = Queuety::workflow_state_at( $workflow_id, 2 );
```

사용 사례와 CLI 명령은 [워크플로 이벤트 로그](https://queuety.inline0.dev/ko/docs/workflows/event-log)에서 확인하세요.

## 내부 인스턴스

이 메서드는 고급 사용 사례에 필요한 내부 인스턴스를 반환합니다.

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `Queuety::queue()` | `Queue` | 큐 작업(클레임, 해제, 묻기) |
| `Queuety::worker()` | `Worker` | 워커 프로세스 제어 |
| `Queuety::workflow_manager()` | `Workflow` | 워크플로 오케스트레이션 |
| `Queuety::machines()` | `StateMachine` | 상태 머신 수명 주기 관리 |
| `Queuety::connection()` | `Connection` | 데이터베이스 연결 |
| `Queuety::batch_manager()` | `BatchManager` | 배치 수명 주기 관리(취소, 정리) |
| `Queuety::chunk_store()` | `ChunkStore` | 스트리밍 단계의 청크 영속성 |
| `Queuety::machine_events()` | `StateMachineEventLog` | 상태 머신 트레이스 이벤트 및 정규화된 트레이스 번들 |
| `Queuety::workflow_events()` | `WorkflowEventLog` | 워크플로 트레이스 이벤트 및 정규화된 트레이스 번들 |
| `Queuety::resource_manager()` | `ResourceManager` | 워커 승인 프로필, 가중 예산 및 시스템 메모리 검사 |
| `Queuety::cache()` | `Cache` | 캐시 백엔드 인스턴스 |

## 테스트

### `Queuety::fake()`

테스트를 위해 큐를 메모리 내 가짜 객체로 대체합니다. 데이터베이스에 접근하지 않고 디스패치된 작업을 기록하는 `QueueFake` 인스턴스를 반환합니다. `create_batch()`는 가짜 배치 메타데이터도 기록하며, 체인은 개별적으로 푸시된 작업을 통해 단언합니다.

```php
$fake = Queuety::fake();
SendEmailJob::dispatch( 'user@example.com', 'Hello', 'Hi!' );
$fake->assert_pushed( SendEmailJob::class );
```

### `Queuety::reset()`

큐 가짜 객체를 포함한 모든 내부 상태를 초기화합니다. `tearDown()`에서 호출하세요.

```php
Queuety::reset();
```

### `QueueFake` 메서드

| 메서드 | 설명 |
|---|---|
| `assert_pushed( string $class, ?Closure $callback )` | 작업 클래스가 푸시되었는지 단언합니다. 선택적 콜백으로 작업 데이터를 필터링합니다. |
| `assert_pushed_times( string $class, int $count )` | 작업이 정확히 N회 푸시되었는지 단언합니다. |
| `assert_not_pushed( string $class )` | 작업이 한 번도 푸시되지 않았는지 단언합니다. |
| `assert_nothing_pushed()` | 작업이 전혀 푸시되지 않았는지 단언합니다. |
| `assert_batched( ?Closure $callback )` | 배치가 디스패치되었는지 단언합니다. 선택적 콜백으로 배치 데이터를 필터링합니다. |
| `pushed( string $class )` | 클래스의 기록된 디스패치를 모두 가져옵니다. 배열을 반환합니다. |
| `batches()` | 기록된 배치를 모두 가져옵니다. 배열을 반환합니다. |
| `reset()` | 기록된 모든 작업과 배치를 지웁니다. |

전체 예제는 [테스트](https://queuety.inline0.dev/ko/docs/features/testing)를 참조하세요.

## 인터페이스

### `Handler`

간단한 작업 핸들러를 위한 인터페이스입니다.

```php
interface Handler {
    public function handle( array $payload ): void;
    public function config(): array;
}
```

`config()`은 `queue`, `max_attempts`, `backoff`, `rate_limit`, `concurrency_group`, `concurrency_limit` 및 `cost_units`을 반환할 수 있습니다.

### `Step`

워크플로 단계 핸들러를 위한 인터페이스입니다.

```php
interface Step {
    public function handle( array $state ): array;
    public function config(): array;
}
```

`config()`은 `max_attempts`, `backoff`, `concurrency_group`, `concurrency_limit` 및 `cost_units`를 반환할 수 있습니다.

### `Contracts\ForEachHandler`

동적 for-each 분기 핸들러를 위한 인터페이스입니다.

```php
namespace Queuety\Contracts;

interface ForEachHandler {
    public function handle_item( array $state, mixed $item, int $index ): array;
    public function config(): array;
}
```

`config()`은 `max_attempts`, `backoff`, `rate_limit`, `concurrency_group`, `concurrency_limit` 및 `cost_units`을 반환할 수 있습니다.

### `Contracts\ForEachReducer`

for-each 결과를 위한 선택적 리듀서입니다.

```php
namespace Queuety\Contracts;

interface ForEachReducer {
    public function reduce( array $state, array $for_each ): array;
}
```

### `Contracts\Compensation`

단계 보상 핸들러를 위한 인터페이스입니다.

```php
namespace Queuety\Contracts;

interface Compensation {
    public function handle( array $state ): void;
}
```

### `Contracts\RepeatCondition`

상태 키 비교만으로는 부족한 경우 `repeat_until()` 및 `repeat_while()`에서 사용하는 루프 조건자를 위한 인터페이스입니다.

```php
namespace Queuety\Contracts;

interface RepeatCondition {
    public function matches( array $state ): bool;
}
```

### `Contracts\StateAction`

대기열 방식 상태 진입 작업을 위한 인터페이스입니다.

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
```

`$payload`은 머신이 작업 정의와 함께 정의된 경우 해당 정의의 구조화된 `payload`을 전달합니다. 공개 상태 업데이트를 반환합니다. 작업이 즉시 전환되어야 하면 `_event` 및 선택적으로 `_event_payload`을 포함합니다.

상태 작업에는 선택적 `config()` 메서드를 정의할 수도 있습니다. 진입 작업이 대기열에 추가될 때 Queuety은 여기에서 `concurrency_group`, `concurrency_limit` 및 `cost_units`를 읽습니다.

### `Contracts\StateGuard`

상태 머신 전환 가드를 위한 인터페이스입니다.

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
```

### `Contracts\Job`

독립형 디스패치 가능 작업 클래스를 위한 인터페이스입니다. 작업 클래스는 페이로드를 공개 속성으로, 실행 로직을 `handle()`에 캡슐화합니다.

```php
namespace Queuety\Contracts;

interface Job {
    public function handle(): void;
}
```

작업 클래스는 선택적으로 `Contracts\Middleware` 인스턴스 배열을 반환하는 `middleware()` 메서드를 정의할 수 있습니다.

### `Contracts\Cache`

캐시 백엔드를 위한 인터페이스입니다. 기본 제공 구현 및 사용자 지정 백엔드는 [캐싱](https://queuety.inline0.dev/ko/docs/features/caching)을 참조하세요.

```php
namespace Queuety\Contracts;

interface Cache {
    public function get( string $key ): mixed;
    public function set( string $key, mixed $value, int $ttl = 0 ): void;
    public function delete( string $key ): void;
    public function has( string $key ): bool;
    public function add( string $key, mixed $value, int $ttl = 0 ): bool;
    public function flush(): void;
}
```

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `get( $key )` | `mixed` | 값을 가져옵니다. 찾을 수 없거나 만료된 경우 `null`를 반환합니다. |
| `set( $key, $value, $ttl )` | `void` | 값을 저장합니다. TTL이 0이면 만료되지 않습니다. |
| `delete( $key )` | `void` | 값을 제거합니다. |
| `has( $key )` | `bool` | 키가 존재하고 만료되지 않았는지 확인합니다. |
| `add( $key, $value, $ttl )` | `bool` | 원자적 설정 후 미존재 확인입니다. 값이 설정되면 `true`을 반환합니다. |
| `flush()` | `void` | 캐시의 모든 항목을 제거합니다. |

### `WorkflowEventLog`

전체 트레이스 컨텍스트와 함께 워크플로 단계 전환을 기록합니다. `Queuety::workflow_events()`을 통해 접근합니다.

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `record_event( array $event )` | `void` | 원시 트레이스 이벤트를 기록합니다. |
| `record_step_started( ... )` | `void` | 입력과 실행 전 상태를 포함해 step_started 이벤트를 기록합니다. |
| `record_step_completed( ... )` | `void` | 입력, 출력, 실행 전 상태 및 실행 후 상태를 포함해 step_completed 이벤트를 기록합니다. |
| `record_step_failed( ... )` | `void` | 트레이스 컨텍스트와 구조화된 오류 세부 정보를 포함해 step_failed 이벤트를 기록합니다. |
| `get_timeline( int $workflow_id, ?int $limit = 100, int $offset = 0 )` | `array` | 선택적 페이지 지정과 함께 워크플로 트레이스 이벤트를 순서대로 가져옵니다. |
| `get_trace( int $workflow_id )` | `array` | 정규화된 트레이스 번들을 가져옵니다. |
| `get_state_at_step( int $workflow_id, int $step_index )` | `array\|null` | 특정 단계가 완료되거나 재개된 후의 공개 상태를 가져옵니다. |
| `prune( int $older_than_days )` | `int` | 지정된 일수보다 오래된 이벤트를 삭제합니다. 삭제된 행 수를 반환합니다. |

### `StateMachineEventLog`

워크플로 이벤트와 동일한 트레이스 필드로 상태 머신의 수명 주기, 작업, 전환 및 가드 이벤트를 기록합니다.

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `record_event( array $event )` | `void` | 원시 머신 추적 이벤트를 기록합니다 |
| `get_timeline( int $machine_id, ?int $limit = 100, int $offset = 0 )` | `array` | 선택적 페이지네이션을 사용해 머신 추적 이벤트를 순서대로 가져옵니다 |
| `get_trace( int $machine_id )` | `array` | 정규화된 머신 추적 번들을 가져옵니다 |
| `get_state_at_event( int $machine_id, int $event_id )` | `array\|null` | 특정 머신 추적 이벤트 이후의 공개 상태를 가져옵니다 |
| `prune( int $older_than_days )` | `int` | 지정한 일수보다 오래된 이벤트를 삭제합니다. 삭제된 행 수를 반환합니다. |

### `Contracts\StreamingStep`

청크 스트림을 생성하는 워크플로 단계 핸들러를 위한 인터페이스입니다. 생성된 각 값은 즉시 데이터베이스에 저장됩니다. [스트리밍 단계](https://queuety.inline0.dev/ko/docs/workflows/streaming)를 참조하세요.

```php
namespace Queuety\Contracts;

interface StreamingStep {
    public function stream( array $state, array $existing_chunks = [] ): \Generator;
    public function on_complete( array $chunks, array $state ): array;
    public function config(): array;
}
```

| 메서드 | 설명 |
|---|---|
| `stream( $state, $existing_chunks )` | 문자열 청크를 생성하는 제너레이터입니다. 생성할 때마다 즉시 DB에 기록합니다. 재시도 시 `$existing_chunks`에는 이전에 저장된 청크가 포함됩니다. |
| `on_complete( $chunks, $state )` | 스트림이 끝난 후 호출됩니다. 워크플로 상태에 병합할 데이터를 반환합니다. |
| `config()` | 선택적 구성입니다. `max_attempts`, `backoff`, `concurrency_group`, `concurrency_limit` 및 `cost_units`을 지원합니다. |

### `ChunkStore`

스트리밍 단계의 청크 저장을 관리합니다. `Queuety::chunk_store()`을 통해 접근합니다.

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `get_chunks( int $job_id )` | `string[]` | 작업의 모든 청크를 청크 인덱스 순으로 가져옵니다 |
| `append_chunk( int $job_id, int $chunk_index, string $content, ?int $workflow_id, ?int $step_index )` | `void` | 청크 하나를 추가합니다 |
| `clear_chunks( int $job_id )` | `void` | 작업의 모든 청크를 삭제합니다 |
| `chunk_count( int $job_id )` | `int` | 작업의 청크 수를 셉니다 |
| `get_accumulated( int $job_id )` | `string` | 모든 청크를 하나의 문자열로 연결합니다 |

### `Heartbeat`

단계 핸들러와 스트리밍 단계 내부에서 활동 하트비트를 보내기 위한 정적 헬퍼입니다. [하트비트](https://queuety.inline0.dev/ko/docs/features/heartbeats)를 참조하세요.

| 메서드 | 설명 |
|---|---|
| `Heartbeat::init( int $job_id, Connection $conn )` | 작업 컨텍스트를 설정합니다. 워커가 자동으로 호출합니다. |
| `Heartbeat::beat( array $progress = [] )` | 하트비트를 보냅니다. `reserved_at`를 업데이트하고 선택적으로 진행 데이터를 `heartbeat_data`에 저장합니다. |
| `Heartbeat::clear()` | 작업 컨텍스트를 지웁니다. 워커가 자동으로 호출합니다. |
| `Heartbeat::current_job_id()` | 현재 작업 ID를 가져옵니다(테스트용). |

### `Contracts\Middleware`

작업 미들웨어를 위한 인터페이스입니다. 미들웨어는 양파형 파이프라인으로 작업 실행을 감쌉니다.

```php
namespace Queuety\Contracts;

interface Middleware {
    public function handle( object $job, \Closure $next ): void;
}
```

## 트레이트

### `Dispatchable`

`Contracts\Job`을 구현하는 작업 클래스에 정적 디스패치 메서드를 제공합니다. 생성자 인수는 작업 인스턴스를 생성할 때 전달됩니다.

```php
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;
    // ...
}

// Dispatches a new SendEmailJob instance
SendEmailJob::dispatch( 'user@example.com', 'Subject', 'Body' );
```

| 메서드 | 반환값 | 설명 |
|---|---|---|
| `dispatch( ...$args )` | `PendingJob` | 인스턴스를 생성하고 디스패치합니다 |
| `dispatch_if( bool $condition, ...$args )` | `PendingJob\|null` | `$condition`가 true인 경우에만 디스패치합니다 |
| `dispatch_unless( bool $condition, ...$args )` | `PendingJob\|null` | `$condition`이 false인 경우에만 디스패치합니다 |
| `with_chain( array $jobs )` | `ChainBuilder` | 이 작업 클래스에서 체인을 시작합니다 |

```php
// Conditional dispatch
SendEmailJob::dispatch_if( $user->wants_email, $user->email, 'Hello', 'Hi!' );
SendEmailJob::dispatch_unless( $maintenance_mode, $user->email, 'Hello', 'Hi!' );

// Chain from a job class
FetchDataJob::with_chain( [
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )->dispatch();
```

### 작업 속성

작업 클래스는 워커가 리플렉션을 통해 읽어 재시도 및 시간 초과 동작을 재정의하는 공개 속성을 선언할 수 있습니다.

| 속성 | 유형 | 설명 |
|---|---|---|
| `$tries` | `int` | 작업이 격리되기 전까지의 최대 시도 횟수 |
| `$timeout` | `int` | 최대 실행 시간(초)(`pcntl` 필요) |
| `$backoff` | `array` | 각 재시도 횟수에 적용되는 점진적 지연 값(초) |

```php
class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];

    // ...
}
```

### `failed()` 훅

모든 재시도를 소진한 후 작업이 격리되면, 워커는 정의된 경우 작업 인스턴스의 `failed()` 메서드를 호출합니다.

```php
public function failed( \Throwable $exception ): void {
    error_log( "Job failed: {$exception->getMessage()}" );
}
```

## 기본 제공 미들웨어

모든 미들웨어 클래스는 `Contracts\Middleware`을 구현하며 `Queuety\Middleware` 네임스페이스에 있습니다.

| 클래스 | 설명 |
|---|---|
| `RateLimited( int $max, int $window )` | `$window`초마다 최대 `$max`회 실행되도록 속도 제한을 적용합니다 |
| `Timeout( int $seconds )` | `$seconds`초 후 작업을 종료합니다(`pcntl` 필요) |
| `UniqueJob( ?string $key )` | 데이터베이스 잠금으로 동시 실행을 방지합니다 |
| `WithoutOverlapping( string $key, int $release_after )` | 자동 잠금 만료를 사용해 중복 실행을 방지합니다 |
| `ThrottlesExceptions( int $max_attempts, int $decay_minutes )` | 외부 서비스가 중단되었을 때 작업 폭주를 방지하도록 예외 발생을 제한합니다 |

사용법과 예시는 [미들웨어](https://queuety.inline0.dev/ko/docs/features/middleware)를 참조하세요.

## 열거형

### `Priority`

```php
enum Priority: int {
    case Low    = 0;
    case Normal = 1;
    case High   = 2;
    case Urgent = 3;
}
```

### `JobStatus`

```php
enum JobStatus: string {
    case Pending    = 'pending';
    case Processing = 'processing';
    case Completed  = 'completed';
    case Failed     = 'failed';
    case Buried     = 'buried';
}
```

### `WorkflowStatus`

```php
enum WorkflowStatus: string {
    case Running        = 'running';
    case Completed      = 'completed';
    case Failed         = 'failed';
    case Paused         = 'paused';
    case WaitingForSignal  = 'waiting_for_signal';
    case WaitingForWorkflows = 'waiting_for_workflows';
    case Cancelled      = 'cancelled';
}
```

### `StateMachineStatus`

```php
enum StateMachineStatus: string {
    case Running = 'running';
    case WaitingEvent = 'waiting_event';
    case Completed = 'completed';
    case Failed = 'failed';
    case Paused = 'paused';
    case Cancelled = 'cancelled';
}
```

### `BackoffStrategy`

```php
enum BackoffStrategy: string {
    case Fixed       = 'fixed';
    case Linear      = 'linear';
    case Exponential = 'exponential';
}
```

### `ForEachMode`

```php
enum ForEachMode: string {
    case All = 'all';
    case FirstSuccess = 'first_success';
    case Quorum = 'quorum';
}
```

### `WaitMode`

```php
enum WaitMode: string {
    case All    = 'all';
    case Any    = 'any';
    case Quorum = 'quorum';
}
```

### `LogEvent`

```php
enum LogEvent: string {
    case Started                  = 'started';
    case Completed                = 'completed';
    case Failed                   = 'failed';
    case Buried                   = 'buried';
    case Retried                  = 'retried';
    case WorkflowStarted          = 'workflow_started';
    case WorkflowCompleted        = 'workflow_completed';
    case WorkflowFailed           = 'workflow_failed';
    case WorkflowPaused           = 'workflow_paused';
    case WorkflowResumed          = 'workflow_resumed';
    case WorkflowCancelled        = 'workflow_cancelled';
    case WorkflowRewound          = 'workflow_rewound';
    case WorkflowForked           = 'workflow_forked';
    case WorkflowDeadlineExceeded = 'workflow_deadline_exceeded';
    case Debug                    = 'debug';
}
```

### `OverlapPolicy`

```php
enum OverlapPolicy: string {
    case Allow  = 'allow';
    case Skip   = 'skip';
    case Buffer = 'buffer';
}
```

각 정책의 작동 방식은 [스케줄링](https://queuety.inline0.dev/ko/docs/features/scheduling)을 참조하세요.

## 속성

### `#[QueuetyHandler]`

자동 등록을 위한 PHP 8 속성입니다. [PHP 속성](https://queuety.inline0.dev/ko/docs/features/attributes)을 참조하세요.

```php
use Queuety\Attributes\QueuetyHandler;

#[QueuetyHandler( name: 'send_email', queue: 'emails', max_attempts: 5 )]
class SendEmailHandler implements Handler {
    // ...
}
```

## CLI 브리지

### `Queuety::cli_command()`

게시된 WP-CLI 루트 명령 이름을 반환합니다.

기본 루트 명령은 `queuety`입니다. 다른 이름으로 명령을 게시하려면 Queuety가 부팅되기 전에 `QUEUETY_CLI_COMMAND`을 정의하세요.

### `Queuety::cli_command_map()`

스크레이핑하지 않고도 배포된 명령 표면을 파악해야 하는 하네스를 위해 직렬화 가능한 Queuety CLI 카탈로그를 반환합니다. WP-CLI 클래스를 스크레이핑할 필요가 없습니다.

각 항목에는 다음이 포함됩니다:
- 안정적인 작업 ID
- 상대 CLI 경로
- WP-CLI 핸들러 클래스 및 메서드
- 가능한 실행 대상
- 원시 CLI 인수를 실행 계획으로 정규화하는 어댑터

### `Queuety::resolve_cli_command( $path, $args, $assoc_args )`

파싱된 CLI 호출을 하네스가 실행해야 하는 실행 계획으로 변환합니다.

```php
$plan = Queuety::resolve_cli_command(
    [ 'workflow', 'export' ],
    [ '42' ],
    [ 'output' => '/tmp/workflow.json' ]
);

echo $plan['callable']; // Queuety\Queuety::export_workflow_to_file
print_r( $plan['arguments'] );
```

실행 계획은 전송 방식을 고려하도록 설계되었습니다:
- 명령은 정규화된 인수와 함께 공개 PHP 호출 가능 항목으로 해석됩니다
- 카탈로그에는 가능한 모든 실행 대상이 미리 나열되며, `callable`과 `transport`는 파싱된 입력에 따라 선택된 분기를 나타냅니다

이는 에이전트 하네스에서 사용하는 표준 Queuety입니다. 안정적인 작업 ID, 공개 API 대상, 그리고 큐 기본값, JSON 페이로드 디코딩, 워커 풀 분기, 워크플로 내보내기 파일 처리와 같은 CLI 전용 고려 사항을 어댑터가 정규화합니다.
