
# 워크플로 취소

워크플로는 프로그래밍 방식이나 CLI을 통해 취소할 수 있습니다. 워크플로가 취소되면 Queuety은 상태를 `cancelled`로 설정하고, 대기 중이거나 처리 중인 작업을 모두 묻어 두며, 선택적 정리 핸들러를 실행합니다.

## 정리 핸들러 등록

워크플로 빌더에서 `on_cancel()`을 사용해 워크플로가 취소될 때 실행할 클래스를 등록하세요. 핸들러 클래스는 `handle( array $state ): void` 메서드를 구현해야 합니다.

```php
use Queuety\Queuety;

Queuety::workflow( 'import_data' )
    ->then( FetchDataHandler::class )
    ->then( TransformDataHandler::class )
    ->then( LoadDataHandler::class )
    ->on_cancel( ImportCleanupHandler::class )
    ->dispatch( [ 'source_url' => $url ] );
```

정리 핸들러는 공개 워크플로 상태(`_`로 시작하지 않는 모든 키)를 받습니다:

```php
class ImportCleanupHandler {

    public function handle( array $state ): void {
        // Clean up any partial imports.
        if ( isset( $state['temp_table'] ) ) {
            global $wpdb;
            $wpdb->query( "DROP TABLE IF EXISTS {$state['temp_table']}" );
        }

        // Notify the user.
        wp_mail(
            $state['notify_email'] ?? '',
            'Import cancelled',
            'Your data import was cancelled.'
        );
    }
}
```

## PHP을 통한 취소

워크플로 ID를 사용해 `Queuety::cancel_workflow()`을 호출하세요:

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

이 메서드는 다음을 수행합니다:

1. 워크플로 행을 잠급니다
2. 워크플로 상태를 `cancelled`로 설정합니다
3. "Workflow cancelled" 메시지와 함께 해당 워크플로의 대기 중 및 처리 중인 모든 작업을 묻어 둡니다
4. `workflow_cancelled` 이벤트를 기록하고 커밋합니다
5. 완료된 단계의 보상 작업을 역순으로 실행합니다(`compensate_with()`을 통해 등록된 경우)
6. `on_cancel` 정리 핸들러를 실행합니다(등록된 경우)

워크플로가 이미 `completed` 또는 `cancelled` 상태이면 `RuntimeException`이 발생합니다.

## CLI을 통한 취소

```bash
wp queuety workflow cancel <id>
```

| 옵션 | 설명 |
|---|---|
| `<id>` | 취소할 워크플로 ID(필수) |

```bash
wp queuety workflow cancel 42
```

## 취소 시 발생하는 일

워크플로가 취소되면 다음과 같이 처리됩니다:

- **상태**가 `cancelled`로 변경됩니다
- `compensate_with()`로 등록한 경우 **단계 보상 작업**이 완료된 역순으로 실행됩니다
- 워크플로 행이 `cancelled`로 표시된 후 **정리 핸들러**가 실행됩니다
- 워크플로의 **대기 중인 작업**이 즉시 묻힙니다
- 워크플로의 **처리 중인 작업**이 묻힙니다. 실행 중인 핸들러가 완료되면 출력은 폐기되고 워크플로는 다음 단계로 진행하지 않습니다
- 이전 단계의 **완료된 작업**은 그대로 유지됩니다
- `workflow_cancelled` 이벤트가 포함된 **로그 항목**이 기록됩니다

## 취소와 실패 비교

| 항목 | 실패 | 취소 |
|---|---|---|
| 트리거 | 단계의 재시도 횟수를 모두 소진 | `cancel_workflow()`을 명시적으로 호출 |
| 상태 | `failed` | `cancelled` |
| 재시도 가능 | 예, `retry_workflow()` 사용. 단, 워크플로가 이미 보상된 경우는 제외 | 아니요 |
| 정리 핸들러 | 호출되지 않음 | `on_cancel()`를 통해 등록된 경우 호출 |
| 로그 이벤트 | `workflow_failed` | `workflow_cancelled` |

## 예시: 확인을 거쳐 취소하기

취소하기 전에 워크플로 상태를 확인하는 방식이 일반적입니다:

```php
$status = Queuety::workflow_status( $workflow_id );

if ( null === $status ) {
    throw new \RuntimeException( 'Workflow not found.' );
}

if ( 'running' === $status->status->value || 'paused' === $status->status->value ) {
    Queuety::cancel_workflow( $workflow_id );
}
```
