# Action CableとWebSocket完全ガイド:2026年Ruby on Rails技術面接対策 > Ruby on RailsのAction CableとWebSocketの深掘り解説。コネクション、チャンネル、ブロードキャスト、Rails 8のSolid Cable、Redisによるスケーリング、テスト手法まで面接で問われるポイントをコード例とともに網羅します。 - Published: 2026-05-07 - Updated: 2026-05-07 - Author: Anthony Fillion-Maillet - Tags: ruby on rails, action cable, websockets, real-time, solid cable, rails 8 - Reading time: 12 min --- 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つのコア抽象化によるレイヤードアーキテクチャに従います。コネクションは認証を処理し、チャンネルはビジネスロジックをカプセル化し、サブスクリプションはコンシューマーと特定のチャンネルを結びつけます。 ```ruby # 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を登録し、その接続上のすべてのチャンネルサブスクリプションで利用可能にします。 ```ruby # 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サーバーに接続し、サブスクリプションを管理します。各サブスクリプションは、サーバーサイドのチャンネルに対応します。 ```javascript // app/javascript/channels/chat_channel.js 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", `
${data.user}: ${data.body}
` ) }, sendMessage(body) { // Calls ChatChannel#receive on the server this.perform("receive", { body: body }) } } ) ``` `received`コールバックは、サーバーがサブスクライブ中のストリームにブロードキャストするたびに発火します。`perform`メソッドは、クライアントからサーバーへデータを送信し、対応するチャンネルメソッドを呼び出します。 ## Rails 8のSolid Cable:データベースベースのPub/Sub Rails 8では、Solid QueueやSolid Cacheと並ぶ「Solid三部作」の一つとして、Solid Cableが導入されました。Solid Cableは、メッセージをデータベーステーブルに保存することで、pub/subバックエンドとしてのRedisを置き換えます。 ```yaml # 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.day ``` Solid Cableは、各ブロードキャストメッセージをデータベーステーブルに書き込み、設定可能な間隔で新しいメッセージをポーリングする仕組みで動作します。デフォルトのポーリング間隔100msは、ほとんどのアプリケーションでほぼリアルタイムの配信を実現します。 トレードオフは明確です。Solid Cableはインフラストラクチャ依存(Redis)を排除する代わりに、若干高いレイテンシーとデータベース負荷が発生します。すでにPostgreSQLやMySQLを運用しているアプリケーションでは、このトレードオフが適切な選択となることが多いです。1秒あたり数千メッセージの高頻度ブロードキャストでは、依然としてRedisの方が適しています。 ```ruby # config/database.yml (Rails 8 multi-database) production: primary: <<: *default database: myapp_production cable: <<: *default database: myapp_cable_production migrations_paths: db/cable_migrate ``` Solid Cableは専用データベースを使用して、プライマリデータベースとの競合を回避します。この分離により、メッセージポーリングがアプリケーションクエリに干渉することを防ぎます。 ## ブロードキャストパターンとサーバーサイドトリガー モデル、ジョブ、コントローラからのブロードキャストは、本番Railsアプリケーションで最も一般的な3つのパターンをカバーします。 ```ruby # 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更新を実現します。 ```ruby # 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 %>
<%= render @chat_room.messages %>
``` `broadcasts_to`マクロは、after_create、after_update、after_destroyのコールバックを生成し、Turbo StreamフラグメントをAction Cable経由でブロードキャストします。クライアント側の`turbo_stream_from`ヘルパーは、対応するストリームをサブスクライブします。新しいメッセージを作成すると、レンダリングされたパーシャルが接続中のすべてのクライアントのDOMに自動的に追加されます。 このパターンにより、リアルタイム機能はモデル宣言とビューヘルパーだけに集約されます。カスタムチャンネルもJavaScriptハンドラーも手動のDOM操作も不要です。 ## 本番環境でのAction Cableスケーリング 本番デプロイメントでは、アダプターの選択、接続数の上限、水平スケーリングについて慎重に検討する必要があります。 ```ruby # config/cable.yml — Redis adapter for high-scale production production: adapter: redis url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> channel_prefix: myapp_production ``` Redisはpub/subの基盤として機能し、あるRailsプロセスでパブリッシュされたメッセージが、他の任意のプロセスに接続しているサブスクライバーに確実に届くようにします。Redis(またはSolid Cable)がない場合、asyncアダプターはブロードキャストを単一プロセスに限定するため、マルチプロセスやマルチサーバーのデプロイメントでは使用できません。 接続上限はサーバーに依存します。PumaとAction Cableは、HTTPリクエストと同じプロセスでWebSocket接続を処理します。各WebSocketは永続的な接続を保持し、スレッド(またはRuby 3.xのファイバーベーススケジューラーではファイバー)を消費します。5スレッド・4ワーカーの一般的なPuma構成では、飽和状態になるまでに約200の同時WebSocket接続を処理できます。 数千の同時接続を必要とするアプリケーションでは、[AnyCable](https://anycable.io/)が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`を提供しています。 ```ruby # 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 end ``` `stub_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の面接問題で練習する](/ja/technologies/ruby-on-rails/interview-questions/action-cable-websockets)ことが効果的です --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/ruby-on-rails/action-cable-websockets-complete-guide