Realtime broadcasting with Laravel and NuxtJS (2/4)

Local Setup

일단, Laravel 6.16 와 NuxtJS 2.11 를 사용한 “out of the box” Laravel 브로드캐스트 기능이 로컬 환경에서 작동하도록 설정 후, 전반적인 구현 디테일을 분석해 본다.

사용자가 브라우저로 접속할 frontend 주소는 localhost:3000 이고, Laradock 으로 설정한 backend API 는 amuse.test 라는 주소 값을 갖고 있으며, Echo 서버는 websockets.test:6001 로 접근가능한 구성이다. 아래의 그림을 참고하기 바란다. Laradock 환경에서, Echo 서버가 amuse.test 으로, 접근할 수 있도록 설정하는 것이 tricky 할 수 있는데 이는 이전 Laradock 관련 포스팅을 참고하기 바란다. (https://whatsupkorea.com/2018/05/26/laradock-with-muliple-projects)

Laravel 브로드캐스트 기능을 NuxtJS 를 사용하는 프로젝트에서 사용하기 위해서는, Laravel Echo 의 NuxtJS 용 wrapper 모듈 @nuxtjs/laravel-echo 을 설치 후, nuxt.config.js 파일을 아래와 같이 설정한다.

  :
  buildModules: [
    '@nuxtjs/dotenv',
    '@nuxtjs/eslint-module',
    '@nuxtjs/vuetify',
    [
      '@nuxtjs/laravel-echo',
      {
        broadcaster: 'socket.io',
        host: 'http://websockets.test:6001',
        plugins: ['~/plugins/echo.js'],
        // transports: ['websocket', 'polling'],
        authModule: true,
        connectOnLogin: true,
        disconnectOnLogout: true
      }
    ],
  ],
  :

plugins/echo.js 에는 아래와 같이 public 채널, Laravel notification 용 프라이빗 채널, 그리고 presence 채널을 위한 3개의 리스너를 추가했다. 각각의 채널은 use-case 별로 추가확장 할 수 있으므로, 가장 심플한 형태의 working example 로 보면 되겠다.

export default function({ $auth, $echo, store, app: { $toast } }) {
  $echo.channel('public').listen('PublicMessageSent', (event) => {
    console.log('data from public', event)
  })

  if ($auth.loggedIn) {
    $echo
      .private(`App.Models.User.${$auth.user.id}`)
      .notification((notification) => {
        console.log(notification)
        $toast.success(notification.message)
        store.commit('notifications/INCREASE_UNREAD_COUNT')
      })

    $echo
      .join('presence')
      .here((users) => {
        store.commit('online/SET_USERS', users)
      })
      .joining((user) => {
        store.commit('online/JOIN_USER', user)
      })
      .leaving((user) => {
        store.commit('online/LEAVE_USER', user)
      })
  }
}

2. Laravel Echo Server 를 위한 laravel-echo-server.json 파일의 내용은 아래와 같이 설정한다.

참고로, Laravel 의 config/database.php Redis 옵션의 prefix 값 즉, config('database.redis.options.prefix') 의 값이 app 내 사용한 채널명과 조합하여 사용되므로, 이를 frontend 에서도 동일한 이름으로 해당 채널을 지정하려면 databaseConfig.redis.keyPrefix 옵션을 같은 값으로 셋팅해주는 것이 좋다. 그러면, Redis 에 실제 설정된 키값에 상관없이 PHP 코드에서는 new PrivateChannel('chat'); 이라 사용하고, JS 코드에서는 Echo.private('chat').listen() 라고 사용하는 것이 가능하다.

{
  "authHost": "http://amuse.test",
  "authEndpoint": "/broadcasting/auth",
  "clients": [
    {
      "appId": "1111aaaa9999cccc",
      "key": "8888dddd2222bbbb4444ffff6666eeee"
    }
  ],
  "database": "redis",
  "databaseConfig": {
    "redis": {
      "port": "6379",
      "host": "redis",
      "keyPrefix": "laravel_database_"
    },
    "sqlite": {
      "databasePath": "/database/laravel-echo-server.sqlite"
    }
  },
  "devMode": true,
  "host": null,
  "port": "6001",
  "protocol": "http",
  "socketio": {},
  "secureOptions": 66666666,
  "sslCertPath": "",
  "sslKeyPath": "",
  "sslCertChainPath": "",
  "sslPassphrase": "",
  "subscribers": {
    "http": true,
    "redis": true
  },
  "apiOriginAllow": {
    "allowCors": true,
    "allowOrigin": "http://localhost:3000",
    "allowMethods": "GET, POST",
    "allowHeaders": "Origin, Content-Type, X-Auth-Token, X-Requested-With, Accept, Authorization, X-CSRF-TOKEN, X-Socket-Id"
  }
}

3. Laravel 7 이 아니라면, CORS 설정을 위해 "fruitcake/laravel-cors": "^1.0" 디펜던시를 수동으로 추가하고, config/cors.php를 아래와 같이 설정한다.

<?php

return [
    'paths' => ['*'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => false,
    'max_age' => false,
    'supports_credentials' => false,
];

위의 설정으로 public, private, presence 채널 접속이 가능하며, Load balancer 를 Echo 서버 앞에 설정하는 구성이라면 HTTPS 를 통신을 위한 Echo 서버의 추가 설정 또한 필요없어진다. 이제 NuxtJS 앱에서, 동적으로 프라이빗 chat.{room id#} 채널을 listen() 하거나 leave() 하면서 웹소켓통신을 할 수 있도록 만들면, 비교적 수월하게 아래 그림과 같은 채팅서비스를 구현할 수 있다.

그럼 당장 Laravel Echo 기능을 이용한 채팅 서비스를 프로덕션에 적용해보자. 라고 할 줄 알았다면… Not so fast.

Leave a Reply