Action CableとWebSocket完全ガイド:2026年Ruby on Rails技術面接対策
Ruby on RailsのAction CableとWebSocketの深掘り解説。コネクション、チャンネル、ブロードキャスト、Rails 8のSolid Cable、Redisによるスケーリング、テスト手法まで面接で問われるポイントをコード例とともに網羅します。

Action Cableは、WebSocketサポートをRailsフレームワークに直接統合する仕組みであり、チャット、通知、ライブダッシュボードなどのリアルタイム機能を外部依存なしに実現できます。2026年の技術面接では、Action Cableの内部構造――コネクションのライフサイクルから本番環境でのスケーリングまで――を深く理解しているかどうかが、候補者の実力を見極める重要な判断基準となっています。
Action Cableは、コントローラやモデルと同じRailsの規約に従ってWebSocketを統合します。Rails 8で導入されたSolid Cableは、データベースベースのアダプターにより、pub/subメッセージングにおけるRedis依存を排除します。
WebSocketプロトコルの基礎とRailsにおける位置づけ
WebSocketプロトコル(RFC 6455)は、単一のTCP接続上で永続的な全二重通信チャンネルを確立します。HTTPのリクエスト・レスポンスサイクルとは異なり、WebSocketは接続を維持し続け、クライアントとサーバーの双方がいつでもメッセージを送信できます。
Action Cableは、このプロトコルをRailsに馴染みやすい抽象化で包み込みます。サーバーは/cableでWebSocketのアップグレードを処理し、コネクション認証を管理し、チャンネルを通じてメッセージをルーティングします。クライアントサイドのJavaScriptライブラリが、サブスクリプションの作成と管理を自動的に行います。
一般的なHTTPリクエストはミリ秒単位で完了して閉じられますが、WebSocket接続は数分、数時間、あるいはセッション全体にわたって維持されます。この根本的な違いが、Action Cableにおけるすべてのアーキテクチャ上の判断を左右します。
Action Cableアーキテクチャ:コネクション、チャンネル、サブスクリプション
Action Cableは、3つのコア抽象化によるレイヤードアーキテクチャに従います。コネクションは認証を処理し、チャンネルはビジネスロジックをカプセル化し、サブスクリプションはコンシューマーと特定のチャンネルを結びつけます。
# 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
# Cookies are available in WebSocket handshake
if verified_user = User.find_by(id: cookies.encrypted[:user_id])
verified_user
else
reject_unauthorized_connection
end
end
end
endコネクションクラスは、WebSocketハンドシェイクごとに1回実行されます。認証はここで行われ、個々のチャンネルでは行いません。identified_by宣言はユーザーIDを登録し、その接続上のすべてのチャンネルサブスクリプションで利用可能にします。
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
room = ChatRoom.find(params[:room_id])
# Authorization: verify user belongs to this room
if room.members.include?(current_user)
stream_for room
else
reject
end
end
def receive(data)
message = ChatMessage.create!(
room_id: params[:room_id],
user: current_user,
body: data["body"]
)
ChatChannel.broadcast_to(
message.room,
{ id: message.id, body: message.body, user: current_user.name }
)
end
def unsubscribed
# Cleanup: mark user as offline
AppearanceTracker.mark_offline(current_user)
end
endチャンネルは3つのライフサイクルコールバックを定義します:subscribed、receive、unsubscribed。stream_forメソッドは、サブスクリプションを特定のモデルインスタンスにバインドし、名前空間付きのストリームを作成します。そのストリームへのブロードキャストは、接続しているすべてのサブスクライバーにメッセージを配信します。
クライアントサイドのJavaScriptサブスクリプション
クライアントサイドのコンシューマーは、Action Cableサーバーに接続し、サブスクリプションを管理します。各サブスクリプションは、サーバーサイドのチャンネルに対応します。
import consumer from "./consumer"
const chatChannel = consumer.subscriptions.create(
{ channel: "ChatChannel", room_id: roomId },
{
connected() {
// Called when the subscription is ready
console.log("Connected to chat room", roomId)
},
disconnected() {
// Called when the subscription is closed
console.log("Disconnected from chat room")
},
received(data) {
// Called when data is broadcast to the channel
const messageList = document.getElementById("messages")
messageList.insertAdjacentHTML("beforeend",
`<div class="message"><strong>${data.user}</strong>: ${data.body}</div>`
)
},
sendMessage(body) {
// Calls ChatChannel#receive on the server
this.perform("receive", { body: body })
}
}
)receivedコールバックは、サーバーがサブスクライブ中のストリームにブロードキャストするたびに発火します。performメソッドは、クライアントからサーバーへデータを送信し、対応するチャンネルメソッドを呼び出します。
Ruby on Railsの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
Rails 8のSolid Cable:データベースベースのPub/Sub
Rails 8では、Solid QueueやSolid Cacheと並ぶ「Solid三部作」の一つとして、Solid Cableが導入されました。Solid Cableは、メッセージをデータベーステーブルに保存することで、pub/subバックエンドとしてのRedisを置き換えます。
# config/cable.yml (Rails 8 default)
development:
adapter: solid_cable
production:
adapter: solid_cable
connects_to:
database:
writing: cable
polling_interval: 0.1.seconds
message_retention: 1.daySolid Cableは、各ブロードキャストメッセージをデータベーステーブルに書き込み、設定可能な間隔で新しいメッセージをポーリングする仕組みで動作します。デフォルトのポーリング間隔100msは、ほとんどのアプリケーションでほぼリアルタイムの配信を実現します。
トレードオフは明確です。Solid Cableはインフラストラクチャ依存(Redis)を排除する代わりに、若干高いレイテンシーとデータベース負荷が発生します。すでにPostgreSQLやMySQLを運用しているアプリケーションでは、このトレードオフが適切な選択となることが多いです。1秒あたり数千メッセージの高頻度ブロードキャストでは、依然としてRedisの方が適しています。
# config/database.yml (Rails 8 multi-database)
production:
primary:
<<: *default
database: myapp_production
cable:
<<: *default
database: myapp_cable_production
migrations_paths: db/cable_migrateSolid Cableは専用データベースを使用して、プライマリデータベースとの競合を回避します。この分離により、メッセージポーリングがアプリケーションクエリに干渉することを防ぎます。
ブロードキャストパターンとサーバーサイドトリガー
モデル、ジョブ、コントローラからのブロードキャストは、本番Railsアプリケーションで最も一般的な3つのパターンをカバーします。
# Broadcasting from a model callback
class Notification < ApplicationRecord
belongs_to :user
after_create_commit :broadcast_to_user
private
def broadcast_to_user
ActionCable.server.broadcast(
"notifications_#{user_id}",
{ id: id, title: title, read: false }
)
end
end
# Broadcasting from a background job
class DashboardUpdateJob < ApplicationJob
queue_as :default
def perform(dashboard_id)
dashboard = Dashboard.find(dashboard_id)
stats = dashboard.compute_stats
ActionCable.server.broadcast(
"dashboard_#{dashboard_id}",
{ stats: stats, updated_at: Time.current.iso8601 }
)
end
endモデルコールバックは、レコード変更によってトリガーされるシンプルな通知に適しています。バックグラウンドジョブは、ダッシュボード統計の計算やデータ集約など、より重い処理をリクエストサイクルをブロックせずに処理します。ブロードキャスト自体は常に軽量で、ペイロードをシリアライズしてアダプターにパブリッシュするだけです。
Turbo StreamsとAction Cableの統合
Rails 8のHotwireは、Action CableをTurbo Streamsのトランスポートレイヤーとして使用し、カスタムJavaScriptを書くことなくリアルタイムのDOM更新を実現します。
# app/models/message.rb
class Message < ApplicationRecord
belongs_to :chat_room
# Automatically broadcasts append to subscribers
broadcasts_to :chat_room
end
# app/views/chat_rooms/show.html.erb
<%= turbo_stream_from @chat_room %>
<div id="messages">
<%= render @chat_room.messages %>
</div>broadcasts_toマクロは、after_create、after_update、after_destroyのコールバックを生成し、Turbo StreamフラグメントをAction Cable経由でブロードキャストします。クライアント側のturbo_stream_fromヘルパーは、対応するストリームをサブスクライブします。新しいメッセージを作成すると、レンダリングされたパーシャルが接続中のすべてのクライアントのDOMに自動的に追加されます。
このパターンにより、リアルタイム機能はモデル宣言とビューヘルパーだけに集約されます。カスタムチャンネルもJavaScriptハンドラーも手動のDOM操作も不要です。
本番環境でのAction Cableスケーリング
本番デプロイメントでは、アダプターの選択、接続数の上限、水平スケーリングについて慎重に検討する必要があります。
# config/cable.yml — Redis adapter for high-scale production
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: myapp_productionRedisはpub/subの基盤として機能し、あるRailsプロセスでパブリッシュされたメッセージが、他の任意のプロセスに接続しているサブスクライバーに確実に届くようにします。Redis(またはSolid Cable)がない場合、asyncアダプターはブロードキャストを単一プロセスに限定するため、マルチプロセスやマルチサーバーのデプロイメントでは使用できません。
接続上限はサーバーに依存します。PumaとAction Cableは、HTTPリクエストと同じプロセスでWebSocket接続を処理します。各WebSocketは永続的な接続を保持し、スレッド(またはRuby 3.xのファイバーベーススケジューラーではファイバー)を消費します。5スレッド・4ワーカーの一般的なPuma構成では、飽和状態になるまでに約200の同時WebSocket接続を処理できます。
数千の同時接続を必要とするアプリケーションでは、AnyCableがRubyのWebSocketサーバーをGoベースのサーバーに置き換え、プロトコルレベルで接続を処理しながら、gRPCを通じてチャンネルロジックをRailsにルーティングします。このアーキテクチャは、ノードあたり10,000以上の同時接続をサポートします。
面接官はAction Cableのスケーリング限界について頻繁に質問します。重要な回答のポイント:Action Cable自体がボトルネックではなく、WebSocket接続を処理するRubyプロセスがボトルネックになります。AnyCableは、接続管理をGoにオフロードしながらチャンネルロジックをRubyに維持することで、この問題を解決します。
Action Cableチャンネルのテスト
Railsは、チャンネルのユニットテスト用にActionCable::Channel::TestCaseを、コネクション認証のテスト用にActionCable::Connection::TestCaseを提供しています。
# test/channels/chat_channel_test.rb
require "test_helper"
class ChatChannelTest < ActionCable::Channel::TestCase
test "subscribes to a valid room" do
room = chat_rooms(:general)
user = users(:alice)
stub_connection current_user: user
subscribe room_id: room.id
assert subscription.confirmed?
assert_has_stream_for room
end
test "rejects subscription for unauthorized user" do
room = chat_rooms(:private)
user = users(:outsider)
stub_connection current_user: user
subscribe room_id: room.id
assert subscription.rejected?
end
test "broadcasts messages to room subscribers" do
room = chat_rooms(:general)
stub_connection current_user: users(:alice)
subscribe room_id: room.id
assert_broadcast_on(room, hash_including(body: "Hello")) do
perform :receive, body: "Hello"
end
end
endstub_connectionメソッドは、実際のWebSocketを使わずにコネクションのコンテキストを設定します。assert_has_stream_forはストリームバインディングを検証します。assert_broadcast_onはブロック内のブロードキャストをキャプチャし、正しいペイロードが正しいストリームに到達することを確認します。
Action Cableに関する頻出面接質問
Action Cableに関する技術面接では、プロトコルの理解、アーキテクチャの判断、本番環境での懸念事項が典型的に問われます。
Action Cableはどのようにコネクションを認証するか? 認証は、WebSocketハンドシェイク中にApplicationCable::Connection#connectで行われます。HTTPセッションのCookieはこの時点で利用可能です。トークンベースの認証では、WebSocket URLのクエリパラメータとしてトークンを渡し、connectで検証します。
WebSocket接続が切断された場合はどうなるか? クライアントライブラリは、指数バックオフ付きの自動再接続を実装しています。サーバー側では、その接続がサブスクライブしていた各チャンネルに対してunsubscribedが発火します。ステートフルなクリーンアップ(ユーザーのオフラインマーク、ロックの解放)はunsubscribedで行います。
Solid CableとRedisのどちらを使うべきか? Solid Cableは、中程度のリアルタイムニーズ(毎秒100メッセージ以下)を持ち、Redisインフラを避けたいアプリケーションに適しています。高スループットシナリオや10ms未満の配信レイテンシーが求められる場合には、依然としてRedisが必要です。
個々のチャンネルではなく、コネクションクラスに認可ロジックを配置してしまうケースがあります。コネクションは認証(このユーザーは誰か?)を担当し、チャンネルは認可(このユーザーはこのルームにアクセスできるか?)を担当します。これらの責務を混同すると、セキュリティ上の隙が生まれます。
まとめ
- Action Cableは、コネクション・チャンネル・サブスクリプションをコア抽象化として、WebSocketプロトコルをRailsの規約に統合する
- 認証は
ApplicationCable::Connectionで行い、認可は個々のチャンネルのsubscribedコールバックで行う - Rails 8のSolid Cableは、データベースをpub/subに使用してRedis依存を排除する。中程度のスループットのアプリケーションに適している
- Turbo Streamsは、Action Cableを活用して最小限のコードで自動的なリアルタイムDOM更新を実現する
- 本番環境のスケーリングには、マルチプロセスデプロイメントにおいてRedisまたはSolid Cableが必要。AnyCableは、WebSocket管理をGoにオフロードすることで10,000以上の接続を処理する
- チャンネルのテストには
stub_connection、assert_has_stream_for、assert_broadcast_onを使用する。実際のWebSocket接続は不要 - これらの概念を定着させるために、Action Cable & WebSocketの面接問題で練習することが効果的です
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
タグ
共有
関連記事

ActiveRecord:Ruby on RailsにおけるN+1クエリ問題の解消
ActiveRecordを用いてRailsのN+1クエリを検出・解決する完全ガイドです。includes、preload、eager_load、そして自動検出ツールを習得します。

Ruby on Rails 面接質問: 2026年トップ25
Ruby on Railsで最も多く問われる面接質問25選。MVCアーキテクチャ、Active Record、マイグレーション、RSpecテスト、REST APIを詳細な解説とコード例とともに解説。

Ruby on Rails 7: HotwireとTurboによるリアクティブアプリケーション
Rails 7におけるHotwireとTurboの完全ガイド。Turbo Drive、Frames、Streamsを使用してJavaScriptを書かずにリアクティブアプリケーションを構築する方法。