Laravel과 Inertia.js로 다부서 워크플로우 라우팅 시스템을 구축한 방법
Source: Dev.to
The Problem I Was Trying to Solve
제가 VMMS — 바우처 관리 시스템 — 를 만들기 시작했을 때 가장 큰 도전은 워크플로 라우팅이었습니다.
바우처 요청은 한 사무실에만 전달되는 것이 아니라 여러 부서를 거칩니다. 어느 시점에서든 부서는 다음을 할 수 있습니다:
- 자신의 단계를 완료하고 요청을 다음 부서로 전달
- 전체 요청을 거부
- 문서 누락을 표시하고 처리를 일시 중지
저는 이러한 모든 상황을 깔끔하게 처리할 수 있는 시스템이 필요했습니다.
Configurable Processing Flow
각 바우처 유형은 transaction_flows 테이블에 저장된 구성 가능한 처리 흐름을 가집니다:
Schema::create('transaction_flows', function (Blueprint $table) {
$table->id();
$table->foreignId('voucher_id')->constrained()->cascadeOnDelete();
$table->string('department');
$table->integer('order_number');
$table->timestamps();
});
클라이언트가 요청을 제출하면 시스템은 이 테이블을 읽어 부서 순서를 결정합니다.
Audit Trails
부서가 요청을 처리할 때마다 audit_trails 테이블에 감사 레코드가 생성됩니다:
Schema::create('audit_trails', function (Blueprint $table) {
$table->id();
$table->foreignId('transaction_id')->constrained()->cascadeOnDelete();
$table->string('processing_offices');
$table->timestamp('process_initiate')->nullable();
$table->timestamp('process_accomplished')->nullable();
$table->date('deadline')->nullable();
$table->timestamps();
});
process_initiate는 부서가 처리를 시작할 때 설정됩니다.process_accomplished는 부서가 처리를 완료했을 때 설정됩니다.
Determining the Current Step
활성 부서(또는 다음 부서)를 찾는 로직은 다음과 같습니다:
$activeAudit = $auditTrails->first(
fn($a) => $a->process_initiate && !$a->process_accomplished
);
if ($activeAudit) {
return $activeAudit->processing_offices;
}
// If no active audit, find the next step
$doneOrders = $auditTrails
->filter(fn($a) => $a->process_accomplished)
->map(fn($a) => $flow->firstWhere('department', $a->processing_offices)?->order_number ?? 0);
$lastDone = $doneOrders->max() ?? 0;
$nextStep = $flow->firstWhere('order_number', $lastDone + 1);
return $nextStep?->department ?? 'Completed';
Edge Cases Handled
- Skipped department – 코드는 다음 순번을 찾습니다.
- Missing documents – 필요한 문서가 제공될 때까지 요청이 일시 중지됩니다.
- Final department completed –
'Completed'를 반환합니다.
Vue 3 Stepper Component
프론트‑엔드에서는 진행 상황을 시각화하기 위해 스테퍼 컴포넌트를 만들었습니다:
const stepStatus = (req, step, stepIndex) => {
const audits = req.audit_trails ?? [];
const accomplishedCount = audits.filter(
a => a.process_accomplished !== null
).length;
if (stepIndex whereNull('resume_transaction')
->pluck('transaction_id')
->toArray();
resume_transaction가 채워지면 워크플로가 자동으로 재개됩니다.
Key Takeaways
- Model the flow separately from the transaction –
transaction_flows를voucher_transactions와 구분하면 라우팅 로직을 쉽게 수정할 수 있습니다. - Audit trails are essential – 현재 상태와 이력을 신뢰할 수 있는 진실의 원천을 제공합니다.
- Plan for edge cases – 건너뛴 단계, 문서 일시 중지, 최종 완료 등을 명시적으로 처리해야 합니다.
Demo & Availability
- Live demo: 🔴
- Purchase VMMS: Gumroad에서 이용 가능 –
댓글에 언제든지 질문을 남겨 주세요!