
# 워크플로 보상

보상을 사용하면 완료된 워크플로 단계에 롤백 핸들러를 연결할 수 있습니다. Queuety은 단계가 끝날 때 공개 상태의 스냅샷을 저장한 다음, 워크플로가 취소되거나 선택적으로 실패할 때 보상 핸들러를 역순으로 실행합니다.

## `compensate_with()` 메서드

되돌려야 할 단계 바로 뒤에 `compensate_with()`을 호출하세요:

```php
use Queuety\Queuety;

Queuety::workflow( 'provision_account' )
    ->then( CreateRemoteUserStep::class )
    ->compensate_with( DeleteRemoteUserCompensation::class )
    ->then( CreateSubscriptionStep::class )
    ->compensate_with( CancelSubscriptionCompensation::class )
    ->then( SendWelcomeEmailStep::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

나중에 워크플로가 취소되면 Queuety이 실행됩니다:

1. `CancelSubscriptionCompensation`
2. `DeleteRemoteUserCompensation`

## 보상 핸들러

보상 핸들러는 `Contracts\Compensation`을 구현합니다:

```php
use Queuety\Contracts\Compensation;

final class DeleteRemoteUserCompensation implements Compensation {
    public function handle( array $state ): void {
        remote_api_delete_user( $state['remote_user_id'] );
    }
}
```

핸들러는 보상 대상 단계가 완료된 직후의 공개 워크플로 상태를 받습니다. 예약된 내부 키는 포함되지 않습니다.

## 취소와 실패 비교

명시적으로 취소하면 완료된 단계의 보상이 선택적 `on_cancel()` 핸들러보다 항상 먼저 실행됩니다.

실패 시 보상은 선택적으로 활성화한 경우에만 실행됩니다:

```php
Queuety::workflow( 'provision_account' )
    ->then( CreateRemoteUserStep::class )
    ->compensate_with( DeleteRemoteUserCompensation::class )
    ->then( CreateSubscriptionStep::class )
    ->compensate_with( CancelSubscriptionCompensation::class )
    ->then( ChargeCardStep::class )
    ->compensate_on_failure()
    ->dispatch( $payload );
```

`compensate_on_failure()`이 없으면 실패한 워크플로는 완료된 단계의 기록을 그대로 유지하며 실패한 단계부터 재시도할 수 있습니다.

`compensate_on_failure()`을 사용하면 워크플로가 실패로 표시된 후 완료된 단계의 보상이 실행됩니다. 이후 워크플로가 이미 보상되었으므로 `retry_workflow()`이 예외를 발생시킵니다.

## 지원되는 단계 유형

다음 항목에 보상을 연결할 수 있습니다:

- 순차 `then()` 단계
- `parallel()` 그룹
- `for_each()` 단계
- `run_workflow()` 단계
- `delay()` 지연 단계
- `wait_for_signal()` 신호 단계
- `wait_for_workflow()` 워크플로 종속성 단계

`run_workflow()`과 `for_each()`의 경우 중첩 작업이나 분기 작업이 모두 끝난 후 보상 스냅샷이 기록됩니다.

## `on_cancel()`과 보상의 차이

완료된 단계의 부수 효과를 역순으로 되돌리려면 `compensate_with()`을 사용하세요.

워크플로가 명시적으로 취소될 때마다 실행해야 하는 워크플로 수준의 정리 후크 하나를 설정하려면 `on_cancel()`을 사용하세요.
