액션 케이블과 웹소켓

 Biweekly Lecture

2015-07-28

Ruby on Rails Korea

- ROR Lab. Season 4 -

순서

  • 개요
  • 젬 추가
  • 테스트 뷰 작성
  • 액션케이블 설정(메시지 채널 등)
  • 메시지를 스트림으로 전달
  • 웹브라우저가 메시지를 수신
  • 참고자료

액션케이블 개요

What is ActionCable:

  • 레일스5의 새로운 주요 기능
  • 실시간으로 상호작용하기 위한 라이브러리
  • 채팅 앱 예시

필요한 젬 추가 Gemfile

  • puma/thin 멀티 쓰레드 필요(웹서버와 별도 실행)
  • unicorn 도 사용할 수 있으나 추천하지 않음
  • 레일스 4.2에서도 액션케이블을 사용할 수 있음
  • 레일스 5(깃헙) 사용할 경우 루비버전 2.2.2 이상필수
source 'https://rubygems.org'

gem 'rails', '4.2.3'
gem 'actioncable', github: 'rails/actioncable'

gem 'sqlite3'
gem 'coffee-rails', '~> 4.1.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'puma'
gem 'uglifier', '>= 1.3.0'

group :development, :test do
  gem 'byebug'
  gem 'spring'
  gem 'web-console', '~> 2.0'
end

테스트 뷰 작성

테스트 뷰 작성

  • SessionsController 첫화면(채팅 유저네임을 입력)
    • create(입력 선택하여 채팅창으로 들어감)
  • MessagesController 채팅 창(유저네임과 메시지 표시)
    • index(채팅 창), create(메시지 생성)
  • route.rb 첫화면 session#new
  • app/views/sessions/index.html.erb 유저네임 선택
  • app/views/messages/index.html.erb form_for remote 옵션

테스트 뷰 작성(session)

  • MessagesController 채팅 창(유저네임과 메시지 표시)
    • index(채팅 창), create(메시지 생성)
  • SessionsController 첫화면(채팅 유저네임을 입력)
    • create(입력 선택하여 채팅창으로 들어감)
# app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    cookies.signed[:username] = params[:session][:username]
    redirect_to messages_path
  end
end
<%= form_for :session, url: sessions_path do |f| %>
  <%= f.label :username, 'Enter a username' %><br/>
  <%= f.text_field :username %><br/>
  <%= f.submit 'Start chatting' %>
<% end %>

테스트 뷰 작성(message)

  • remote: true 이 옵션으로 화면 새로고침하지 않고 비동기 자바스크립트 통신(AJAX) 함.
  • #messages div 엘리멘트에 채팅 메시지 계속 추가
class MessagesController < ApplicationController
  def create
    head :ok
  end
end
Signed in as @<%= cookies.signed[:username] %>.
<br/><br/>

<div id='messages'></div>
<br/><br/>

<%= form_for :message, url: messages_path, remote: true, id: 'messages-form' do |f| %>
  <%= f.label :body, 'Enter a message:'  %><br/>
  <%= f.text_field :body %><br/>
  <%= f.submit 'Send message' %>
<% end %>

액션케이블 설정

레일스 프로젝트에 사용자 직접 만들어야 하는 파일

  • app/channels/application_cable/connection.rb
  • app/channels/application_cable/channel.rb
  • config/redis/cable.yml  레디스로 (PUBLISH/SUBSCRIBE)
  • cable/config.ru 웹애플리케이션과 별도 실행하기 위한 Rackup파일
  • bin/cable 긴 명령어를 줄여서 실행하는 배시 스크립트
  • app/channels/messages_channel.rb

액션케이블(channel, redis)

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
  end
end

# app/channels/application_cable/channel.rb
module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end
local: &local
  :url: redis://localhost:6379
  :host: localhost
  :port: 6379
  :timeout: 1
  :inline: true
development: *local
test: *local

액션케이블(rackup)

# cable/config.ru
require ::File.expand_path('../../config/environment',  __FILE__)
Rails.application.eager_load!

require 'action_cable/process/logging'

run ActionCable.server
# /bin/bash
bundle exec puma -p 28080 cable/config.ru

액션케이블(messages)

# app/channels/messages_channel.rb
class MessagesChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'messages'
  end
end

메시지 전달

레일스 프로젝트에 사용자 직접 만들어야 하는 파일

  • app/channels/application_cable/connection.rb
  • app/channels/application_cable/channel.rb
  • config/redis/cable.yml  레디스로 (PUBLISH/SUBSCRIBE)
  • cable/config.ru 웹애플리케이션과 별도 실행하기 위한 Rackup파일
  • bin/cable 긴 명령어를 줄여서 실행하는 배시 스크립트
  • app/channels/messages_channel.rb
class MessagesController < ApplicationController
  def create
    ActionCable.server.broadcast 'messages',
      message: params[:message][:body],
      username: cookies.signed[:username]

    head :ok
  end
end

웹브라우저가 메시지를 수신

  • app/assets/javascripts/channels/index.coffee 웹소켓 서버 연결
  • app/assets/javascripts/channels/messages.coffee
    • received 콜백은 메시지를 수신하고 JSON으로 넘어온 데이터에서 유저네임과 메시지를 div 엘리먼트에 추가한다.
# app/assets/javascripts/channels/index.coffee

#= require cable
#= require_self
#= require_tree .

@App = {}
App.cable = Cable.createConsumer 'ws://127.0.0.1:28080'
# app/assets/javascripts/channels/index.coffee

App.messages = App.cable.subscriptions.create 'MessagesChannel',
  received: (data) ->
    $('#messages').append @renderMessage(data)

  renderMessage: (data) ->
    "<p><b>[#{data.username}]:</b> #{data.message}</p>"

참고자료

  • 예시 프로젝트(니딘베칼 nithinbekal)
    • http://nithinbekal.com/posts/rails-action-cable
  • 레일스 5버전으로 만든 액션 케이블 예시 소스(니딘베칼)

    • https://github.com/nithinbekal/actioncable-chat-example

  • 레일스 5 를 사용할 경우 젬 파일 구성

    • https://github.com/nithinbekal/actioncable-chat-example/blob/f0f4c7269c0c491576fd23db4fd11c09ebe69f54/Gemfile

  • 액션케이블 소스 저장소

    • https://github.com/rails/actioncable

  • 레일스 개발 팀이 공개한 액션 케이블 예시

    • https://github.com/rails/actioncable-examples

  • 고 레일스(동영상) 레일스 개발팀의 액션 케이블 예시를 설명

    • https://gorails.com/episodes/rails-5-actioncable-websockets

  • 래피드파이어(설문조사) 관련 액션케이블 예시

    • https://github.com/code-mancers/rapidfire-demo/compare/actioncable

간단한 채팅 애플리케이션

데모

감사합니다

액션케이블과 웹소켓 ActionCable and Websocket

By wagurano

액션케이블과 웹소켓 ActionCable and Websocket

액션케이블과 웹소켓 ActionCable and Websocket

  • 2,308