상태 머신
상태 머신과 워크플로우는 서로 다른 문제를 해결합니다.
워크플로우는 “다음에 어떤 작업을 실행할까?”에 답합니다. 상태 머신은 “이 대상의 현재 상태는 무엇이며, 여기서 유효한 이벤트는 무엇일까?”에 답합니다. 만들려는 대상이 단순한 단계의 연속이 아니라 에이전트 세션, 채팅 스레드, 검토 수명 주기, 티켓 또는 승인 흐름처럼 시간이 지나면서 외부 입력에 반응하는 장기 실행 개체라면 이 차이가 중요합니다.
빠른 예제
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;
$machine_id = Queuety::machine( 'agent_session' )
->version( 'agent-session.v1' )
->state( 'awaiting_user' )
->on( 'user_message', 'planning' )
->state( 'planning' )
->action( PlanSessionAction::class )
->on( 'planned', 'awaiting_review' )
->state( 'awaiting_review' )
->on( 'approve', 'completed', ReviewApprovedGuard::class )
->on( 'reject', 'awaiting_user' )
->state( 'completed', StateMachineStatus::Completed )
->dispatch( [ 'thread_id' => 42 ] );
Queuety::machine_event(
$machine_id,
'user_message',
[ 'message' => 'Find competitors for this product' ]
);
Queuety::machine_event(
$machine_id,
'approve',
[ 'approved' => true, 'reviewer' => 'editor@example.com' ]
);
이 머신은 awaiting_user에서 시작해 사용자 메시지가 도착하면 planning로 이동하고, 대기 중인 작업이 planned를 내보낸 뒤 외부 승인이 세션을 완료할 때까지 awaiting_review에서 기다립니다. 핵심은 코드 구문 자체가 아닙니다. 수명 주기가 명시적이고, 검사할 수 있으며, 이벤트 기반이라는 점입니다.
머신 실행 방식
머신 인스턴스가 디스패치되면 Queuety은 현재 상태 이름, public 상태 페이로드, 정의 버전과 해시, 현재 상태에서 유효한 수신 이벤트, 영속 이벤트 타임라인을 저장합니다.
진입한 상태에 action()이 있으면 Queuety은 상태 진입 작업을 큐에 넣습니다. 해당 작업은 public 상태만 업데이트하고 다음 이벤트를 기다릴 수도 있고, _event와 선택적인 _event_payload를 내보내 다음 전이를 즉시 진행할 수도 있습니다. 상태에 작업이 없고 종료 상태도 아니라면 머신은 Queuety::machine_event()로 전달되는 외부 이벤트를 기다립니다.
워크플로우와 상태 머신 중 선택하기
실행이 근본적으로 단계 중심이면 워크플로우를 사용하세요. 데이터를 가져오고, 각 작업을 처리하고, 기다리고, 종합한 다음 마무리합니다. 시작과 끝이 분명하고 다음에 실행할 작업이 핵심 질문인 경우 이 모델이 잘 맞습니다.
유효한 동작이 현재 수명 주기 단계에 따라 달라지면 상태 머신을 사용하세요. 이벤트가 서로 다른 순서로 도착할 수 있거나, 개체가 오래 유지될 수 있거나, awaiting_user, planning, running_agents, awaiting_review 및 completed와 같은 상태를 통해 수명 주기를 명시적으로 검사해야 하는 경우에 더 적합합니다.
두 방식을 함께 사용하는 방법
두 기본 요소는 서로 경쟁하기보다 함께 사용할 때 가장 효과적입니다. 전이에 더 많은 실행이 필요하면 상태 작업에서 워크플로우를 디스패치할 수 있고, 작업이 끝나면 워크플로우가 머신에 이벤트를 보낼 수 있습니다. 실제로는 상태 머신이 수명 주기를 관리하고 워크플로우가 비용이 크거나 구조가 복잡한 실행을 맡게 됩니다.
빌더 구성
StateMachineBuilder은 의도적으로 간결하게 유지됩니다. initial()과 state()는 수명 주기 그래프를 정의합니다. action()은 대기 중인 작업을 하나의 상태에 연결합니다. on()는 머신을 다음 상태로 이동시킬 수 있는 이벤트를 선언합니다. on_queue(), with_priority() 및 max_attempts()은 상태 작업의 실행 방식을 제어하고, version()과 idempotency_key()는 영속 디스패치 메타데이터를 연결합니다. dispatch()은 머신 인스턴스를 생성합니다.
상태 작업은 선택적으로 config() 메서드를 제공할 수도 있으며, 이 메서드는 워크플로우 단계 및 핸들러와 동일한 리소스 정책 키를 사용합니다. 이를 통해 비용이 큰 상태 전이를 공유 동시성 그룹에 포함하거나, 일반 단계보다 더 많은 예산을 사용하는 작업에 더 높은 비용 가중치를 지정할 수 있습니다.
더 폭넓은 리소스 모델은 리소스 관리에 설명되어 있습니다.
계약
상태 진입 작업은 Queuety\Contracts\StateAction을 구현합니다:
namespace Queuety\Contracts;
interface StateAction {
public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
$payload은 작업 정의에서 전달된 구조화 페이로드이며, 작업이 일반 클래스 이름으로 지정된 경우에는 비어 있습니다. Queuety은 new $class()를 사용해 작업을 생성하므로 작업 클래스에는 필수 인수가 없는 생성자가 필요합니다.
상태 작업은 선택적으로 config() 메서드를 정의할 수 있으며, 대기 중인 진입 작업이 작업자 허용, 가중치가 적용된 큐 또는 공급자 예산, 기타 리소스 정책에 참여해야 할 때 concurrency_group, concurrency_limit 및 cost_units을 사용합니다.
전이 가드는 Queuety\Contracts\StateGuard을 구현합니다:
namespace Queuety\Contracts;
interface StateGuard {
public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
검사 API
파사드를 사용해 실행 중인 머신을 검사하세요:
$status = Queuety::machine_status( $machine_id );
$machines = Queuety::list_machines();
$events = Queuety::machine_timeline( $machine_id );
$trace = Queuety::machine_trace( $machine_id );
$state = Queuety::machine_state_at( $machine_id, $event_id );
상태 머신에는 WP-CLI 명령이 없습니다. wp queuety workflow ...은 워크플로우만 다룹니다. 명령줄에서 머신을 검사하려면 예를 들어 wp eval을 통해 이러한 PHP API를 사용하세요.
이러한 API는 현재 상태 이름, 수명 주기 상태, public 상태, 사용 가능한 이벤트, 정의 메타데이터, 전체 전이 타임라인, 디버거 UI용 정규화된 추적 번들을 제공합니다. 머신 목록 행에는 정의 버전, 정의 해시, 멱등성 키가 포함되어 있어 각 머신을 개별적으로 불러오지 않고도 목록 화면에 소스 및 런타임 버전을 표시할 수 있습니다. 추적 번들에는 그룹화된 상태, 원시 이벤트, 연관된 작업 작업, 로그, 아티팩트 참조, 청크 참조, 핸들러 입력 및 출력, 전후 상태, 컨텍스트, 타이밍, 구조화된 오류가 포함됩니다.
에이전트 세션
파이프라인보다 세션에 가까운 방식으로 동작하는 에이전트 시스템에는 상태 머신이 특히 유용합니다. awaiting_user, planning, running_agents, awaiting_review 및 completed를 거치는 세션은 대기 단계와 되돌아가는 분기가 많은 하나의 긴 워크플로우 정의로 표현하기보다 명시적인 수명 주기로 표현하는 편이 대개 이해하기 쉽습니다.