소개
Rails 앱을 생동감 있게 만들고 싶다면 Action Cable를 확인해보세요. 이것은 Rails에 내장된 WebSockets 방식으로, 서버와 브라우저가 실시간으로 연결되어 대화할 수 있게 해줍니다. 페이지 새로 고침도 필요 없고, 번거로운 폴링도 없습니다 — 업데이트가 즉시 나타납니다.
이 가이드에서는 재미있는 데모인 실시간 트래픽 카운터를 만들어보겠습니다. 누군가 사이트에 접속하면, 그 방문을 데이터베이스에 기록하고, 메모리에서 카운터를 증가시키며, 업데이트된 숫자를 온라인에 있는 모든 사람에게 방송합니다. 탭을 닫으면? 온라인 사용자 수가 즉시 감소합니다. 간단하지만, Action Cable이 Rails 7에 가져다주는 실시간 마법을 보여줍니다.
1단계: 방문 모델 만들기
먼저, 방문을 기록할 모델을 만들어봅시다.
bin/rails g model Visit session_id:string ip:string user_agent:string
bin/rails db:migrate
# app/models/visit.rb
class Visit < ApplicationRecord
end
2단계: 사용자 정의 연결 만들기
Rails는 이미 app/channels/application_cable/에 두 개의 기본 클래스를 제공합니다:
- channel.rb → 모든 채널의 기본 클래스.
- connection.rb → 모든 WebSocket 연결의 기본 클래스.
보통 우리는 이들을 수정하지 않습니다. 대신, app/channels/ 안에 우리만의 연결 클래스를 만들어 이들을 확장합니다. 이렇게 하면 기본값을 깔끔하게 유지하고 우리의 로직을 명확한 위치에 두게 됩니다.
# app/channels/traffic_connection.rb
module ApplicationCable
class TrafficConnection < Connection
identified_by :session_id, :ip, :user_agent
def connect
self.session_id = request.session.id
self.ip = request.ip
self.user_agent = request.user_agent
end
end
end
3단계: 메모리 내 카운터
우리는 빠른 처리를 위해 원자 카운터를 사용할 것입니다. 시작할 때 DB에서 총 방문 수를 가져오고, 온라인 사용자 수를 0으로 설정합니다.
# config/initializers/traffic_counter.rb
$online_users = Concurrent::AtomicFixnum.new(0)
$total_visits = Concurrent::AtomicFixnum.new(Visit.count)
4단계: 채널
여기서 재미있는 일이 벌어집니다 — 방문을 기록하고, 카운터를 업데이트하고, 방송합니다.
# app/channels/traffic_channel.rb
class TrafficChannel < ApplicationCable::Channel
def subscribed
# 방문 기록
Visit.create!(session_id: connection.session_id,
ip: connection.ip,
user_agent: connection.user_agent)
# 카운터 업데이트
$online_users.increment
$total_visits.increment
stream_from "traffic_channel"
broadcast_counts
end
def unsubscribed
$online_users.decrement
broadcast_counts
end
private
def broadcast_counts
ActionCable.server.broadcast("traffic_channel", {
online: $online_users.value,
total: $total_visits.value
})
end
end
5단계: JavaScript 측
이제 bin/rails generate channel Traffic를 실행하면, Rails가 두 개의 파일을 생성합니다: Ruby 채널과 JavaScript 구독 파일. 기본적으로 JS 파일은 consumer.js를 가져오며, 이는 앱 생성 시 Rails가 설정해준 것입니다.
// app/javascript/channels/traffic_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("TrafficChannel", {
received(data) {
document.querySelector("#online-count").textContent = data.online
document.querySelector("#total-count").textContent = data.total
}
})
consumer.js 파일은 새 앱을 만들 때 Rails가 생성합니다. 이 파일은 /cable에 대한 전역 WebSocket 연결을 설정하며, 다음과 같은 형태입니다:
// app/javascript/channels/consumer.js
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
6단계: 뷰
<h1>🚦 실시간 트래픽</h1>
<p>현재 온라인: <span id="online-count">0</span></p>
<p>총 방문 수: <span id="total-count"><%= Visit.count %></span></p>
7단계: 라우트
# config/routes.rb
Rails.application.routes.draw do
root "home#index"
mount ActionCable.server => "/cable"
end
8단계: 실행해보기
앱을 bin/dev로 실행하세요. 여러 탭에서 사이트를 열어보세요. 각 탭이 열릴 때마다 온라인 사용자 수가 증가하고, 탭을 닫으면 감소합니다. 총 방문 수는 계속 증가합니다. 보는 것만으로도 정말 만족스럽습니다!
운영 환경을 위한 주의사항
- 여러 Puma 워커를 사용하나요? 카운터를 위해 Redis를 사용하세요 — 메모리는 프로세스 간에 동기화되지 않습니다.
- 세션 대신 실제 사용자를 추적하고 싶나요? 연결에 current_user를 추가하세요.
- 방문을 DB에 저장하면 나중에 일일 트래픽이나 브라우저 분석과 같은 분석을 실행할 수 있습니다.
이로써 Action Cable을 사용한 Rails 7의 실시간 트래픽 카운터가 완성되었습니다. 100줄도 안 되는 코드로 즉각적인 업데이트와 WebSockets의 매력을 경험할 수 있습니다. 이 설정은 알림, 대시보드 및 다양한 실시간 기능에도 적용할 수 있습니다. 한 번 사용해보면 어디에나 적용하고 싶어질 것입니다.