> ## 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의 브로드캐스팅과 Laravel Reverb를 사용한 WebSocket 실시간 통신의 구현 방법을 설명합니다.

## 브로드캐스팅이란

WebSocket을 사용하면 서버에서 클라이언트로 실시간으로 데이터를 전송할 수 있습니다.
Laravel의 브로드캐스팅은 서버 사이드의 이벤트를 WebSocket 경유로 프런트엔드의 JavaScript에 전달하는 구조입니다.

예를 들어 주문 상태가 변화했을 때, 페이지를 재로드하지 않아도 브라우저 상에서 즉시 업데이트를 표시할 수 있습니다.
서버 사이드의 이벤트 이름과 데이터를 클라이언트 사이드와 그대로 공유할 수 있는 것이 Laravel 브로드캐스팅의 강점입니다.

<Info>
  브로드캐스팅은 Laravel의 이벤트 시스템 위에 구축되어 있습니다.
  먼저 [이벤트와 리스너](/ko/events)의 기본을 이해해 두는 것을 권장합니다.
</Info>

```mermaid theme={null}
flowchart LR
    A["서버 사이드<br>이벤트 발화<br>dispatch()"] --> B["브로드캐스팅<br>드라이버<br>(Reverb 등)"]
    B --> C["WebSocket<br>서버"]
    C --> D["채널<br>인가 체크"]
    D --> E["클라이언트<br>Laravel Echo"]
    E --> F["UI 의<br>실시간 업데이트"]
```

## 셋업

신규 Laravel 애플리케이션에서 브로드캐스팅은 기본적으로 무효입니다.
`install:broadcasting` Artisan 명령어로 유효화합니다.

```shell theme={null}
php artisan install:broadcasting
```

이 명령어를 실행하면 사용할 브로드캐스팅 서비스를 선택하도록 촉구됩니다.
또한 `config/broadcasting.php`와 `routes/channels.php`가 생성됩니다.

## Laravel Reverb

Laravel 11 이후에서는 공식 WebSocket 서버인 **Laravel Reverb**가 권장됩니다.
Reverb는 셀프 호스트형으로, 추가의 외부 서비스 없이 실시간 통신을 실현합니다.

### 설치

`install:broadcasting` 명령어에 `--reverb` 옵션을 붙이면, Reverb에 필요한 Composer 패키지·NPM 패키지의 설치와 `.env`의 설정을 일괄로 수행합니다.

```shell theme={null}
php artisan install:broadcasting --reverb
```

수동으로 설치하는 경우, Composer로 패키지를 추가한 후에 설치 명령어를 실행합니다.

```shell theme={null}
composer require laravel/reverb

php artisan reverb:install
```

### .env 의 주요 설정값

```ini theme={null}
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
```

### Reverb 서버 기동

```shell theme={null}
php artisan reverb:start
```

운영 환경에서는 Supervisor 등의 프로세스 매니저로 데몬으로서 관리합니다.

<Tip>
  브로드캐스팅 이벤트는 큐 경유로 처리됩니다.
  Reverb 서버와는 별도로 큐 워커도 기동해 두어야 합니다.

  ```shell theme={null}
  php artisan queue:work
  ```
</Tip>

## 브로드캐스팅 이벤트 작성

### 이벤트 클래스 생성

```shell theme={null}
php artisan make:event OrderShipmentStatusUpdated
```

생성된 이벤트 클래스에 `ShouldBroadcast` 인터페이스를 구현합니다.

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

namespace App\Events;

use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;

class OrderShipmentStatusUpdated implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public function __construct(
        public Order $order,
    ) {}

    /**
     * 이벤트를 브로드캐스트할 채널을 반환
     */
    public function broadcastOn(): Channel
    {
        return new PrivateChannel('orders.' . $this->order->id);
    }
}
```

`ShouldBroadcast`를 구현하는 것만으로 이벤트가 발화되면 자동으로 큐 경유로 브로드캐스트됩니다.

### 브로드캐스팅 데이터 커스터마이즈

기본적으로 이벤트 클래스의 `public` 프로퍼티가 모두 브로드캐스팅의 페이로드에 포함됩니다.
전송할 데이터를 좁히려면 `broadcastWith` 메서드를 정의합니다.

```php theme={null}
public function broadcastWith(): array
{
    return [
        'order_id' => $this->order->id,
        'status'   => $this->order->status,
    ];
}
```

### 브로드캐스팅 이름 커스터마이즈

기본적으로 클래스 이름이 이벤트 이름이 됩니다.
`broadcastAs` 메서드로 커스텀 이름을 지정할 수 있습니다.

```php theme={null}
public function broadcastAs(): string
{
    return 'order.status.updated';
}
```

프런트엔드에서 리스닝할 때는 선두에 `.`을 붙여 앱의 네임스페이스 프리픽스를 무효화합니다.

```js theme={null}
Echo.private(`orders.${orderId}`)
    .listen('.order.status.updated', (e) => {
        console.log(e);
    });
```

## 채널의 종류

| 채널           | 클래스               | 설명                             |
| ------------ | ----------------- | ------------------------------ |
| **Public**   | `Channel`         | 인증 불필요. 누구나 구독 가능              |
| **Private**  | `PrivateChannel`  | 인증된 사용자만. 인가 로직이 필요            |
| **Presence** | `PresenceChannel` | Private의 확장. 채널의 참가자 목록을 취득 가능 |

```php theme={null}
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;

// Public 채널
public function broadcastOn(): Channel
{
    return new Channel('posts');
}

// Private 채널
public function broadcastOn(): Channel
{
    return new PrivateChannel('orders.' . $this->order->id);
}

// Presence 채널
public function broadcastOn(): Channel
{
    return new PresenceChannel('rooms.' . $this->room->id);
}
```

여러 채널로 브로드캐스트하려면 배열로 반환합니다.

```php theme={null}
public function broadcastOn(): array
{
    return [
        new PrivateChannel('orders.' . $this->order->id),
        new Channel('admin.orders'),
    ];
}
```

## 채널 인가

Private 채널과 Presence 채널은 구독 전에 서버 사이드에서 인가 체크가 수행됩니다.

### routes/channels.php

`install:broadcasting` 명령어로 생성되는 `routes/channels.php`에 인가 콜백을 정의합니다.

```php theme={null}
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
    return $user->id === Order::findOrNew($orderId)->user_id;
});
```

콜백의 첫 번째 인수는 인증된 사용자, 이후의 인수는 채널 이름의 와일드카드입니다.
`true` 또는 truthy한 값을 반환하면 인가 성공, `false`를 반환하면 거부됩니다.

<Info>
  라우트 모델 바인딩도 사용할 수 있습니다.
  채널 이름을 `orders.{order}`로 하면 `Order` 모델의 인스턴스가 전달됩니다.
</Info>

```php theme={null}
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
    return $user->id === $order->user_id;
});
```

### 채널 클래스에 의한 인가

채널이 늘어난 경우, 채널 클래스를 사용해 정리할 수 있습니다.

```shell theme={null}
php artisan make:channel OrderChannel
```

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

namespace App\Broadcasting;

use App\Models\Order;
use App\Models\User;

class OrderChannel
{
    public function join(User $user, Order $order): bool
    {
        return $user->id === $order->user_id;
    }
}
```

`routes/channels.php`에서 등록합니다.

```php theme={null}
use App\Broadcasting\OrderChannel;

Broadcast::channel('orders.{order}', OrderChannel::class);
```

## 이벤트 발화

`ShouldBroadcast`를 구현한 이벤트는 일반적인 이벤트와 동일하게 발화합니다.

```php theme={null}
use App\Events\OrderShipmentStatusUpdated;

OrderShipmentStatusUpdated::dispatch($order);
```

자기 자신을 제외한 다른 사용자에게만 브로드캐스트하고 싶은 경우 `toOthers`를 사용합니다.

```php theme={null}
broadcast(new OrderShipmentStatusUpdated($order))->toOthers();
```

<Warning>
  `toOthers`를 사용하려면 이벤트 클래스가 `InteractsWithSockets` 트레이트를 사용하고 있어야 합니다.
</Warning>

## 프런트엔드에서의 수신

### Laravel Echo 셋업

Reverb를 사용하는 경우, `resources/js/bootstrap.js`에 Echo 인스턴스를 설정합니다.

```shell theme={null}
npm install --save-dev laravel-echo pusher-js
```

```js theme={null}
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
```

### 이벤트 리스닝

```js theme={null}
// Public 채널
Echo.channel('posts')
    .listen('PostPublished', (e) => {
        console.log(e.post);
    });

// Private 채널
Echo.private(`orders.${orderId}`)
    .listen('OrderShipmentStatusUpdated', (e) => {
        console.log(e.order);
    });

// Presence 채널
Echo.join(`rooms.${roomId}`)
    .here((users) => {
        console.log('현재 멤버:', users);
    })
    .joining((user) => {
        console.log(user.name, '가 참가했습니다');
    })
    .leaving((user) => {
        console.log(user.name, '가 퇴장했습니다');
    })
    .listen('MessagePosted', (e) => {
        console.log(e.message);
    });
```

### React / Vue 훅의 사용

React·Vue의 스타터 킷을 사용하고 있는 경우, 전용 훅을 사용하면 간결하게 쓸 수 있습니다.

```js theme={null}
import { useEcho } from "@laravel/echo-react";

// Private 채널
useEcho(
    `orders.${orderId}`,
    "OrderShipmentStatusUpdated",
    (e) => {
        console.log(e.order);
    },
);

// Public 채널
import { useEchoPublic } from "@laravel/echo-react";

useEchoPublic("posts", "PostPublished", (e) => {
    console.log(e.post);
});
```

`useEcho` 훅은 컴포넌트의 언마운트 시에 자동으로 채널을 이탈합니다.

<Tip>
  프런트엔드의 애셋을 빌드하기 전에, `.env`의 `VITE_REVERB_*` 변수가 올바르게 설정되어 있는지 확인해 주십시오.

  ```shell theme={null}
  npm run build
  ```
</Tip>

## 실전 예시: 주문 상태의 실시간 업데이트

<Steps>
  <Step title="브로드캐스팅을 유효화">
    ```shell theme={null}
    php artisan install:broadcasting --reverb
    ```

    Reverb 서버와 큐 워커를 기동합니다.

    ```shell theme={null}
    php artisan reverb:start
    php artisan queue:work
    ```
  </Step>

  <Step title="이벤트 클래스를 작성">
    ```shell theme={null}
    php artisan make:event OrderShipmentStatusUpdated
    ```

    `app/Events/OrderShipmentStatusUpdated.php`를 편집합니다.

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

    namespace App\Events;

    use App\Models\Order;
    use Illuminate\Broadcasting\Channel;
    use Illuminate\Broadcasting\InteractsWithSockets;
    use Illuminate\Broadcasting\PrivateChannel;
    use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
    use Illuminate\Queue\SerializesModels;

    class OrderShipmentStatusUpdated implements ShouldBroadcast
    {
        use InteractsWithSockets, SerializesModels;

        public function __construct(
            public Order $order,
        ) {}

        public function broadcastOn(): Channel
        {
            return new PrivateChannel('orders.' . $this->order->id);
        }

        public function broadcastWith(): array
        {
            return [
                'order_id' => $this->order->id,
                'status'   => $this->order->status,
            ];
        }
    }
    ```
  </Step>

  <Step title="채널 인가를 정의">
    `routes/channels.php`에 인가 로직을 추가합니다.

    ```php theme={null}
    use App\Models\Order;
    use App\Models\User;
    use Illuminate\Support\Facades\Broadcast;

    Broadcast::channel('orders.{order}', function (User $user, Order $order) {
        return $user->id === $order->user_id;
    });
    ```
  </Step>

  <Step title="이벤트를 발화">
    주문 상태를 업데이트하는 컨트롤러나 Job에서 발화합니다.

    ```php theme={null}
    use App\Events\OrderShipmentStatusUpdated;

    $order->update(['status' => 'shipped']);

    OrderShipmentStatusUpdated::dispatch($order);
    ```
  </Step>

  <Step title="프런트엔드에서 리스닝">
    Blade 템플릿에 인라인 스크립트, 또는 React/Vue 컴포넌트로 수신합니다.

    ```js theme={null}
    Echo.private(`orders.${orderId}`)
        .listen('OrderShipmentStatusUpdated', (e) => {
            document.getElementById('status').textContent = e.status;
        });
    ```
  </Step>
</Steps>

## 다음 단계

<Card title="Laravel Reverb" href="/ko/reverb">
  Reverb 서버의 셋업, 운영 환경에서의 운용, 스케일링의 세부 사항을 확인하십시오.
</Card>

<Card title="큐와 잡" href="/ko/queues">
  브로드캐스팅은 큐 경유로 처리됩니다. 큐의 설정과 운용 방법을 확인하십시오.
</Card>

<Card title="이벤트와 리스너" href="/ko/events">
  브로드캐스팅의 기반이 되는 Laravel 이벤트 시스템의 세부 사항을 배웁니다.
</Card>


## Related topics

- [Laravel AI SDK](/ko/ai-sdk.md)
- [Laravel Reverb](/ko/reverb.md)
- [Laravel 11 이후의 새 앱 구조 FAQ](/ko/advanced/app-structure-faq.md)
- [SessionEvent](/ko/packages/laravel-copilot-sdk/session-event.md)
- [스트리밍](/ko/packages/laravel-copilot-sdk/streaming.md)
