Action Cable 개요
이 가이드에서는 Action Cable이 어떻게 작동하고 WebSocket을 사용하여 Rails 애플리케이션에 실시간 기능을 통합하는 방법을 배울 것입니다.
이 가이드를 읽고 나면 다음을 알 수 있습니다:
- Action Cable이 무엇이며 백엔드와 프런트엔드 통합 방법
- Action Cable 설정 방법
- 채널 설정 방법
- Action Cable 실행을 위한 배포 및 아키텍처 설정
Action Cable이란 무엇인가?
Action Cable은 Rails 애플리케이션의 나머지 부분과 원활하게 통합되는 WebSocket을 제공합니다. 이를 통해 Ruby로 작성된 실시간 기능을 Rails 애플리케이션의 나머지 부분과 동일한 스타일과 형식으로 작성할 수 있으면서도 성능과 확장성을 유지할 수 있습니다. Action Cable은 클라이언트 측 JavaScript 프레임워크와 서버 측 Ruby 프레임워크를 모두 제공하는 완전한 스택 솔루션입니다. Active Record 또는 선택한 ORM으로 작성된 전체 도메인 모델에 액세스할 수 있습니다.
용어
Action Cable은 HTTP 요청-응답 프로토콜 대신 WebSocket을 사용합니다. Action Cable과 WebSocket은 다음과 같은 익숙하지 않은 용어를 도입합니다:
연결
연결은 클라이언트-서버 관계의 기반을 형성합니다. 단일 Action Cable 서버는 여러 연결 인스턴스를 처리할 수 있습니다. 각 WebSocket 연결에 대해 하나의 연결 인스턴스가 있습니다. 단일 사용자는 여러 브라우저 탭 또는 장치를 사용하는 경우 애플리케이션에 여러 WebSocket을 열 수 있습니다.
소비자
WebSocket 연결의 클라이언트는 소비자라고 합니다. Action Cable에서 소비자는 클라이언트 측 JavaScript 프레임워크에 의해 생성됩니다.
채널
각 소비자는 여러 채널에 가입할 수 있습니다. 각 채널은 일반적인 MVC 설정에서 컨트롤러가 하는 것과 유사한 논리적 작업 단위를 캡슐화합니다. 예를 들어 ChatChannel
과 AppearanceChannel
을 가질 수 있으며, 소비자는 이 두 채널 중 하나 또는 둘 다에 가입할 수 있습니다. 최소한 소비자는 하나의 채널에 가입해야 합니다.
구독자
소비자가 채널에 가입하면 구독자로 행동합니다. 구독자와 채널 간의 연결은 구독이라고 합니다. 소비자는 주어진 채널에 대해 여러 번 구독자로 행동할 수 있습니다. 예를 들어 소비자는 동시에 여러 채팅방에 가입할 수 있습니다. (그리고 물리적 사용자는 열린 탭/장치당 하나씩 여러 소비자를 가질 수 있다는 것을 기억하세요).
Pub/Sub
Pub/Sub 또는 발행-구독은 정보 발행자(발행자)가 개별 수신자를 지정하지 않고 추상 수신자 클래스(구독자)에게 데이터를 보내는 메시지 큐 패러다임을 말합니다. Action Cable은 이 접근 방식을 사용하여 서버와 많은 클라이언트 간에 통신합니다.
브로드캐스팅
브로드캐스팅은 발행자가 전송한 모든 것이 해당 브로드캐스팅을 스트리밍하는 채널 구독자에게 직접 전송되는 pub/sub 링크입니다. 각 채널은 0개 이상의 브로드캐스팅을 스트리밍할 수 있습니다.
서버 측 구성 요소
연결
서버가 수락하는 모든 WebSocket에 대해 연결 객체가 인스턴스화됩니다. 이 객체는 그 이후에 생성되는 모든 채널 구독의 부모가 됩니다. 연결 자체는 인증 및 권한 부여 이외의 특정 애플리케이션 로직을 처리하지 않습니다. WebSocket 연결의 클라이언트는 연결 소비자라고 합니다. 개별 사용자는 열린 브라우저 탭, 창 또는 장치마다 하나의 소비자-연결 쌍을 만듭니다.
연결은 ActionCable::Connection::Base
를 확장하는 ApplicationCable::Connection
인스턴스입니다. ApplicationCable::Connection
에서 들어오는 연결을 승인하고 사용자를 식별할 수 있는 경우 연결을 설정합니다.
연결 설정
# app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user if verified_user = User.find_by(id: cookies.encrypted[:user_id]) verified_user else reject_unauthorized_connection end end end end
여기서 identified_by
는 나중에 특정 연결을 찾는 데 사용할 수 있는 연결 식별자를 지정합니다. 식별자로 표시된 것은 자동으로 연결에서 파생된 채널 인스턴스에 대한 위임자가 됩니다.
이 예에서는 애플리케이션의 다른 부분에서 이미 사용자 인증을 처리했으며 성공적인 인증이 사용자 ID로 암호화된 쿠키를 설정한다는 사실에 의존합니다.
연결이 시도될 때 쿠키가 자동으로 연결 인스턴스에 전송되며 이를 사용하여 current_user
를 설정합니다. 이 same current user로 연결을 식별함으로써 나중에 특정 사용자의 모든 열린 연결을 검색할 수 있게 되며(사용자가 삭제되거나 권한이 없어진 경우 모든 연결을 연결 해제할 수 있음) 합니다.
사용자 인증 접근 방식에 세션 사용이 포함되고 세션 쿠키에 _session
이름이 지정되며 사용자 ID 키가 user_id
인 경우 다음 접근 방식을 사용할 수 있습니다:
verified_user = User.find_by(id: cookies.encrypted['_session']['user_id'])
예외 처리
기본적으로 처리되지 않은 예외는 캡처되어 Rails 로거에 기록됩니다. 이러한 예외를 전역적으로 가로채고 외부 버그 추적 서비스에 보고하려는 경우 rescue_from
을 사용할 수 있습니다:
# app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base rescue_from StandardError, with: :report_error private def report_error(e) SomeExternalBugtrackingService.notify(e) end end end
연결 콜백
ActionCable::Connection::Callbacks
는 클라이언트에 명령을 보낼 때 호출되는 콜백 훅을 제공합니다. 예를 들어 가입, 가입 취소 또는 작업 수행 시:
채널
채널은 일반적인 MVC 설정에서 컨트롤러가 하는 것과 유사한 논리적 작업 단위를 캡슐화합니다. 기본적으로 Rails는 공유 로직을 캡슐화하기 위한 부모 ApplicationCable::Channel
클래스(which extends ActionCable::Channel::Base
)를 만듭니다.
부모 채널 설정
# app/channels/application_cable/channel.rb module ApplicationCable class Channel < ActionCable::Channel::Base end end
그런 다음 자체 채널 클래스를 만들 수 있습니다. 예를 들어 ChatChannel
과 AppearanceChannel
을 가질 수 있습니다:
# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel end
# app/channels/appearance_channel.rb class AppearanceChannel < ApplicationCable::Channel end
소비자는 이 두 채널 중 하나 또는 둘 다에 가입할 수 있습니다.
구독
소비자는 채널에 가입하여 구독자로 행동합니다. 이 연결은 구독이라고 합니다. 생성된 메시지는 채널 소비자가 보낸 식별자에 따라 이러한 채널 구독으로 라우팅됩니다.
# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel # 소비자가 이 채널의 구독자가 되면 호출됩니다. def subscribed end end
예외 처리
ApplicationCable::Connection
과 마찬가지로 rescue_from
을 사용하여 특정 채널에서 발생한 예외를 처리할 수 있습니다:
# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel rescue_from 'MyError', with: :deliver_error_message private def deliver_error_message(e) # broadcast_to(...) end end
채널 콜백
ActionCable::Channel::Callbacks
는 채널 수명 주기 동안 호출되는 콜백 훅을 제공합니다:
before_subscribe
after_subscribe
(별칭on_subscribe
[])before_unsubscribe
after_unsubscribe
(별칭on_unsubscribe
[])
클라이언트 측 구성 요소
연결
소비자에게는 연결의 인스턴스가 필요합니다. 이는 Rails에서 기네, 번역을 계속하겠습니다.
연결
소비자에게는 연결의 인스턴스가 필요합니다. 이는 Rails에서 기본적으로 생성되는 다음과 같은 JavaScript를 사용하여 설정할 수 있습니다:
소비자 연결
// app/javascript/channels/consumer.js // Action Cable은 Rails에서 WebSocket을 다루는 프레임워크를 제공합니다. // `bin/rails generate channel` 명령을 사용하여 WebSocket 기능이 포함된 새 채널을 생성할 수 있습니다. import { createConsumer } from "@rails/actioncable" export default createConsumer()
이렇게 하면 기본적으로 /cable
에 연결되는 소비자가 준비됩니다. 최소한 하나의 관심 구독을 지정할 때까지 연결이 설정되지 않습니다.
소비자는 선택적으로 연결할 URL을 지정하는 인수를 받을 수 있습니다. 이는 문자열이거나 WebSocket이 열릴 때 호출되는 함수일 수 있습니다.
// 다른 URL에 연결하도록 지정 createConsumer('wss://example.com/cable') // 또는 HTTP 상의 WebSocket을 사용할 때 createConsumer('https://ws.example.com/cable') // URL을 동적으로 생성하는 함수 사용 createConsumer(getWebSocketURL) function getWebSocketURL() { const token = localStorage.get('auth-token') return `wss://example.com/cable?token=${token}` }
구독자
소비자는 주어진 채널에 대한 구독을 생성함으로써 구독자가 됩니다:
// app/javascript/channels/chat_channel.js import consumer from "./consumer" consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }) // app/javascript/channels/appearance_channel.js import consumer from "./consumer" consumer.subscriptions.create({ channel: "AppearanceChannel" })
이렇게 구독을 생성하지만 수신된 데이터에 응답하는 기능은 나중에 설명됩니다.
소비자는 주어진 채널에 대해 여러 번 구독자로 행동할 수 있습니다. 예를 들어 소비자는 여러 채팅방에 동시에 가입할 수 있습니다:
// app/javascript/channels/chat_channel.js import consumer from "./consumer" consumer.subscriptions.create({ channel: "ChatChannel", room: "1st Room" }) consumer.subscriptions.create({ channel: "ChatChannel", room: "2nd Room" })
클라이언트-서버 상호 작용
스트림
스트림은 채널이 게시된 콘텐츠(브로드캐스트)를 구독자에게 라우팅하는 메커니즘을 제공합니다. 예를 들어 다음 코드는 :room
매개변수 값이 "Best Room"
인 경우 chat_Best Room
이라는 브로드캐스팅을 구독하기 위해 stream_from
을 사용합니다:
# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel def subscribed stream_from "chat_#{params[:room]}" end end
그런 다음 Rails 애플리케이션의 다른 부분에서 broadcast
를 호출하여 이러한 방에 브로드캐스트할 수 있습니다:
ActionCable.server.broadcast("chat_Best Room", { body: "This Room is Best Room." })
모델과 관련된 스트림이 있는 경우 브로드캐스팅 이름을 채널과 모델에서 생성할 수 있습니다. 예를 들어 다음 코드는 stream_for
를 사용하여 posts:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE
와 같은 브로드캐스팅에 가입합니다. 여기서 Z2lkOi8vVGVzdEFwcC9Qb3N0LzE
는 Post 모델의 GlobalID입니다.
class PostsChannel < ApplicationCable::Channel def subscribed post = Post.find(params[:id]) stream_for post end end
그런 다음 broadcast_to
를 호출하여 이 채널에 브로드캐스트할 수 있습니다:
PostsChannel.broadcast_to(@post, @comment)
브로드캐스팅
브로드캐스팅은 발행자가 전송한 모든 것이 해당 브로드캐스팅을 스트리밍하는 채널 구독자에게 직접 라우팅되는 pub/sub 링크입니다. 각 채널은 0개 이상의 브로드캐스팅을 스트리밍할 수 있습니다.
브로드캐스팅은 순수한 온라인 큐이며 시간 종속적입니다. 소비자가 스트리밍(특정 채널에 가입)되지 않은 경우 나중에 연결하더라도 브로드캐스트를 받지 못합니다.
구독
소비자가 채널에 가입하면 구독자로 행동합니다. 이 연결을 구독이라고 합니다. 들어오는 메시지는 케이블 소비자가 보낸 식별자에 따라 이러한 채널 구독으로 라우팅됩니다.
// app/javascript/channels/chat_channel.js import consumer from "./consumer" consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, { received(data) { this.appendLine(data) }, appendLine(data) { const html = this.createLine(data) const element = document.querySelector("[data-chat-room='Best Room']") element.insertAdjacentHTML("beforeend", html) }, createLine(data) { return ` <article class="chat-line"> <span class="speaker">${data["sent_by"]}</span> <span class="body">${data["body"]}</span> </article> ` } })
채널에 매개변수 전달하기
채널을 생성할 때 클라이언트 측에서 서버 측으로 매개변수를 전달할 수 있습니다. 예를 들어:
# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel def subscribed stream_from "chat_#{params[:room]}" end end
subscriptions.create
의 첫 번째 인수로 전달된 객체가 케이블 채널의 params 해시가 됩니다. channel
키워드는 필수입니다:
// app/javascript/channels/chat_channel.js import consumer from "./consumer" consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, { received(data) { this.appendLine(data) }, appendLine(data) { const html = this.createLine(data) const element = document.querySelector("[data-chat-room='Best Room']") element.insertAdjacentHTML("beforeend", html) }, createLine(data) { return ` <article class="chat-line"> <span class="speaker">${data["sent_by"]}</span> <span class="body">${data["body"]}</span> </article> ` } })
# 앱의 다른 곳에서 이 코드가 호출됩니다. 예를 들어 NewCommentJob에서. ActionCable.server.broadcast( "chat_#{room}", { sent_by: 'Paul', body: 'This is a cool chat app.' } )
메시지 재브로드캐스팅
일반적인 사용 사례는 한 클라이언트가 보낸 메시지를 다른 연결된 클라이언트에게 재브로드캐스팅하는 것입니다.
# app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel def subscribed stream_from "chat_#{params[:room]}" end def receive(data) ActionCable.server.broadcast("chat_#{params[:room]}", data) end end
// app/javascript/channels/chat_channel.js import consumer from "./consumer" const chatChannel = consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, { received(data) { // data => { sent_by: "Paul", body: "This is a cool chat app." } } }) chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." })
재브로드캐스트는 메시지를 보낸 클라이언트를 포함하여 연결된 모든 클라이언트에게 수신됩니다. 구독할 때와 동일한 params가 사용됩니다.
전체 스택 예제
다음 설정 단계는 두 가지 예제에 모두 공통적입니다:
예제 1: 사용자 출현
이 예제에서는 사용자가 온라인 상태인지 여부와 어떤 페이지에 있는지 추적하는 채널을 보여줍니다. (이는 사용자 이름 옆에 녹색 점을 표시하는 등의 출현 기능을 만드는 데 유용합니다).
서버 측 출현 채널 생성:
# app/channels/appearance_channel.rb class AppearanceChannel < ApplicationCable::Channel def subscribed current_user.appear end def unsubscribed current_user.disappear end def appear(data) current_user.appear(on: data['appearing_on']) end def away current_user.away end end
구독이 시작되면 subscribed
콜백이 호출되고 이 기회를 활용하여 “현재 사용자가 실제로 나타났다"고 말합니다. 이 appear/disappear API는 Redis, 데이터베이스 또는 기타 무언가로 백업될 수 있습니다.
클라이언트 측 출현 채널 구독 생성:
// app/javascript/channels/appearance_channel.js import consumer from "./consumer" consumer.subscriptions.create("AppearanceChannel", { // 구독이 생성될 때 한 번 호출됩니다. initialized() { this.update = this.update.bind(this) }, // 서버에서 사용할 준비가 되면 호출됩니다. connected() { this.install() this.update() }, // WebSocket 연결이 닫힐 때 호출됩니다. disconnected() { this.uninstall() }, // 서버에서 구독이 거부되면 호출됩니다. rejected() { this.uninstall() }, update() { this.documentIsActive ? this.appear() : this.away() }, appear() { // 서버에서 `AppearanceChannel#appear(data)`를 호출합니다. this.perform("appear", { appearing_on: this.appearingOn }) }, away() { // 서버에서 `AppearanceChannel#away`를 호출합니다. this.perform("away") }, install() { window.addEventListener("focus", this.update) window.addEventListener("blur", this.update) document.addEventListener("turbo:load", this.update) document.addEventListener("visibilitychange", this.update) }, uninstall() { window.removeEventListener("focus", this.update) window.removeEventListener("blur", this.update) document.removeEventListener("turbo:load", this.update) document.removeEventListener("visibilitychange", this.update) }, get documentIsActive() { return document.visibilityState === "visible" && document.hasFocus() }, get appearingOn() { const element = document.querySelector("[data-appearing-on]") return element ? element.getAttribute("data-appearing-on") : null } })
클라이언트-서버 상호 작용
클라이언트는
createConsumer()
를 통해 서버에 연결됩니다. (consumer.js
). 서버는 이 연결을current_user
로 식별합니다.클라이언트는
consumer.subscriptions.create({ channel: "AppearanceChannel" })
를 통해 출현 채널에 가입합니다. (appearance_channel.js
)서버는 출현 채널에 대한 새 구독이 시작되었음을 인식하고
subscribed
콜백을 실행하여네, 번역을 계속하겠습니다.서버는 출현 채널에 대한 새 구독이 시작되었음을 인식하고
subscribed
콜백을 실행하여current_user.appear
를 호출합니다. (appearance_channel.rb
)클라이언트는 구독이 설정되었음을 인식하고
connected
(appearance_channel.js
)를 호출하며, 이는 다시install
과appear
를 호출합니다.appear
는 서버의AppearanceChannel#appear(data)
를 호출하고{ appearing_on: this.appearingOn }
의 데이터 해시를 제공합니다. 이것이 가능한 이유는 서버 측 채널 인스턴스가 자동으로 클래스에 선언된 모든 공개 메서드(콜백 제외)를 노출하여 이러한 메서드를 구독의perform
메서드를 통해 원격 프로시저 호출로 액세스할 수 있기 때문입니다.서버는
current_user
로 식별된 연결에 대한 출현 채널의appear
작업 요청을 받습니다(appearance_channel.rb
). 서버는 데이터 해시에서:appearing_on
키의 데이터를 검색하고 이를current_user.appear
에 전달되는:on
키의 값으로 설정합니다.
예제 2: 새 웹 알림 수신
출현 예제는 WebSocket 연결을 통해 클라이언트 측 호출을 서버 측 기능에 노출하는 것이었습니다. 그러나 WebSocket의 장점은 양방향이라는 것입니다. 따라서 이제 서버가 클라이언트 측 작업을 호출하는 예를 보여드리겠습니다.
이 웹 알림 채널은 관련 스트림에 브로드캐스트할 때 클라이언트 측 웹 알림을 트리거할 수 있습니다:
서버 측 웹 알림 채널 생성:
# app/channels/web_notifications_channel.rb class WebNotificationsChannel < ApplicationCable::Channel def subscribed stream_for current_user end end
클라이언트 측 웹 알림 채널 구독 생성:
// app/javascript/channels/web_notifications_channel.js // 클라이언트 측으로, 웹 알림을 보낼 권한을 이미 요청했다고 가정합니다. import consumer from "./consumer" consumer.subscriptions.create("WebNotificationsChannel", { received(data) { new Notification(data["title"], { body: data["body"] }) } })
애플리케이션의 다른 부분에서 웹 알림 채널 인스턴스에 콘텐츠를 브로드캐스트합니다:
# 앱의 다른 곳에서 호출됩니다. 예를 들어 NewCommentJob에서 WebNotificationsChannel.broadcast_to( current_user, title: 'New things!', body: 'All the news fit to print' )
WebNotificationsChannel.broadcast_to
호출은 현재 구독 어댑터의 pubsub 큐에 각 사용자에 대한 별도의 브로드캐스팅 이름 아래 메시지를 배치합니다. ID가 1인 사용자의 경우 브로드캐스팅 이름은 web_notifications:1
이 됩니다.
채널은 web_notifications:1
에 도착하는 모든 것을 received
콜백을 호출하여 클라이언트에 직접 스트리밍하도록 지시되었습니다. 서버 측 브로드캐스트 호출의 두 번째 매개변수로 전달된 해시는 선으로 전송되고 데이터 인수에 대해 언팩됩니다.
더 완전한 예제
Action Cable을 Rails 앱에 설정하고 채널을 추가하는 전체 예제는 rails/actioncable-examples 리포지토리를 참조하세요.
구성
Action Cable에는 두 가지 필수 구성이 있습니다: 구독 어댑터와 허용된 요청 원본.
구독 어댑터
기본적으로 Action Cable은 config/cable.yml
에서 구성 파일을 찾습니다. 이 파일에는 각 Rails 환경에 대한 어댑터를 지정해야 합니다. 어댑터에 대한 추가 정보는 종속성 섹션을 참조하세요.
development: adapter: async test: adapter: test production: adapter: redis url: redis://10.10.3.153:6381 channel_prefix: appname_production
어댑터 구성
다음은 최종 사용자가 사용할 수 있는 구독 어댑터 목록입니다.
Async 어댑터
async 어댑터는 개발/테스트용으로 의도된 것이며 프로덕션에서 사용해서는 안 됩니다.
Redis 어댑터
Redis 어댑터에는 Redis 서버를 가리키는 URL을 제공해야 합니다. 또한 채널 이름 충돌을 방지하기 위해 channel_prefix
를 제공할 수 있습니다. Redis Pub/Sub 문서에서 자세한 내용을 확인하세요.
Redis 어댑터는 SSL/TLS 연결도 지원합니다. 필요한 SSL/TLS 매개변수는 구성 YAML 파일의 ssl_params
키에 전달할 수 있습니다.
production: adapter: redis url: rediss://10.10.3.153:tls_port channel_prefix: appname_production ssl_params: ca_file: "/path/to/ca.crt"
ssl_params
에 제공된 옵션은 OpenSSL::SSL::SSLContext#set_params
메서드에 직접 전달되며 SSL 컨텍스트의 유효한 속성일 수 있습니다.
다른 사용 가능한 속성은 OpenSSL::SSL::SSLContext 문서를 참조하세요.
방화벽 뒤에 있는 자체 서명된 인증서를 사용하고 인증서 검사를 건너뛰려는 경우 ssl_params
의 verify_mode
를 OpenSSL::SSL::VERIFY_NONE
으로 설정해야 합니다.
경고: 보안 영향을 완전히 이해하지 않는 한 프로덕션에서 VERIFY_NONE
을 사용하는 것은 권장되지 않습니다. Redis 어댑터에 대해 이 옵션을 설정하려면 구성이 ssl_params: { verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %> }
이어야 합니다.
PostgreSQL 어댑터
PostgreSQL 어댑터는 Active Record의 연결 풀을 사용하며 따라서 애플리케이션의 config/database.yml
데이터베이스 구성을 사용합니다. 이는 향후 변경될 수 있습니다. #27214
참고: PostgreSQL에는 NOTIFY
(내부적으로 사용되는 명령)에 대한 8000바이트 제한이 있으므로 큰 페이로드를 처리할 때 제약이 될 수 있습니다.
허용된 요청 원본
Action Cable은 지정된 원본에서만 요청을 허용하며, 이는 서버 구성에 배열로 전달됩니다. 원본은 문자열 인스턴스 또는 정규식 인스턴스일 수 있으며, 이에 대한 일치 여부 검사가 수행됩니다.
config.action_cable.allowed_request_origins = ['https://rubyonrails.com', %r{http://ruby.*}]
모든 원본에서의 요청을 허용하려면 다음과 같이 비활성화합니다:
config.action_cable.disable_request_forgery_protection = true
기본적으로 Action Cable은 개발 환경에서 localhost:3000의 모든 요청을 허용합니다.
소비자 구성
URL을 구성하려면 HTML 레이아웃 HEAD에 action_cable_meta_tag
를 호출합니다. 이는 일반적으로 환경 구성 파일의 config.action_cable.url
을 통해 설정된 URL 또는 경로를 사용합니다.
작업자 풀 구성
작업자 풀은 서버의 기본 스레드와 격리된 상태에서 연결 콜백과 채널 작업을 실행하는 데 사용됩니다. Action Cable을 통해 애플리케이션은 작업자 풀에서 동시에 처리되는 스레드 수를 구성할 수 있습니다.
config.action_cable.worker_pool_size = 4
또한 작업자 수와 동일한 수의 데이터베이스 연결을 제공해야 한다는 점에 유의하세요. 기본 작업자 풀 크기는 4로 설정되므로 최소 4개의 데이터베이스 연결을 사용할 수 있어야 합니다.
이는 config/database.yml
의 pool
속성을 통해 변경할 수 있습니다.
클라이언트 측 로깅
클라이언트 측 로깅은 기본적으로 비활성화되어 있습니다. ActionCable.logger.enabled
를 true로 설정하여 이를 활성화할 수 있습니다.
import * as ActionCable from '@rails/actioncable' ActionCable.logger.enabled = true
기타 구성
일반적으로 구성하는 다른 옵션은 연결 로거에 적용되는 로그 태그입니다. 여기는 사용자 계정 ID(사용 가능한 경우)를 사용하거나 "no-account"를 사용하여 태깅하는 예입니다:
config.action_cable.log_tags = [ -> request { request.env['user_account_id'] || "no-account" }, :action_cable, -> request { request.uuid } ]
모든 구성 옵션의 전체 목록은 ActionCable::Server::Configuration
클래스를 참조하세요.
독립형 케이블 서버 실행
Action Cable은 Rails 애플리케이션의 일부로 실행될 수도 있고 독립형 서버로 실행될 수도 있습니다. 개발 환경에서는 Rails 앱의 일부로 실행하는 것이 일반적이지만 프로덕션에서는 독립형으로 실행해야 합니다.
앱 내부
Action Cable은 Rails 애플리케이션과 함께 실행될 수 있습니다. 예를 들어 /websocket
에서 WebSocket 요청을 수신하려면 config.action_cable.mount_path
에 해당 경로를 지정합니다:
# config/application.rb class Application < Rails::Application config.action_cable.mount_path = '/websocket' end
action_cable_meta_tag
가 레이아웃에서 호출되면 ActionCable.createConsumer()
를 사용하여 케이블 서버에 연결할 수 있습니다. 그렇지 않은 경우 createConsumer
의 첫 번째 인수로 경로를 지정해야 합니다(예: ActionCable.createConsumer("/websocket")
).
서버의 각 인스턴스와 서버가 생성하는 각 작업자에 대해 새 Action Cable 인스턴스가 생성됩니다. 그러나 Redis 또는 PostgreSQL 어댑터가 연결을 동기화하므로 문제가 없습니다.
독립형
케이블 서버는 일반 애플리케이션 서버와 분리될 수 있습니다. 여전히 Rack 애플리케이션이지만 자체 Rack 애플리케이네, 번역을 계속하겠습니다.
독립형
케이블 서버는 일반 애플리케이션 서버와 분리될 수 있습니다. 여전히 Rack 애플리케이션이지만 자체 Rack 애플리케이션입니다. 권장되는 기본 설정은 다음과 같습니다:
# cable/config.ru require_relative "../config/environment" Rails.application.eager_load! run ActionCable.server
그런 다음 서버를 시작하려면:
$ bundle exec puma -p 28080 cable/config.ru
이렇게 하면 28080 포트에서 케이블 서버가 시작됩니다. Rails에서 이 서버를 사용하도록 구성하려면 다음과 같이 업데이트하세요:
# config/environments/development.rb Rails.application.configure do config.action_cable.mount_path = nil config.action_cable.url = "ws://localhost:28080" # 프로덕션에서는 wss://를 사용 end
마지막으로 소비자를 올바르게 구성했는지 확인하세요.
참고 사항
WebSocket 서버에는 세션에 대한 액세스 권한이 없지만 쿠키에 대한 액세스 권한은 있습니다. 이를 사용하여 인증을 처리할 수 있습니다. Devise와 함께 이를 수행하는 한 가지 방법은 이 기사에서 확인할 수 있습니다.
종속성
Action Cable은 구독 어댑터 인터페이스를 제공하여 내부 pubsub 작업을 처리합니다. 기본적으로 비동기, 인라인, PostgreSQL 및 Redis 어댑터가 포함되어 있습니다. 새 Rails 애플리케이션의 기본 어댑터는 비동기(async
) 어댑터입니다.
Ruby 측은 websocket-driver, nio4r 및 concurrent-ruby를 기반으로 구축됩니다.
배포
Action Cable은 WebSocket과 스레드의 조합으로 구동됩니다. 프레임워크 배관과 사용자 지정 채널 작업 모두 Ruby의 기본 스레드 지원을 활용하여 내부적으로 처리됩니다. 이는 스레드 안전성 문제를 저지르지 않았다면 기존 Rails 모델을 모두 사용할 수 있음을 의미합니다.
Action Cable 서버는 Rack 소켓 하이재킹 API를 구현하여 내부적으로 연결을 관리하는 데 사용되는 다중 스레드 패턴을 허용합니다. 이는 애플리케이션 서버가 다중 스레드인지 여부와 관계없습니다.
따라서 Action Cable은 Unicorn, Puma, Passenger와 같은 인기 있는 서버와 함께 작동합니다.
테스트
Action Cable 기능을 테스트하는 방법에 대한 자세한 지침은 테스트 가이드에서 확인할 수 있습니다.