브로드캐스팅이란
WebSocket을 사용하면 서버에서 클라이언트로 실시간으로 데이터를 전송할 수 있습니다.
Laravel의 브로드캐스팅은 서버 사이드의 이벤트를 WebSocket 경유로 프런트엔드의 JavaScript에 전달하는 구조입니다.
예를 들어 주문 상태가 변화했을 때, 페이지를 재로드하지 않아도 브라우저 상에서 즉시 업데이트를 표시할 수 있습니다.
서버 사이드의 이벤트 이름과 데이터를 클라이언트 사이드와 그대로 공유할 수 있는 것이 Laravel 브로드캐스팅의 강점입니다.
브로드캐스팅은 Laravel의 이벤트 시스템 위에 구축되어 있습니다.
먼저 이벤트와 리스너의 기본을 이해해 두는 것을 권장합니다.
신규 Laravel 애플리케이션에서 브로드캐스팅은 기본적으로 무효입니다.
install:broadcasting Artisan 명령어로 유효화합니다.
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의 설정을 일괄로 수행합니다.
php artisan install:broadcasting --reverb
수동으로 설치하는 경우, Composer로 패키지를 추가한 후에 설치 명령어를 실행합니다.
composer require laravel/reverb
php artisan reverb:install
.env 의 주요 설정값
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 서버 기동
운영 환경에서는 Supervisor 등의 프로세스 매니저로 데몬으로서 관리합니다.
브로드캐스팅 이벤트는 큐 경유로 처리됩니다.
Reverb 서버와는 별도로 큐 워커도 기동해 두어야 합니다.
브로드캐스팅 이벤트 작성
이벤트 클래스 생성
php artisan make:event OrderShipmentStatusUpdated
생성된 이벤트 클래스에 ShouldBroadcast 인터페이스를 구현합니다.
<?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 메서드를 정의합니다.
public function broadcastWith(): array
{
return [
'order_id' => $this->order->id,
'status' => $this->order->status,
];
}
브로드캐스팅 이름 커스터마이즈
기본적으로 클래스 이름이 이벤트 이름이 됩니다.
broadcastAs 메서드로 커스텀 이름을 지정할 수 있습니다.
public function broadcastAs(): string
{
return 'order.status.updated';
}
프런트엔드에서 리스닝할 때는 선두에 .을 붙여 앱의 네임스페이스 프리픽스를 무효화합니다.
Echo.private(`orders.${orderId}`)
.listen('.order.status.updated', (e) => {
console.log(e);
});
채널의 종류
| 채널 | 클래스 | 설명 |
|---|
| Public | Channel | 인증 불필요. 누구나 구독 가능 |
| Private | PrivateChannel | 인증된 사용자만. 인가 로직이 필요 |
| Presence | PresenceChannel | Private의 확장. 채널의 참가자 목록을 취득 가능 |
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);
}
여러 채널로 브로드캐스트하려면 배열로 반환합니다.
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에 인가 콜백을 정의합니다.
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를 반환하면 거부됩니다.
라우트 모델 바인딩도 사용할 수 있습니다.
채널 이름을 orders.{order}로 하면 Order 모델의 인스턴스가 전달됩니다.
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
return $user->id === $order->user_id;
});
채널 클래스에 의한 인가
채널이 늘어난 경우, 채널 클래스를 사용해 정리할 수 있습니다.
php artisan make:channel OrderChannel
<?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에서 등록합니다.
use App\Broadcasting\OrderChannel;
Broadcast::channel('orders.{order}', OrderChannel::class);
이벤트 발화
ShouldBroadcast를 구현한 이벤트는 일반적인 이벤트와 동일하게 발화합니다.
use App\Events\OrderShipmentStatusUpdated;
OrderShipmentStatusUpdated::dispatch($order);
자기 자신을 제외한 다른 사용자에게만 브로드캐스트하고 싶은 경우 toOthers를 사용합니다.
broadcast(new OrderShipmentStatusUpdated($order))->toOthers();
toOthers를 사용하려면 이벤트 클래스가 InteractsWithSockets 트레이트를 사용하고 있어야 합니다.
프런트엔드에서의 수신
Laravel Echo 셋업
Reverb를 사용하는 경우, resources/js/bootstrap.js에 Echo 인스턴스를 설정합니다.
npm install --save-dev laravel-echo pusher-js
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'],
});
이벤트 리스닝
// 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의 스타터 킷을 사용하고 있는 경우, 전용 훅을 사용하면 간결하게 쓸 수 있습니다.
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 훅은 컴포넌트의 언마운트 시에 자동으로 채널을 이탈합니다.
프런트엔드의 애셋을 빌드하기 전에, .env의 VITE_REVERB_* 변수가 올바르게 설정되어 있는지 확인해 주십시오.
실전 예시: 주문 상태의 실시간 업데이트
브로드캐스팅을 유효화
php artisan install:broadcasting --reverb
Reverb 서버와 큐 워커를 기동합니다.php artisan reverb:start
php artisan queue:work
이벤트 클래스를 작성
php artisan make:event OrderShipmentStatusUpdated
app/Events/OrderShipmentStatusUpdated.php를 편집합니다.<?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,
];
}
}
채널 인가를 정의
routes/channels.php에 인가 로직을 추가합니다.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;
});
이벤트를 발화
주문 상태를 업데이트하는 컨트롤러나 Job에서 발화합니다.use App\Events\OrderShipmentStatusUpdated;
$order->update(['status' => 'shipped']);
OrderShipmentStatusUpdated::dispatch($order);
프런트엔드에서 리스닝
Blade 템플릿에 인라인 스크립트, 또는 React/Vue 컴포넌트로 수신합니다.Echo.private(`orders.${orderId}`)
.listen('OrderShipmentStatusUpdated', (e) => {
document.getElementById('status').textContent = e.status;
});
다음 단계
Laravel Reverb
Reverb 서버의 셋업, 운영 환경에서의 운용, 스케일링의 세부 사항을 확인하십시오.
큐와 잡
브로드캐스팅은 큐 경유로 처리됩니다. 큐의 설정과 운용 방법을 확인하십시오.
이벤트와 리스너
브로드캐스팅의 기반이 되는 Laravel 이벤트 시스템의 세부 사항을 배웁니다.