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

# 패키지의 브로드캐스트 채널 인가와 덮어쓰기

> 패키지가 프라이빗 채널을 등록할 때 인가가 평가되는 순서, 사용하는 애플리케이션이 이를 덮어쓰는 방식, HTTP 테스트로 확인하는 방법을 설명합니다.

패키지가 실시간 이벤트를 전송할 때는 **채널 이름의 정의**와 **사용자를 통과시키는 판정**을 나누어 설계합니다. 패키지의 `boot()`에서 등록한 채널은 사용하는 애플리케이션의 `routes/channels.php`와 이름이 같으면 덮어써질 수 있습니다. 이 등록 순서와 평가 순서를 이해해 두면 유지보수 시의 버그를 줄일 수 있습니다.

이 페이지는 Laravel 공식 문서의 [브로드캐스팅](https://kawax.biz/ko/broadcasting.md)과 확인에 사용한 최신 릴리스 `v13.42.0`의 구현을 참조합니다. 이벤트를 전송하는 방법은 [브로드캐스팅](https://kawax.biz/ko/broadcasting.md)을, 프로바이더를 불러오는 방법은 [패키지 자동 검색](https://kawax.biz/ko/advanced/package-discovery.md)을 참조하세요.

## 채널 인가의 동작 방식

프라이빗 채널을 구독하면 클라이언트는 `/broadcasting/auth`로 요청을 보냅니다. `BroadcastController::authenticate()`는 처리를 `Broadcast::auth()`에 넘기고, `Broadcaster`가 등록된 콜백을 순서대로 평가합니다.

평가 규칙은 다음과 같습니다.

* 콜백이 `false`를 반환하면 그 시점에 `AccessDeniedHttpException`이 발생합니다.
* `true` 같은 참 값을 반환하면 그 시점에 인가가 성립합니다.
* `null` 같은 거짓 값을 반환하면 다음에 등록된 콜백으로 넘어갑니다. 어느 콜백도 통과시키지 않으면 마지막에 `AccessDeniedHttpException`이 발생합니다.

> [!NOTE]
>
> `Pusher`와 `Reverb` 드라이버는 인가 전에 `private-`와 `presence-` 접두사를 제거합니다. 따라서 등록하는 채널 이름에는 접두사를 포함하지 마세요.

## 패키지에서 채널 등록하기

예로, 가상의 `Acme\Orders` 패키지에서 주문 상태 변화를 전송합니다. 채널 이름은 `acme.orders.{order}`로 하고, 주문 소유자만 구독할 수 있게 합니다.

패키지의 `ServiceProvider`에서는 `boot()` 안에서 `Broadcast::channel()`을 호출합니다. 판정은 패키지 내부 클래스에 위임합니다.

```php theme={null}
use Acme\Orders\Broadcasting\OrderChannel;
use Illuminate\Support\Facades\Broadcast;

public function boot(): void
{
    Broadcast::channel('acme.orders.{order}', OrderChannel::class);
}
```

```php theme={null}
<?php

namespace Acme\Orders\Broadcasting;

use Acme\Orders\Models\Order;
use Illuminate\Contracts\Auth\Authenticatable;

class OrderChannel
{
    public function join(Authenticatable $user, Order $order): bool
    {
        return $user->getAuthIdentifier() === $order->owner_id;
    }
}
```

`join()`의 첫 번째 인수에는 인가 시점의 사용자가 전달됩니다. 이후 인수는 채널 이름의 `{...}` 값입니다. `{order}` 파라미터 이름과 `Order $order`가 일치하고 타입이 `UrlRoutable`의 서브클래스이면 라우트 모델 바인딩이 동작합니다.

> [!NOTE]
>
> `Broadcast::channel()`의 `$callback`에는 `callable` 또는 `join()` 메서드를 가진 클래스 이름을 지정할 수 있습니다. 클래스 이름인 경우 `Broadcaster::normalizeChannelHandlerToCallable()`이 컨테이너로 인스턴스를 생성한 뒤 `join()`을 호출합니다.

## 사용하는 애플리케이션에 의한 덮어쓰기

사용하는 애플리케이션이 `withBroadcasting()`에 전달한 파일(보통 `routes/channels.php`)은 `withBroadcasting()`이 등록하는 `booted` 콜백 안에서 불러옵니다. `booted` 콜백은 모든 서비스 프로바이더의 `boot()`가 끝난 뒤에 실행되므로, 패키지 `boot()`의 등록이 먼저 이루어집니다.

| 순서 | 처리 | 등록되는 것 |
| - | - | - |
| 1 | 패키지의 `boot()` | `acme.orders.{order}`의 콜백 |
| 2 | 애플리케이션 `booted`에서의 라우트 로드 | `routes/channels.php`의 콜백 |

`Broadcaster::channel()`은 채널 이름을 키로 하여 내부 배열에 저장합니다. 따라서 사용하는 애플리케이션이 같은 이름을 등록하면 두 번째 등록이 패키지의 등록을 대체합니다. Laravel은 같은 이름의 등록을 중복 오류로 처리하지 않습니다.

덮어쓰기를 막으려면 채널 이름을 고유하게 만드세요. 예를 들어 `acme.orders.{order}`처럼 패키지 고유의 접두사를 붙이면 사용하는 애플리케이션이 같은 이름을 등록할 가능성이 낮아집니다.
채널 이름은 클라이언트와의 공개된 계약입니다. 한 번 정하면 변경하지 마세요. 변경하면 기존 클라이언트가 구독할 수 없게 됩니다.

> [!WARNING]
>
> 같은 이름의 채널은 나중에 등록한 것이 조용히 우선합니다. `php artisan channel:list`에서는 채널 이름과 해석 대상(클래스 이름 또는 `Closure`)이 표시되므로 어느 등록이 남아 있는지 확인할 수 있습니다.

## 채널 옵션과 가드

`Broadcast::channel()`의 세 번째 인수 `$options`는 채널별 설정으로 저장됩니다. `guards`를 지정하면 인증에 사용할 가드를 순서대로 시도합니다.

```php theme={null}
Broadcast::channel('acme.orders.{order}', OrderChannel::class, [
    'guards' => ['web', 'api'],
]);
```

`guards`를 생략하면 요청의 기본 사용자(`$request->user()`)를 인가에 사용합니다. `guards`를 지정하면 앞에서부터 순서대로 `$request->user($guard)`를 호출하고, 처음 얻은 사용자를 사용합니다. 패키지 쪽에서 가드 이름을 고정하지 않고 설정값으로 공개하면 사용하는 애플리케이션이 선택할 수 있습니다.

## 테스트에서 채널 등록 확인하기

등록된 채널은 드라이버의 `getChannels()`로 `Collection`으로 가져올 수 있습니다. 키는 채널 이름 그 자체입니다.

```php theme={null}
use Illuminate\Support\Facades\Broadcast;

public function test_package_registers_order_channel(): void
{
    $channels = Broadcast::driver()->getChannels();

    $this->assertTrue($channels->has('acme.orders.{order}'));
}
```

인가 결과는 `/broadcasting/auth`에 대한 HTTP 테스트로 확인합니다. `null`과 `log` 드라이버는 `auth()`가 아무것도 반환하지 않으므로 이 테스트에는 사용할 수 없습니다. 채널은 애플리케이션 시작 시 기본 드라이버에 등록되므로, 테스트용 `phpunit.xml` 등에서 `BROADCAST_CONNECTION`을 `reverb`나 `pusher`로 설정한 뒤 인증된 요청을 보냅니다.

```php theme={null}
use Acme\Orders\Models\Order;
use App\Models\User;

public function test_owner_can_join_order_channel(): void
{
    $user = User::factory()->create();
    $order = Order::factory()->for($user, 'owner')->create();

    $this->actingAs($user)
        ->post('/broadcasting/auth', [
            'channel_name' => 'private-acme.orders.'.$order->id,
            'socket_id' => '1234.5678',
        ])
        ->assertOk();
}
```

> [!TIP]
>
> 테스트 앱에 `/broadcasting/auth`가 등록되어 있어야 합니다. 이 라우트는 `Broadcast::routes()`가 등록하며, `BroadcastManager::channelRoutes()`는 같은 처리를 `routes()`에 맡길 뿐입니다. 라우트가 캐시되어 있으면 `routes()`는 아무것도 하지 않습니다.

## 유지보수 시 주의점

* 채널 이름은 공개된 계약입니다. 이름을 변경하면 기존 클라이언트가 구독할 수 없게 됩니다.
* 클래스 기반 채널은 `Container`로 해석됩니다. 생성자의 의존성은 컨테이너로 해석할 수 있는 타입으로 하세요.

## 정리

패키지의 채널 인가는 `boot()`에서 `Broadcast::channel()`을 등록하면 동작합니다. 사용하는 애플리케이션의 `routes/channels.php`가 같은 이름을 나중에 등록하면 패키지의 인가는 대체됩니다. 채널 이름에 접두사를 붙이고, 판정을 클래스로 분리하고, HTTP 테스트로 `/broadcasting/auth`의 결과를 확인하면 유지보수하기 쉬워집니다.

## 참조한 1차 정보

* [Laravel 공식 문서: 브로드캐스팅](https://github.com/laravel/docs/blob/13.x/broadcasting.md)
* [Framework v13.42.0: Broadcaster](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php)
* [Framework v13.42.0: UsePusherChannelConventions](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/Broadcasters/UsePusherChannelConventions.php)
* [Framework v13.42.0: BroadcastManager](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/BroadcastManager.php)
* [Framework v13.42.0: ApplicationBuilder](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Foundation/Configuration/ApplicationBuilder.php)
* [Framework v13.42.0: BroadcastController](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Broadcasting/BroadcastController.php)
* [Framework v13.42.0: channel:list 명령어](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Foundation/Console/ChannelListCommand.php)


## Related topics

- [Mercure 브로드캐스트 드라이버의 내부 구조](https://kawax.biz/ko/advanced/mercure-broadcasting.md)
- [패키지 번역의 덮어쓰기와 업데이트](https://kawax.biz/ko/advanced/package-translations.md)
- [브로드캐스팅](https://kawax.biz/ko/broadcasting.md)
- [패키지 뷰의 덮어쓰기와 업데이트](https://kawax.biz/ko/advanced/package-views.md)
- [패키지의 서비스 해석 후 훅](https://kawax.biz/ko/advanced/package-service-resolution.md)


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