> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 패키지의 미들웨어 등록과 적용 범위

> Laravel 13의 Router 구현을 바탕으로, 패키지의 미들웨어 별칭 등록, 명시적 적용, 그룹 덮어쓰기 회피와 캐시 시의 검증을 해설합니다.

패키지에 HTTP 기능을 추가할 때는 미들웨어를 사용 가능하게 하는 것과 사용하는 애플리케이션의 요청에 적용하는 것을 구분합니다. 설치만으로 모든 라우트의 동작을 바꾸지 않는 설계로 하면, 도입 시의 영향과 향후 호환성을 관리하기 쉬워집니다.

이 페이지에서는 [패키지 개발의 기초](/ko/advanced/package-development)를 전제로, Laravel 공식 문서의 `13.x`와 Framework `v13.36.0`의 구현을 참조합니다. 일반적인 미들웨어 작성법은 [미들웨어](/ko/middleware)를 참조하세요.

## 등록·적용·실행을 구분한다

`Router::aliasMiddleware()`는 별칭을 키, 클래스명을 값으로 하여 Router의 배열에 등록합니다. 미들웨어의 인스턴스 생성이나 라우트에 대한 적용은 수행하지 않습니다.

```mermaid theme={null}
flowchart TD
    A["프로바이더의 boot"] --> B["aliasMiddleware로<br>이름과 클래스를 등록"]
    C["라우트 정의"] --> D["middleware로<br>적용할 이름을 지정"]
    B --> E["요청의 라우트가 결정됨"]
    D --> E
    E --> F["별칭·그룹을 전개<br>제외와 우선순위를 반영"]
    F --> G["Pipeline이 미들웨어를 실행"]
```

| 조작 | 역할 | 그것만으로는 수행되지 않는 것 |
| - | - | - |
| `aliasMiddleware()` | 이름에서 클래스로의 대응을 등록한다 | 요청에 대한 적용 |
| 라우트의 `middleware()` | 대상 라우트에 할당한다 | 미들웨어 클래스의 고유 구현 |
| `middlewareGroup()` | 이름 있는 그룹의 내용을 설정한다 | 글로벌 미들웨어에 대한 추가 |
| HTTP Kernel의 글로벌 스택에 추가 | 모든 요청을 대상으로 한다 | 라우트 단위의 제외에 대한 대응 |

별칭 이름은 공개 API입니다. `auth`나 `throttle` 같은 내장 이름, 다른 패키지와 충돌하기 쉬운 일반적인 이름을 피하고 패키지 고유의 접두사를 붙입니다. 같은 키로 다시 등록하면 `aliasMiddleware()`는 이전 대응을 덮어씁니다.

## 프로바이더에서 별칭을 공개한다

다음 예에서는 응답에 고정된 식별 헤더를 붙이는 미들웨어를 만듭니다. 인증·인가나 접근 제어의 예가 아닙니다. Composer의 PSR-4에서 `Acme\Courier\`를 `src/`에 대응시키고, 프로바이더를 [자동 검출](/ko/advanced/package-discovery) 또는 수동으로 등록하는 것을 전제로 합니다.

```php src/Http/Middleware/AddCourierHeader.php theme={null}
<?php

namespace Acme\Courier\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class AddCourierHeader
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set('X-Acme-Courier', 'enabled');

        return $response;
    }
}
```

Router는 `boot()`에서 사용합니다. `boot()`의 인수는 서비스 컨테이너에서 주입되므로, 미들웨어 등록을 위해 사용자의 `bootstrap/app.php`를 자동으로 편집할 필요는 없습니다.

```php src/CourierServiceProvider.php theme={null}
<?php

namespace Acme\Courier;

use Acme\Courier\Http\Middleware\AddCourierHeader;
use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(Router $router): void
    {
        $router->aliasMiddleware('acme-courier.header', AddCourierHeader::class);
    }
}
```

이 프로바이더는 일반 프로바이더로 기동시킵니다. 별칭 등록을 다른 서비스가 해석된 경우에만 실행되는 지연 프로바이더로 옮기면, 라우트가 필요로 하는 시점에 등록된다는 보장이 없어집니다.

### 사용자가 라우트 단위로 적용한다

사용하는 애플리케이션의 `routes/web.php`에서 필요한 라우트에만 할당합니다.

```php routes/web.php theme={null}
<?php

use Illuminate\Support\Facades\Route;

Route::get('/courier-status', function () {
    return response()->json(['available' => true]);
})->middleware('acme-courier.header');
```

별칭을 제공하지 않고 `->middleware(AddCourierHeader::class)`처럼 클래스명을 직접 지정할 수도 있습니다. 짧은 이름을 사용자에게 제공할 필요가 있는지에 따라 선택합니다.

패키지 자체가 라우트를 제공하는 경우에도 라우트 파일에서 필요한 미들웨어를 명시합니다. `loadRoutesFrom()`은 `web`이나 `api`를 자동으로 추가하지 않습니다. 등록 방법과 라우트 캐시의 처리는 [패키지의 라우트 등록과 캐시](/ko/advanced/package-routes)를 참조하세요.

## 애플리케이션 전체에 대한 적용은 사용자가 선택한다

사용하는 애플리케이션의 모든 `web` 라우트에 적용하고 싶은 경우에는, 사용자가 기존 `withMiddleware()` 콜백에 다음 설정을 추가합니다. 기존 설정을 대체하는 예가 아닙니다.

```php theme={null}
use Acme\Courier\Http\Middleware\AddCourierHeader;
use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->web(append: [
        AddCourierHeader::class,
    ]);
})
```

이 구성에서는 클래스명을 직접 사용하므로 별칭의 등록 순서에 의존하지 않습니다. 적용 범위는 `web` 그룹이며 글로벌 스택이 아닙니다. `api`나 어느 그룹도 붙어 있지 않은 라우트에는 자동으로 적용되지 않습니다.

<Warning>
  패키지 설치 시 `web` 전체에 적용하는 설계와 사용자가 명시적으로 적용하는 설계는 호환되지 않습니다. 인증, 세션, 응답 헤더, 외부 통신 등에 영향을 주는 처리를 자동 업데이트로 모든 라우트에 확대하지 마세요.
</Warning>

## Router의 그룹 조작에는 차이가 있다

패키지가 고유한 그룹을 제공하는 경우에는 고유한 이름을 사용합니다. 다음은 프로바이더의 `boot(Router $router)` 안에 두는 예입니다.

```php theme={null}
$router->middlewareGroup('acme-courier', [
    AddCourierHeader::class,
]);
```

사용자는 `->middleware('acme-courier')`로 적용할 수 있습니다. 단, 이 그룹에 포함되는 것은 지정한 클래스뿐이며, `web`의 세션이나 CSRF 보호를 이어받는 것은 아닙니다.

기존 그룹에 추가하는 API도 있지만, `v13.36.0`의 Router 구현에는 다음과 같은 차이가 있습니다.

| Router의 메서드 | 그룹이 존재하는 경우 | 그룹이 존재하지 않는 경우 |
| - | - | - |
| `middlewareGroup($name, $items)` | 배열 전체를 대체한다 | 지정한 배열로 만든다 |
| `pushMiddlewareToGroup($name, $item)` | 같은 항목이 없으면 끝에 추가 | 빈 배열을 만든 후 추가한다 |
| `prependMiddlewareToGroup($name, $item)` | 같은 항목이 없으면 앞에 추가 | 아무것도 하지 않는다 |

`middlewareGroup('web', [...])`로 패키지의 클래스만 설정하면 기존 `web`의 내용을 대체해 버립니다. 추가용 메서드와 혼동하지 마세요. 또한 존재하지 않는 `web`에 `pushMiddlewareToGroup()`을 해도 표준 `web` 그룹이 복원되는 것은 아닙니다.

추가용 메서드의 중복 판정은 등록한 값에 대한 것입니다. 같은 클래스를 가리키는 별칭과 클래스명은 서로 다른 문자열이므로, 등록 테이블 단계에서는 별개의 항목이 됩니다. 표기를 통일하고, 표기가 다른 동일 클래스까지 등록 시에 중복 제거된다고 가정하지 마세요.

### 등록 순서만으로 실행 순서를 보장하지 않는다

Router는 라우트에 할당된 미들웨어의 별칭과 그룹을 전개하고, 제외를 처리한 후 우선순위로 정렬합니다. 앞·끝에 추가하는 것만으로 인증이나 라우트 모델 바인딩과의 엄밀한 전후 관계가 보장되는 것은 아닙니다.

또한 HTTP Kernel의 `syncMiddlewareToRouter()`는 Kernel이 가진 그룹을 Router에 `middlewareGroup()`으로 동기화합니다. Kernel 측의 구성이 나중에 다시 동기화되면 Router에 직접 추가한 그룹의 내용이 대체되는 경우가 있습니다. 패키지 간의 기동 순서로 기존 그룹의 변경을 경쟁하는 것이 아니라, 사용하는 애플리케이션 측의 `withMiddleware()`에서 구성을 집약하는 방침을 기본으로 합니다.

세션 시작 후, 인증 후 등의 순서가 필요한 기능에서는 그 전제를 설치 절차에 명시하고, 사용하는 애플리케이션의 우선순위 설정과 함께 확인합니다. 패키지가 애플리케이션 전체의 우선순위 목록을 통째로 대체하는 것은 피하세요.

## 캐시와 검증의 경계

별칭 등록을 `routesAreCached()`의 조건 안이나 라우트 파일에 두지 마세요. `loadRoutesFrom()`은 캐시가 있으면 라우트 파일을 읽어 들이지 않지만, 캐시된 라우트에 남아 있는 별칭을 실행 시에 해석하기 위한 등록은 필요합니다. 프로바이더의 `boot()`에서 매번 등록합니다.

라우트에 대한 할당을 변경한 경우에는 사용하는 애플리케이션에서 라우트 캐시를 다시 생성합니다.

```bash theme={null}
php artisan route:cache
php artisan route:list --path=courier-status -vv
```

`-vv`는 그룹의 내용을 전개하여 확인하기 위해 사용합니다. 단, 목록 표시가 성공하더라도 미들웨어 내의 처리나 세션 사용까지 보장하는 것은 아닙니다. HTTP 요청 테스트도 필요합니다.

다음은 위의 `/courier-status`를 정의한 Laravel 애플리케이션의 Feature 테스트에 추가하는 메서드입니다.

```php theme={null}
public function test_courier_header_is_applied(): void
{
    $this->get('/courier-status')
        ->assertOk()
        ->assertHeader('X-Acme-Courier', 'enabled');
}
```

| 확인 케이스 | 기대하는 결과 |
| - | - |
| 별칭을 등록만 한 라우트 | 헤더가 붙지 않는다 |
| 별칭을 할당한 라우트 | 헤더가 붙는다 |
| 클래스명을 직접 할당한 라우트 | 같은 동작이 된다 |
| 캐시 생성 후 새 애플리케이션을 기동 | 같은 라우트에서 같은 동작이 된다 |
| `web` 전체에 대한 적용을 선택 | 대상 `web` 라우트에 적용되고, 대상 외로는 확대되지 않는다 |
| 사용하는 애플리케이션 고유의 그룹·우선순위를 설정 | 세션·인증·바인딩과의 순서가 요건을 충족한다 |

그룹의 존재 확인이나 별칭 배열을 조사하는 단위 테스트와 실제 HTTP 실행은 별개의 검증입니다. 특히 캐시 검증은 같은 프로세스의 Router를 살펴보는 것만이 아니라, 캐시 생성 후 새로운 기동에서 수행합니다. 장수명 워커를 사용하고 있는 경우에는 일반 배포 절차에 따라 다시 로드하세요.

## 관련 페이지

<Columns cols={2}>
  <Card title="미들웨어" icon="layer-group" href="/ko/middleware">
    작성, 라우트에 대한 적용, 파라미터, 우선순위의 기본을 확인합니다.
  </Card>

  <Card title="패키지의 라우트 등록과 캐시" icon="route" href="/ko/advanced/package-routes">
    라우트 로딩과 캐시 생성 시 설정의 관계를 확인합니다.
  </Card>
</Columns>

## 참고한 1차 자료

* [Laravel 공식 문서: 미들웨어](https://github.com/laravel/docs/blob/6bb0a3be3c623f7964c8277625a07b6df7f1de2e/middleware.md)
* [Laravel 공식 문서: 서비스 프로바이더의 boot와 의존성 주입](https://github.com/laravel/docs/blob/6bb0a3be3c623f7964c8277625a07b6df7f1de2e/providers.md#the-boot-method)
* [Router: 별칭 등록·그룹 조작·미들웨어의 전개와 정렬](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Routing/Router.php)
* [HTTP Kernel: syncMiddlewareToRouter](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Foundation/Http/Kernel.php)


## Related topics

- [패키지의 라우트 등록과 캐시](/ko/advanced/package-routes.md)
- [Laravel 패키지 개발](/ko/advanced/package-development.md)
- [패키지의 이벤트 리스너 등록과 캐시](/ko/advanced/package-events.md)
- [미들웨어](/ko/middleware.md)
- [컨트롤러 PHP 어트리뷰트](/ko/advanced/controller-attributes.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.