refactor(extension): 확장 미들웨어 선언적 self-gate 정규화

확장이 SP Kernel 미들웨어 그룹을 직접 조작하거나 라우트 파일에 미들웨어
FQCN 을 직접 부착하던 임시 방식을, 확장이 부착 대상(targets)을 명시 선언하고
코어가 요청 시점에 라우트명·URI 로 매칭해 실행하는 self-gate 로 정규화.
번들 7건(ecommerce/gdpr/pay 3종) 이전, audit 룰 3건·문서·버전 동기화 동반.

부수적으로 generate-skills.cjs 가 재생성 시 신의성실(부수의무) 섹션을 소실
시키던 회귀를 수정 — 스킬별 불릿을 스크립트 SSoT 로 편입해 재생성에도 보존.
This commit is contained in:
HeuJung committed 2026-07-16 10:22:51 +09:00
1 parent 8b9bfd0c91
commit c0e0d82459
57 files changed
+2427 -205

No files matched your search

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Middleware;
use App\Contracts\Extension\ExtensionMiddlewareRegistryInterface;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Pipeline\Pipeline;
use Symfony\Component\HttpFoundation\Response;
/**
* 확장 선언 미들웨어의 self-gate 실행기.
*
* 확장이 getMiddleware() 로 선언한 미들웨어를 web/api 그룹에 직접 넣는 대신,
* 이 래퍼가 그룹에 등록되어 요청 시점에 현재 라우트 이름·URI 가 각 확장 미들웨어의
* targets 패턴에 매칭될 때만 해당 미들웨어를 동적으로 실행한다. 매칭 없으면
* 즉시 통과 (코어 EnforceIdentityPolicy 의 라우트명 인덱스 조회 모델과 동일).
*
* 부팅 시점 라우트 순회로 확장 간 미들웨어를 부착할 수 없는 제약(확장 라우트는
* booted 지연 등록 + 순서 미보장)을 요청 시점 매칭으로 원천 회피한다.
*
* bootstrap/app.php 에서 web/api × before_core/after_core 로 총 4회 등록된다.
* Kernel 상 prepend/append 위치는 등록 시점 매핑이고, 게이트 실행 로직은 timing 을 안다.
*
* @since 7.0.5
*/
class ExtensionMiddlewareGate
{
/**
* @param Application $app Pipeline 실행용 컨테이너
* @param ExtensionMiddlewareRegistryInterface $registry 확장 미들웨어 매칭 인덱스
*/
public function __construct(
protected Application $app,
protected ExtensionMiddlewareRegistryInterface $registry,
) {}
/**
* 미들웨어 진입점.
*
* @param Request $request HTTP 요청
* @param Closure $next 다음 파이프라인
* @param string $group 이 래퍼가 등록된 그룹 ('web'|'api')
* @param string $timing 이 래퍼 인스턴스가 담당하는 실행 시점 ('before_core'|'after_core')
*/
public function handle(Request $request, Closure $next, string $group, string $timing): Response
{
// 라우트명(무명이면 빈 문자열)과 URI path(항상 존재, 선행 슬래시 없음) 둘 다 전달 —
// 무명 SSR 셸 catch-all 은 URI 패턴 target 으로만 매칭되므로 routeName 빈 값에 조기 통과하지 않는다.
$routeName = $request->route()?->getName() ?? '';
$path = $request->path();
$matched = $this->registry->resolveForRoute($routeName, $path, $group, $timing);
if ($matched === []) {
return $next($request);
}
// 매칭된 확장 미들웨어들을 Laravel Pipeline 으로 순차 실행 후 $next.
return (new Pipeline($this->app))
->send($request)
->through($matched)
->then($next);
}
}