Files
Gnuboard7/docs/backend/broadcasting.md
T
2026-04-01 10:30:52 +09:00

11 KiB

Broadcasting (실시간 이벤트)

중요도: 중요 관련 문서: service-repository.md | authentication.md


TL;DR (5초 요약)

1. Laravel Reverb 사용 (WebSocket)
2. 이벤트: ShouldBroadcast 인터페이스 구현
3. 채널: public, private, presence
4. 훅 연동: HookManager에서 broadcast() 호출
5. 클라이언트: Laravel Echo + Pusher-js

목차

  1. 개요
  2. Laravel Reverb 설정
  3. 브로드캐스트 이벤트 생성
  4. 채널 인증
  5. API 인증 엔드포인트
  6. 훅을 통한 이벤트 발생
  7. 스케줄러를 통한 주기적 브로드캐스트
  8. 개발 환경 설정
  9. 프로덕션 환경 설정

개요

G7은 Laravel Reverb를 사용하여 실시간 WebSocket 통신을 지원합니다.

주요 사용 사례:

  • 대시보드 실시간 통계 업데이트
  • 알림 실시간 전송
  • 채팅 기능
  • 실시간 협업 기능

아키텍처:

클라이언트 (Echo/Pusher-js)
        ↕ WebSocket
Laravel Reverb 서버
        ↕
Laravel 백엔드 (broadcast() 호출)
        ↕
Queue Worker (이벤트 처리)

Laravel Reverb 설정

환경변수 (.env)

# Broadcasting 드라이버
BROADCAST_CONNECTION=reverb

# Reverb 서버 설정
REVERB_APP_ID=467955
REVERB_APP_KEY=zm0vobuy4zpqorc3ro9r
REVERB_APP_SECRET=your-secret-key
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=https

# 개발 환경에서 자체 서명 인증서 사용 시
REVERB_VERIFY_SSL=false

# 클라이언트용 설정 (Vite)
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

config/broadcasting.php

'reverb' => [
    'driver' => 'reverb',
    'key' => env('REVERB_APP_KEY'),
    'secret' => env('REVERB_APP_SECRET'),
    'app_id' => env('REVERB_APP_ID'),
    'options' => [
        'host' => env('REVERB_HOST'),
        'port' => env('REVERB_PORT', 443),
        'scheme' => env('REVERB_SCHEME', 'https'),
        'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
    ],
    'client_options' => [
        // 개발 환경에서 자체 서명 인증서 사용 시 SSL 검증 비활성화
        'verify' => env('REVERB_VERIFY_SSL', true),
    ],
],

주의: 프로덕션 환경에서는 REVERB_VERIFY_SSL=true로 설정해야 합니다.


브로드캐스트 이벤트 생성

기본 구조

<?php

namespace App\Events\Dashboard;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

/**
 * 대시보드 업데이트 브로드캐스트 이벤트
 */
class DashboardUpdated implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * 이벤트 인스턴스를 생성합니다.
     *
     * @param string $type 업데이트 타입 ('stats', 'resources', 'activities')
     * @param array $data 업데이트된 데이터
     */
    public function __construct(
        public string $type,
        public array $data
    ) {}

    /**
     * 브로드캐스트할 채널을 반환합니다.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [new PrivateChannel('admin.dashboard')];
    }

    /**
     * 브로드캐스트 이벤트명을 반환합니다.
     *
     * @return string
     */
    public function broadcastAs(): string
    {
        return "dashboard.{$this->type}.updated";
    }
}

채널 타입

타입 클래스 용도 인증 필요
Public Channel 모든 사용자 접근 가능 ❌
Private PrivateChannel 인증된 사용자만 접근 ✅
Presence PresenceChannel 인증 + 접속자 목록 공유 ✅

이벤트 발생

// 방법 1: broadcast() 헬퍼 (권장)
broadcast(new DashboardUpdated('stats', $statsData));

// 방법 2: event() 헬퍼
event(new DashboardUpdated('resources', $resourceData));

// 방법 3: 즉시 전송 (큐 미사용)
broadcast(new DashboardUpdated('alerts', $alertData))->toOthers();

채널 인증

routes/channels.php

<?php

use Illuminate\Support\Facades\Broadcast;

// 사용자별 Private 채널
Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

// 관리자 대시보드 채널 - 권한 체크
Broadcast::channel('admin.dashboard', function ($user) {
    return $user->hasPermission('core.dashboard.read');
});

// 특정 리소스 채널
Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
    return $user->hasPermission('ecommerce.orders.read');
});

인증 콜백 규칙

// ✅ DO: boolean 반환 (Private 채널)
Broadcast::channel('admin.dashboard', function ($user) {
    return $user->hasPermission('core.dashboard.read');
});

// ✅ DO: 배열 반환 (Presence 채널 - 사용자 정보 공유)
Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
    if ($user->canJoinRoom($roomId)) {
        return ['id' => $user->id, 'name' => $user->name];
    }
    return false;
});

// ❌ DON'T: 예외 던지기
Broadcast::channel('admin.dashboard', function ($user) {
    throw new \Exception('Unauthorized'); // 금지
});

API 인증 엔드포인트

G7은 SPA 환경에서 Sanctum 토큰 기반 인증을 사용합니다. 기본 /broadcasting/auth 엔드포인트는 세션 기반이므로, API용 별도 엔드포인트를 사용합니다.

routes/api.php

// 브로드캐스팅 인증 (Sanctum 토큰 사용)
Route::middleware(['auth:sanctum'])->post('broadcasting/auth', function (\Illuminate\Http\Request $request) {
    return \Illuminate\Support\Facades\Broadcast::auth($request);
})->name('api.broadcasting.auth');

클라이언트 설정 (WebSocketManager)

const pusherOptions = {
  // ... 기타 옵션
  authEndpoint: '/api/broadcasting/auth',
  auth: {
    headers: {
      'Authorization': `Bearer ${authToken}`,
      'Accept': 'application/json',
    },
  },
};

훅을 통한 이벤트 발생

Service 계층에서 데이터 변경 시 훅 리스너를 통해 브로드캐스트 이벤트를 발생시킵니다.

config/hooks.php

return [
    'listeners' => [
        // 대시보드 통계 업데이트 시 브로드캐스트
        'core.dashboard.stats_updated' => [
            \App\Listeners\BroadcastDashboardStats::class,
        ],

        // 사용자 생성 시 대시보드 통계 업데이트
        'core.user.after_create' => [
            \App\Listeners\UpdateDashboardStatsOnUserChange::class,
        ],
    ],
];

리스너 구현

<?php

namespace App\Listeners;

use App\Events\Dashboard\DashboardUpdated;
use App\Services\DashboardService;

class BroadcastDashboardStats
{
    public function __construct(
        private DashboardService $dashboardService
    ) {}

    /**
     * 대시보드 통계 브로드캐스트
     *
     * @param array $data 훅 데이터
     * @return void
     */
    public function handle(array $data): void
    {
        $stats = $this->dashboardService->getStats();
        broadcast(new DashboardUpdated('stats', $stats));
    }
}

스케줄러를 통한 주기적 브로드캐스트

시스템 리소스 등 주기적으로 업데이트가 필요한 데이터는 스케줄러를 사용합니다.

Artisan 커맨드

<?php

namespace App\Console\Commands;

use App\Events\Dashboard\DashboardUpdated;
use App\Services\DashboardService;
use Illuminate\Console\Command;

class BroadcastDashboardResources extends Command
{
    protected $signature = 'dashboard:broadcast-resources';
    protected $description = '시스템 리소스 정보를 WebSocket으로 브로드캐스트합니다.';

    public function handle(DashboardService $dashboardService): int
    {
        $resources = $dashboardService->getSystemResources();
        broadcast(new DashboardUpdated('resources', $resources));

        $this->info('시스템 리소스 정보가 브로드캐스트되었습니다.');
        return Command::SUCCESS;
    }
}

routes/console.php (스케줄러 등록)

use Illuminate\Support\Facades\Schedule;

// 30초마다 시스템 리소스 브로드캐스트
Schedule::command('dashboard:broadcast-resources')->everyThirtySeconds();

실행 방법

# 스케줄러 실행 (개발 환경)
php artisan schedule:work

# 큐 워커 실행 (브로드캐스트 이벤트 처리)
php artisan queue:work

# Reverb 서버 실행
php artisan reverb:start --debug

개발 환경 설정

필요한 프로세스

개발 환경에서 WebSocket을 테스트하려면 다음 프로세스가 모두 실행 중이어야 합니다:

# 터미널 1: Laravel 개발 서버
php artisan serve

# 터미널 2: Reverb WebSocket 서버
php artisan reverb:start --debug

# 터미널 3: 큐 워커 (브로드캐스트 이벤트 처리)
php artisan queue:work

# 터미널 4: 스케줄러 (주기적 브로드캐스트 사용 시)
php artisan schedule:work

SSL 인증서 문제 해결

개발 환경에서 자체 서명 인증서 사용 시 다음 설정이 필요합니다:

# .env
REVERB_VERIFY_SSL=false

이 설정은 Laravel이 Reverb 서버로 이벤트를 전송할 때 SSL 인증서 검증을 비활성화합니다.


프로덕션 환경 설정

권장 설정

# .env (프로덕션)
REVERB_VERIFY_SSL=true
REVERB_SCHEME=https
REVERB_PORT=443

Supervisor 설정 예시

[program:reverb]
command=php /var/www/g7/artisan reverb:start
directory=/var/www/g7
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/reverb.log

[program:queue-worker]
command=php /var/www/g7/artisan queue:work --sleep=3 --tries=3
directory=/var/www/g7
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/queue-worker.log

디버깅

이벤트 전송 확인

# 수동으로 이벤트 발생
php artisan dashboard:broadcast-resources

# 큐 작업 확인
php artisan queue:work --once

로그 확인

# Laravel 로그
tail -f storage/logs/laravel.log

# Reverb 서버 로그 (--debug 옵션 사용 시)
php artisan reverb:start --debug

클라이언트 디버깅

브라우저 개발자 도구에서:

  1. Network 탭 → WS 필터 → WebSocket 연결 확인
  2. Messages 탭에서 송수신 메시지 확인
  3. Console 탭에서 [WebSocketManager] 로그 확인

관련 문서