Rails Action Cable 기반 실시간 Presence 기능 구현: 개발 노트와 고찰

Notes from building a ?who is doing what right now on our website?? presence feature with Action Cable | Island94.org

작성자
발행일
2025년 10월 06일

핵심 요약

  • 1 Rails Action Cable, Turbo Streams, Stimulus를 활용하여 관리자 대시보드에 실시간 Presence 기능을 성공적으로 구현했습니다.
  • 2 Action Cable의 개념적 복잡성, 테스트 난이도, Turbo Drive의 페이지 라이프사이클 문제 등 개발 과정의 주요 기술적 난관을 분석합니다.
  • 3 다중 Presence 키 추적, 클라이언트-서버 아키텍처, 그리고 프론트엔드 UI 업데이트 방식을 포함한 기능의 핵심 설계 원칙을 제시합니다.

도입

본 글은 스타트업의 관리자 대시보드에 팀원들의 실시간 작업 현황을 파악할 수 있는 'Presence' 기능 구현 경험을 공유합니다. 이 기능은 내부 직원 간의 협업을 증진하고 중복 작업을 줄이는 것을 목표로 하며, Basecamp의 Campfire 앱과 Rob Race의 개발 노트를 참고하고 AI 에이전트의 도움을 받아 설계되었습니다. 저자는 이 글을 통해 개발 과정에서 얻은 통찰과 기술적 난관을 상세히 기록하여 공유하고자 합니다.

핵심 아이디어 및 아키텍처

구현된 Presence 기능은 사용자가 동시에 여러 Presence 키를 추적할 수 있도록 설계되었습니다. 예를 들어, 특정 클라이언트 페이지(/admin/clients/1/messages)에 접속 중인 사용자는 해당 클라이언트, 모든 클라이언트, 그리고 대시보드 전체에 대해 ‘존재’ 상태로 인식됩니다. 또한, ‘내 Presence 추적’과 ‘모두의 Presence 표시’는 별개의 로직으로 분리하여 관리됩니다.

기술 스택 및 구현 상세

  • 클라이언트 측:
    • 브라우저 클라이언트는 특정 키를 가진 PresenceChannel에 구독합니다.
    • Stimulus 컨트롤러는 Turbo Cable 연결을 활용하며, 30초마다 setInterval을 이용해 서버로 ‘touch’ 메시지를 전송하여 하트비트 역할을 수행합니다.
  • 서버 측:
    • PresenceChannelconnected, disconnected, touch 액션을 처리하며, 연결 시 전달된 키를 저장합니다.
    • UserPresence Active Record 모델은 이러한 상태를 원자적으로(Postgres!) 영속화하고, increment, decrement, touch 메서드를 호출합니다.
    • 상태 변경 시, GoodJob을 통해 일반 Turbo Stream Broadcast Laters가 트리거됩니다.
  • 프론트엔드 시각화:
    • Turbo::StreamsChannel을 통한 일반 Turbo Stream Broadcasts를 활용하여, 현재 접속 중인 사용자 아바타의 고유 DOM 요소를 추가하거나 제거하는 방식으로 시각적 업데이트를 처리합니다.

개발 과정의 난관 및 비판

  • Action Cable의 개념적 복잡성:
    • Connection, Channel, Subscription, Consumer, Stream 등 Action Cable의 다양한 개념과 용어들이 혼재되어 있어, 인터페이스의 일관성이 부족하다는 지적입니다. 특히 클라이언트 측에서 consumer.subscriptions.create와 Turbo Rails의 cable.subscribeTo 사용 방식에서 이러한 혼란이 두드러집니다.
    • 서버 측에서는 ChannelStream을 연결하고, Stream을 통해 브로드캐스트하며, 특정 클라이언트에게 Channel을 통해 전송하는 복잡한 다중화 구조가 개발자의 이해를 어렵게 합니다. 저자는 StreamChannel 구현에 나중에 추가된 요소일 수 있다고 추정합니다.
  • 테스팅의 어려움:
    • Action Cable 관련 테스트 후에는 ActionCable.server.restart를 통해 서버를 격리하고 초기화해야 하는 번거로움이 있습니다.
    • 데드락, pg.exec 정지, Active Record의 undefined method 'count' for nil 오류 등은 데이터베이스 연결이 비정상적으로 읽히거나 비동기적으로 처리될 때 발생할 수 있는 문제로 지적됩니다.
  • 페이지 라이프사이클 관리:
    • data-turbo-permanent 속성을 사용하더라도 Stimulus 컨트롤러와 Turbo Cable JavaScript가 페이지 로드 중 연결/해제되는 현상이 발생했습니다.
    • 이를 해결하기 위해 nextTick/nextFrame을 사용하거나, data-permanent-cable-stream-source라는 사용자 정의 요소를 만들어 채널 구독 해제를 지연시키는 등의 우회적인 방법이 필요했습니다. 이러한 섬세한 코딩이 요구되는 점은 일반적인 개발 흐름을 방해합니다.

결론

결론적으로, 본 Presence 기능은 성공적으로 구현되어 팀원들의 업무 효율성과 협업을 향상시켰습니다. Action Cable의 복잡성으로 인해 개발 과정에서 여러 기술적 난관에 부딪혔지만, 이러한 깊이 있는 탐구는 최종적으로 만족스러운 결과물로 이어졌습니다. 이 과정에서 발견된 문제점들은 상류(upstream) 이슈 및 PR로 이어지며 커뮤니티에 기여할 수 있었습니다. 저자는 이 개발 노트가 다른 Ruby on Rails 개발자들에게 유용한 통찰을 제공하기를 바랍니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!