
# 병렬 단계

여러 단계를 동시에 실행할 수 있는 경우가 있습니다. `->parallel()` 메서드는 핸들러 그룹을 동시에 디스패치합니다. 모든 병렬 핸들러가 완료된 후에만 워크플로우가 다음 단계로 진행됩니다.

## `->parallel()` 메서드

```php
use Queuety\Queuety;

Queuety::workflow( 'enrich_profile' )
    ->then( FetchUserHandler::class )
    ->parallel( [
        FetchSocialProfileHandler::class,
        FetchPaymentHistoryHandler::class,
        FetchActivityLogHandler::class,
    ] )
    ->then( MergeAndSaveHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

이 예시에서는 다음 작업을 수행합니다:
1. `FetchUserHandler`가 먼저 실행됩니다(순차).
2. 핸들러 3개가 동시에 실행됩니다(병렬).
3. 세 핸들러가 모두 완료된 뒤 `MergeAndSaveHandler`가 실행됩니다(순차).

## 작동 방식

병렬 단계에 도달하면 Queuety는 그룹의 핸들러마다 작업을 하나씩 디스패치합니다. 각 작업은 현재 워크플로우 상태를 받아 독립적으로 실행됩니다. 워크플로우는 대기 중인 병렬 작업 수를 추적하고, 모든 작업이 완료된 뒤에만 다음 단계로 진행합니다.

각 병렬 핸들러의 반환 값은 워크플로우 상태에 병합됩니다. 여러 핸들러가 같은 키를 반환하면 마지막에 완료된 핸들러의 값이 적용됩니다.

## 상태 병합

각 병렬 핸들러는 공유 워크플로우 상태에 병합될 데이터를 반환합니다:

```php
class FetchSocialProfileHandler implements Step {
    public function handle( array $state ): array {
        $profile = fetch_social_api( $state['user_id'] );
        return [ 'social_profile' => $profile ];
    }

    public function config(): array {
        return [];
    }
}

class FetchPaymentHistoryHandler implements Step {
    public function handle( array $state ): array {
        $payments = fetch_payment_api( $state['user_id'] );
        return [ 'payment_history' => $payments ];
    }

    public function config(): array {
        return [];
    }
}
```

병렬 단계가 완료되면 상태에 `social_profile` 및 `payment_history`가 모두 포함되고, 다음 순차 단계에서 전체 상태를 사용할 수 있습니다.

## 순차 및 병렬 실행 조합하기

`->then()` 호출과 `->parallel()` 호출을 자유롭게 조합할 수 있습니다:

```php
Queuety::workflow( 'complex_pipeline' )
    ->then( SetupHandler::class )               // sequential
    ->parallel( [ TaskA::class, TaskB::class ] ) // parallel
    ->then( IntermediateHandler::class )         // sequential
    ->parallel( [ TaskC::class, TaskD::class ] ) // parallel
    ->then( FinalizeHandler::class )             // sequential
    ->dispatch( $payload );
```

## 오류 처리

병렬 그룹의 핸들러 중 하나라도 실패하면 해당 핸들러에 일반 재시도 로직이 적용됩니다. 이미 완료된 다른 병렬 핸들러는 다시 실행되지 않습니다. 실패한 핸들러가 성공하면 워크플로가 계속 진행됩니다. 재시도 횟수를 모두 소진하고 격리되면 워크플로가 실패로 표시되고 나머지 분기 작업도 격리됩니다. `retry_workflow()`을 사용해 실패한 단계를 다시 실행하세요.
