Ruby에서 지속적인 연결: Rack을 활용한 스트리밍 바디, SSE, 그리고 웹소켓

Persistent Connections in Ruby: Streaming Bodies, SSE, and WebSockets with Rack | DevelClan

작성자
Ruby Weekly
발행일
2025년 07월 31일

핵심 요약

  • 1 본 문서는 Ruby Rack을 사용하여 실시간 데이터 처리를 위한 지속적인 연결 기법인 스트리밍 바디, Server-Sent Events (SSE), 그리고 WebSockets 구현 방법을 설명합니다.
  • 2 스트리밍 바디는 단순 HTTP 연결을 통해 점진적 데이터 전송을 가능하게 하며, SSE는 서버에서 클라이언트로의 단방향 이벤트 전송에, WebSockets은 양방향 실시간 통신에 적합합니다.
  • 3 Puma와 같은 웹 서버의 스레드 한계를 극복하기 위해 Falcon 서버의 Ruby Fibers를 활용한 동시성 처리 방식과 각 기법의 실용적인 코드 예시를 제공합니다.

도입

HTTP는 기본적으로 요청-응답 모델을 따르며, 이는 대부분의 웹 페이지에 적합하지만 실시간 데이터(알림, 대시보드, 채팅 등) 처리에는 한계가 있습니다. 이러한 한계를 극복하기 위해 Ruby 개발 환경에서는 지속적인 연결 기법이 필수적입니다. 본 문서는 Rack 인터페이스를 활용하여 Ruby 애플리케이션에서 스트리밍 바디(Streaming Bodies), Server-Sent Events (SSE), 그리고 WebSockets을 구현하는 방법을 다룹니다. Rack은 Ruby 애플리케이션과 웹 서버를 연결하는 표준 인터페이스로, 이 세 가지 기법을 모두 지원하여 실시간 통신 애플리케이션 개발을 용이하게 합니다.

Rack에서 스트리밍 바디는 애플리케이션이 body로 호출 가능한 객체(예: proc)를 반환할 때 작동합니다. Rack은 먼저 헤더를 전송한 후, write, flush, close를 구현하는 스트림 객체를 전달하여 연결을 닫지 않고 클라이언트에 데이터를 푸시할 수 있게 합니다. 이는 실시간 진행률 표시줄이나 로그 표시, 그리고 연결을 다시 열지 않고 새로운 데이터가 도착할 때까지 요청을 유지하는 롱 폴링 구현에 유용합니다. Puma와 같은 서버에서 스트리밍 바디를 사용할 경우, 각 요청이 스레드를 점유하여 동시 연결 수가 많아지면 스레드 부족 현상이 발생할 수 있습니다. 이를 해결하기 위해 Ruby Fibers를 사용하는 Falcon 서버가 대안으로 제시됩니다. Falcon은 각 요청을 경량의 파이버에서 실행하여 단일 스레드 내에서 수백 개의 협력적 파이버를 멀티플렉싱함으로써 Puma의 스레드 한계를 극복하고 병렬 처리를 가능하게 합니다.

Server-Sent Events (SSE)는 서버에서 클라이언트로만 이벤트를 전송하는 표준으로, HTTP 연결을 유지합니다. 브라우저에서는 JavaScript EventSource API를 사용하고, 서버는 Content-Type: text/event-stream 헤더로 응답합니다. 본문에서는 Thread::Queue를 사용하여 이벤트 생산과 소비를 분리하고, 스트림 스레드가 블로킹되는 것을 방지하기 위해 Thread.new로 백그라운드 스레드에서 이벤트를 생성하는 예시를 보여줍니다. 이는 데이터 소스를 유연하게 변경하고(예: Redis pub/sub 또는 PostgreSQL LISTEN/NOTIFY) 경쟁 조건을 피하는 데 도움이 됩니다.

WebSockets은 HTTP 핸드셰이크(101 Switching Protocols) 이후 TCP 연결을 유지하여 클라이언트와 서버가 동시에 데이터를 주고받을 수 있는 전이중(full-duplex) 프로토콜입니다. 이는 채팅과 같은 양방향 통신 애플리케이션에 이상적입니다. WebSocket 프로토콜을 직접 구현하는 것은 복잡하지만, faye-websocket gem을 사용하면 낮은 수준의 세부 사항을 추상화하여 쉽게 구현할 수 있습니다. 본문에서는 faye-websocket gem을 활용하여 활성 연결을 관리하고, 메시지를 모든 연결된 클라이언트에 브로드캐스트하는 간단한 채팅 애플리케이션의 Rack 서버 및 HTML 클라이언트 구현 예시를 제시합니다. ping: KEEPALIVE 매개변수를 사용하여 연결을 유지하고, web_socket.rack_response를 통해 올바른 핸드셰이크 헤더를 포함한 Rack 배열을 반환하는 방법을 설명합니다.

결론

본 문서를 통해 Ruby 환경에서 실시간 데이터 처리를 위한 세 가지 핵심적인 지속적인 연결 기법인 스트리밍 바디, SSE, 그리고 WebSockets의 개념과 구현 방법을 심층적으로 탐구했습니다. 스트리밍 바디는 HTTP를 통해 점진적으로 데이터를 전송하는 기본적이고 효율적인 방법이며, SSE는 서버에서 클라이언트로의 단방향 알림에 특화되어 HTTP를 계속 활용합니다. 궁극적으로 WebSockets은 HTTP를 대체하는 전이중 프로토콜로서 양방향 실시간 통신에 최적화된 솔루션을 제공합니다. 이 모든 과정에서 Rack은 Ruby 애플리케이션이 다양한 형태의 지속적인 연결 전략을 유연하게 처리할 수 있도록 하는 다재다능한 인터페이스임을 입증했습니다. 이러한 기법들은 Ruby를 활용한 현대적인 실시간 웹 애플리케이션 개발에 필수적인 역량을 제공합니다.

댓글 0

댓글 작성

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

아직 댓글이 없습니다

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