웹소켓(WebSocket)
웹 브라우저에서 오랫동안 사용한 통신 프로토콜은 HTTP다. HTTP는 동기 방식의 요청-응답 프로토콜로 반드시 클라이언트인 웹 브라우저가 요청해야만 서버인 웹 서버가 응답하는 프로토콜이다. 그러므로 HTTP 접속은 요청으로 시작해서 요청-응답을 반복하다가 반드시 응답으로 끝난다. 이 방식은 최초에 웹 혁명을 이끄는 데 있어서 중요한 단순성을 제공했다. 단순한 요청-응답 방식은 극단적인 수준까지 요청-응답을 처리하도록 서버를 개선하도록 했다. 범용 웹 사이트를 가능하게 하기 위해 TCP/IP 스택은 비약적으로 개선됐고, 아파치 웹 서버 같은 견고하고 성능도 좋은 서버들도 개발됐다. 사실 웹 기술이 보급되기 전까지 전통적인 클라이언트 서버 방식의 서버는 수십 클라이언트들의 접속을 처리할 수 있다면 할 일을 충분히 한다고 인정받던 시절도 있었다. 그러나 웹의 보급과 발전 덕분에 서버는 동시에 수천 접속을 유지하고 수만 요청도 처리할 수 있도록 비약적으로 진화했다. C10k 문제(일만 동시 접속 문제)라는 용어가 등장할 정도로 다수 동시 접속 처리 문제는 개발자들을 골치 아프게 했지만, 현재는 다양한 아키텍처 패턴을 이용해 이 문제를 극복하고 있다. 그러나 시대가 발전하면서 요청-응답으로는 처리하기 어려운 시나리오들도 웹 브라우저를 이용해 처리하고자 하는 요구가 끊임없이 확대됐다. 예를 들어 웹에서 실시간으로 변하는 증권 시세를 표시하려면 요청-응답 방식만으로는 해결할 수 없었다. 해결했다손 치더라도 속도나 성능 효용성이 충분하지 못했다. 그래서 자바 애플릿이나 액티브X 컨트롤을 이용해 소켓 통신으로 문제를 우회하려고도 했다. 그러나 이런 플러그인 기술들도 만족스럽지 못했고 보안 취약성이 또 다른 문제가 되기도 했다. 또 다른 예로 웹 채팅 애플리케이션이 있다. 웹 채팅을 구현하려면 Ajax 같은 복잡한 기술을 사용해야 그런대로 가능했다. 이런 방법들은 온전한 비동기 통신 기술이 아니다. 채팅은 상대방이 입력한 글이 웹 브라우저의 요청 없이도 즉시 본인의 웹 브라우저에 도착해야 하는 전형적인 비동기 통신 모델이다. 그런데 Ajax도 반드시 요청해야만 응답을 (비동기로) 수신할 수 있으므로 온전한 비동기 통신 방식은 아니다. 결국 요청-응답 방식의 HTTP 프로토콜 덕에 가질 수 있었던 단순성이 웹의 성공을 이끌었던 반면, 이 단순성은 웹 기반 비즈니스 시나리오가 확대됨에 따라 점점 부족함으로 작용하게 됐다. 완전한 비동기를 웹 브라우저에서 사용하려고 할 때, 단순함이 도리어 복잡한 Ajax 방식을 사용하게 강요한 면도 있었다. 이와 같이 확장되는 웹 기반 비즈니스 시나리오를 충족시키기 위해서는 좀더 편리한 통신 방법이 필요했다. 그리고 그 산물로 완전한 양방향 통신을 지원하는 웹소켓 프로토콜이 등장했다.
웹소켓 프로토콜은 상당히 최근에 등장했다. 2011년에서야 IETF는 웹소켓 프로토콜을 정의한 RFC 6455 문서를 표준으로 제정했다. 이에 따라 웹 브라우저들은 2011년부터 상호 호환성이 보장되는 RFC 6455 웹소켓 표준으로 웹소켓 지원을 통일했다 크롬 16, 사파리 6, 파이어폭스 11, 인터넷 익스플로러 10 이후 웹 브라우저들은 RFC 6455 웹소켓 표준을 준수한다. 웹소켓은 글자 그대로 소켓과 거의 같은 기능을 제공한다. 즉 일반적인 소켓이 제공하는 양방향 통신을 제공한다. (그러나 TCP/IP 계층보다 상위 계층 프로토콜로 독자적인 패킷 구조를 갖는다.) 그러므로 HTTP 프로토콜과 달리 접속이 완성된 후 TCP 소켓 접속처럼 서버의 우선 전송도 가능해 진다. 즉 요청-응답 순서가 클라이언트에서 서버뿐만 아니라 서버에서 클라이언트로도 가능하다. 그러므로 웹소켓은 클라이언트 풀(pull) 뿐만 아니라 서버의 푸시(push)도 어렵지 않게 제공한다. 그 결과 웹소켓은 동기 방식뿐만 아니라 비동기 방식 통신 프로토콜로 완벽하게 구현하게 하는 기반을 제공한다.
웹소켓은 HTTP 프로토콜에서 프로토콜 갱신 핸드쉐이크를 통해 웹소켓 프로토콜로 전환이 되는 구조를 가지고 있어, HTTP 표준 포트인 TCP 80 포트를 그대로 활용할 수 있다는 장점을 가진다고 한다. 그러나 이 부분에 있어서 필자는 다른 견해를 갖는데, 그 이유는 TCP 80 포트를 바인드하는 웹 서버가 HTTP 프로토콜과 웹소켓 프로토콜을 동시에 처리하려면 내부 아키텍처가 상당히 다른 두 프로토콜 스택을 서버 안에 함께 포함해야 한다. 웹소켓은 Stomp 같은 상위 프로토콜의 하부 프로토콜로 사용되기도 하는데 이 경우 또 다른 Stomp 프로토콜 스택도 웹 서버에 포함시켜야 하는 문제가 발생한다. 프로토콜 스택 개발은 웹 서버 개발과는 다른 문제일 수 있다. 그리고 아직 웹 서버들이 충분이 웹소켓을 다루는 기술이 성숙되지 않은 문제도 있다. 그러므로 웹소켓을 사용해야 한다면 웹 서버와 별도로 전용 웹소켓 서버를 운영하면서 TCP 포트를 별도로 개방하거나 방화벽 등의 정책으로 인해 접속 문제가 있을 수 있다면 별도 서버에 또 다른 TCP 80 포트를 개방해 웹소켓 서버를 운영하는 방법이 합리적이지 않을까 생각된다. 아직까지는 웹소켓의 사용 방식에 대해 뚜렷한 일관적인 방식이 등장하고 있지는 않은 것 같다.
웹소켓의 또 다른 문제는 웹소켓 위의 애플리케이션 프로토콜이 충분히 성숙돼지 않았다는 점이다. 웹소켓은 TCP 계층에 아주 얇은 층을 추가한다. 그러므로 웹소켓을 직접 사용하는 것은 마치 TCP 소켓을 직접 사용하는 것과 같다. 일반적으로 개발자들은 TCP 소켓을 직접 사용하기보다 상위 애플리케이션 프로토콜을 이용한다. 이런 예로 Telnet, Ftp, Ssh, HTTP, AMQP, OpenWire, Stomp, MQTT 등등이 애플리케이션 프로토콜이다. 그러므로 웹소켓을 충분히 잘 활용하려면 웹소켓 위의 애플리케이션 프로토콜이 많아지고 또 쓰임새도 정비돼야 한다.
실시간 트위터 이미지 사이트의 웹소켓 사용
웹소켓을 먼저 설명한 이유는 실시간 트위터 이미지 사이트도 웹소켓을 사용하기 때문이다. 실시간 트위터 이미지 사이트는 웹소켓을 아주 쓸모 있게 사용한다. 실시간 트위터 이미지 사이트의 전체적인 정보 흐름과 통신 프로토콜들은 다음과 같다.
1) 미국에 위치한 실시간 트위터 이미지 사이트의 백그라운드 Camel 서비스는 트위터에서 고양이로 트윗을 검색해 결과들을 수신한다. 2) 그 중 이미지를 포함한 트윗을 추출해 한국에 위치한 ActiveMQ 서버에 OpenWire 프로토콜을 사용해 메시지로 전달한다. 3) 메시지를 수신한 ActiveMQ 서버는 메시지를 게시 구독 방식의 토픽에 게시한다. 실시간 트위터 이미지 사이트에서 페이지를 조회한 웹 브라우저는 이미 웹소켓 위의 Stomp 프로토콜로 ActiveMQ 서버에 접속한 토픽 구독자다. 4) 토픽에 게시된 고양이 이미지 트윗 메시지 사본은 웹소켓 위의 Stomp 프로토콜을 통해 ActiveMQ 서버로부터 웹 브라우저로 비동기적으로 전송된다. 웹 브라우저는 비동기적으로 수신된 메시지를 웹 브라우저 화면에 고양이 쎔네일 이미지로 출력한다.
미국에 위치한 실시간 트위터 이미지 사이트 웹 서버의 Camel 서비스와 대한민국에 위치한 ActiveMQ 서버는 메시징 프로토콜로 OpenWire 프로토콜을 사용했다. OpenWire 프로토콜은 다양한 언어나 플랫폼에서 ActiveMQ에 접속할 수 있게 하는 메시징 프로토콜이다. ActiveMQ는 OpenWire, AMQP, XMPP, MQTT 등 다양한 메시징 프로토콜을 지원한다. 웹 브라우저와 ActiveMQ 서버는 웹소켓 위의 Stomp 프로토콜을 사용한다. Stomp 프로토콜을 사용한 이유는 WebSocket 위의 Stomp 프로토콜을 지원하는 STOMP.js라는 JavaScript 라이브러리가 있고, ActiveMQ 서버도 웹소켓 위의 Stomp 프로토콜을 잘 지원하기 때문이다. 참고로 TCP/IP 위의 Stomp 프로토콜과 웹소켓 위의 Stomp 프로토콜은 호환되지 않는다. TCP/IP 위의 Stomp 프로토콜은 Stomp 프로토콜이 TCP 계층 바로 위에 구현되는 반면, 웹소켓 위의 Stomp 프로토콜은 TCP 계층 위에 웹소켓 계층이 있고 그 위에 Stomp 프로토콜이 구현된다. 그러므로 Stomp 프로토콜을 지원한다고 해도 직접적으로 웹소켓 위의 Stomp 프로토콜로 통신할 수 없다. 문자 기반 프로토콜이어서 인지, 웹소켓 위에서 가장 구현하기 쉬워서 인지 상당히 이른 시기에 웹소켓 위의 Stomp 프로토콜은 등장했다. 한편 RabbitMQ의 기본 프로토콜인 AMQP 프로토콜이 웹소켓 위의 JavaScript 라이브러리로 개발이 된다면 웹 브라우저에서도 AMQP 프로토콜을 사용할 수 있을 것이다. 그러나 현재 RabbitMQ 쪽에서는 Node.js와 연결에 주력하고 있는 것 같다. RabbitMQ는 Node.js에서 동작하는 AMQP JavaScript 라이브러리까지만 지원하고 있다. 아마도 이 JavaScript 라이브러리는 TCP/IP 위에서 구현된 라이브러리일 것이다. 즉 AMQP 프로토콜의 하부 통신 계층이 TCP일 것이다. 그러므로 현재 웹소켓 위에서 사용할 수 있는 메시징 프로토콜 라이브러리는 STOMP.js이 유일하다. (글을 쓰면서 좀더 조사해 보니 최근 들어 웹소켓 위의 MQTT 프로토콜도 사용 가능한 것으로 보인다. 이에 대한 검토는 독자들에게 맡긴다!) 여기서 주목할 점은 메시징 프로토콜과 메시지 채널(큐, 토픽)은 서로 다른 종류의 개체라는 점이다. 즉 같은 메시지 채널이라도 여러 프로토콜로 발신, 수신, 게시, 구독이 가능하다. 실시간 트위터 이미지 사이트도 동일한 토픽에 대해 메시지 전송은 OpenWire 프로토콜을 메시지 수신은 웹소켓 위의 Stomp 프로토콜을 사용했다.
웹소켓을 사용하면 웹 브라우저를 기업 통합 솔루션의 비동기 메시징 컴포넌트의 일원으로 포함시킬 수 있게 된다. 실시간 트위터 이미지 사이트는 트위터 이미지 메시지를 비동기로 전송하는 용도로 간단하게 웹소켓을 사용했지만, 앞서 언급했듯이 웹소켓은 웹 브라우저의 요청-응답 또는 폴링 방법으로는 해결하기 어려운 실시간 비동기 정보를 손쉽게 처리할 수 있게 해준다. 예를 들어 실시간 뉴스, 실시간 대시보드, 실시간 제어, 실시간 게임, 실시간 그래프 애플리케이션 같은 것들도 웹 브라우저로 구현할 수 있는 가능성을 열어준다. 이를 위해서는 SVG(SVG(Scalable Vector Graphics)나 Canvas 같은 HTML5의 다른 기능들도 활용해야 할 것이다. 또한 필자도 웹소켓이 요청-응답 형식의 기존 HTTP 프로토콜을 완전히 대체할 수 있다고는 생각지 않는다. 기존 HTTP 프로토콜은 화면 조회, 웹 서비스, Restful, Ajax RPC 등 다양한 요청-응답 패턴을 여전히 잘 수용한다. 그러므로 웹소켓의 역할은 요청-응답 방식의 HTTP 프로토콜로는 쉽게 해결할 수 없었던 실시간 비동기 통신을 웹 브라우저에 제공함으로 웹 브라우저의 사용 범위를 넓혀주는 것으로 봐야 한다. 즉 실시간 비즈니스 시나리오가 포함된 멋진 웹 애플리케이션을 만들려면, HTTP 프로토콜과 웹소켓 프로토콜 중 어느 하나만을 고집하는 것이 아니라 서로의 장점을 함께 활용해야 한다. 편식은 언제나 몸을 해롭게 한다.
웹소켓은 표준이 2011년에 완성됐고 표준을 지원하는 브라우저도 2011년 이후 버전들이고 특히 대한민국에서 많이 사용하는 인터넷 익스플로러는 버전 10부터 지원되므로 아직은 웹소켓을 사용한 사례가 많지 않은 것도 사실이다. 그러나 웹소켓을 범용 서비스가 아닌 기업 통합 등 기업의 특화된 서비스에 이용한다면 실시간 정보의 사용에 있어서 전용 클라이언트 애플리케이션 못지 않게 훌륭한 웹 브라우저 애플리케이션을 개발할 수 있을 것이라 기대한다. 이 글을 통해 독자들도 웹소켓의 가능성에 주목할 수 있는 계기가 됐으면 한다.