시나몬 브레드
개발과 관련된 이야기를 하는 곳입니다. 쉽게 찾을 수 있는 소개 형식의 글 보다는 실무를 통해 얻어진 깊이있는 정보와 전체 흐름을 이해할 수 있는 지식을 다루려고 노력중입니다.
webSocket 으로 개발하기 전에 알고 있어야 할 것들

이번 프로젝트에 webSocket으로 통신하는 기능을 넣기 위해 검색을 해봤는데 많은 글들이 이런 저런 용어들에 대해서 두서없이 사용하고 있어서, 관련 내용을 이해하는데 어려움이 있었다. 우여곡절 끝에 전체적인 흐름을 이해하게 되어 다른 개발자들은 고생을 덜 하기를 바라는 마음으로 정리를 해본다. 관련 기술에 대해서 어떻게 발전해왔는지 흐름을 이해하면 프로젝트 진행시 사용할 기술들을 선택하는데 큰 도움이 될 수 있다.




기존의 양방향 통신 방법

webSocket은 웹페이지와 서버간에 실시간 상호작용을 위해 만들어진 스팩이다.

http 규격 자체가 클라이언트에서 서버로의 단방향 통신을 위해 만들어진 방법으로,
webSocket 이전에는 실시간 통신을 위해서 일반 http request에 약간의 트릭을 사용해서 실시간인것 처럼 작동하게 하는 아래와 같은 기술들이 있었다.

polling

클라이언트가 평범한 http request를 서버로 계속 날려서 이벤트 내용을 전달받는 방식이다. 가장 쉬운방법이지만 클라이언트가 계속적으로 request를 날리기때문에 클라이언가 많아지면 서버의 부담이 급증하게 된다. http request connection을 맺고 끊는것 자체가 부담이 많은 방식이다. 그럼에도 불구하고 클라이언트에서 실시간정도의 빠른 응답을 기대하기도 어렵다.



long polling

클라이언트에서 서버로 일단 http request를 날린다. 이상태로 계속 기다리다가 서버에서 해당 클라이언트로 전달할 이벤트가 있다면 그순간 response 메시지를 전달하면서 연결이 종료된다. 클라이언트에서는 곧바로 다시 http request를 날려서 서버의 다음 이벤트를 기다리게 되는 방식이다. 일반 polling 방식보다는 서버의 부담이 줄겠지만 클라이언트로 보내는 이벤트들의 시간간격이 좁다면 polling 과 별 차이가 없게 되며, 다수의 클라이언트에게 동시에 이벤트가 발생될 경우에는 곧바로 다수의 클라이언트가 서버로 접속을 시도하면서 서버의 부담이 급증하게 된다.



streaming

long polling과 마찬가지로 클라이언트에서 서버로 일단 http request를 날린다. 서버에서 클라이언트로 이벤트를 전달할때 해당 요청을 끊지 않고 필요한 메시지만 보내기를(flush) 반복하는 방식이다. long polling에 비해 서버에서 메시지를 보내고도 다시 http request 연결을 하지 않아도 되어 부담이 경감될것으로 보인다.


이와 같은 기술을 사용해본적은 없지만 long polling, streaming 방식의 경우 서버에서 클라이언트로 메시지를 보낼 수 는 있으나 클라이언트에서 서버로 메시지를 보내는것은 문제가 있어 보인다.



추가설명





이런 꼼수에서 벋어나 정식으로 클라이언트 서버간 양방향 통신이 가능하게 하기 위해서 HTML5 표준의 일부로 webSocket이 만들어지게 되었다.

webSocket이 기존의 일반 TCP Socket과 다른 점은 최초 접속이 일반 http request를 통해 handshaking과정을 통해 이루어 진다는 점이다. http request를 그대로 사용하기 때문에 기존의 80, 443 포트로 접속을 하므로 추가로 방화벽을 열지 않고도 양방향 통신이 가능하고, http 규격인 CORS적용이나 인증등의 과정을 기존과 동일하게 가저갈 수 있는것이 장점이다.



이렇게 websocket은 처음부터 웹페이지와 서버간에 양방향 통신을 위해 만들어진 스팩이다.


여기까지는 아무문제 없고 복잡할 것도 없어 보이는데 아래와 같은 이슈로 인해 생각할 것들이 많아진다.


  1. webSocket 미지원 웹 브라우저: 오래된 버전의 웹 브라우저는 webSocket을 지원하지 않는다.(특히 인터넷 익스플로러 구버전)
  2. 웹 브라우저 이외의 클라이언트 지원: 서버의 입장에서 클라이언트는 웹 브라우저뿐만이 아니다.

    1. webSocket 미지원 웹 브라우저

    웹개발에 있어서 인터넷 익스플로러는 여러가지로 골칫거리다. 가장 큰 문제는 자동업데이트가 안된다는 문제이다. 웹 스팩이 계속 발전하고 있기 때문에 웹 브라우저도 항상 최신버전을 유지해 주지 않으면 새로 만든 스팩으로 개발된 페이지는 작동하지 않게 되는것이다. 그래서 최근의 웹 브라우저들은 모두 묻지도 따지지도 않고 무조건 최신버전으로 업그레이드가 자동으로 이루어 진다. 이렇게 해서 컴맹이라 할지라도 웹브라우저 업그레이드에 신경쓰지 않고 최신 스팩으로 작성된 웹 페이지를 여는데 아무 문제가 없다. 
    그런데 인터넷 익스플로러 과거버전은 심지어 윈도우 업데이트를 해야만 업그레이드가 가능했기 때문에 사람들이 사용하는 인터넷 익스플로러의 버전은 파편화가 심해지게 되었다. 각 버전마다 기능이 다르고, 게다가 인터넷 익스플로러는 그당시의 표준 규격도 준수하지 않는등 개발자들 사이에서 악명이 높다.



    인터넷 익스플로러 구버전 사용자들은 WebSocket으로 작성된 웹 페이지를 볼 수가 없기 때문에 웹 서비스를 제공하는 사업자 입장에서는 치명적일 수 있다.
    그래서 이를 해결하기 위해 나온 기술들이 몇가지 있는데 원리는 간단하다. 웹페이지가 열리는 브라우저가 webSocket을 지원하면 일반 webSocket 방식으로 동작하고 지원하지 않는 브라우저라면 위에서 설명한 일반 http 스팩을 이용해서 실시간통신을 흉내낼수 있는 방식으로 통신을 하게 해주는 것이다. 아래는 이런 방식으로 만들어진 솔루션이다.

    Socket.io(http://socket.io)

    node.js 기반으로 만들어진 기술로 자체 스팩으로 만들어진 socket.io 서버를 만들고 socket.io 클라이언트와 브라우저에 구애받지 않고 실시간 통신이 가능해진다. socket.io는 node.js 기반이기때문에 모든 코드가 javascript로 작성되어 있다. 서버, 클라이언트 모두 javascript 기반으로 개발하는 것이 기본이다. 그러다보니 자바 개발자들은 socket.io를 쓸 수 없다. 자바로 개발이 가능하게 해주는 방법이 몇가지 있긴한 것 같지만 역시 javascript 기반 솔루션은 javascript로 개발해야 문제발생을 줄일 수 있을 것이다.


    SockJS(http://sockjs.org)

    springframework에서 WebSocket을 지원한다. 스프링 메뉴얼에 webSocket 부분을 보면 위와 같은 브라우저 문제를 해결하기 위한 방법으로 SockJS를 솔루션으로 제시한다. 역시 자체 스팩으로 webScoket 미지원 브라우저를 관리한다. 서버개발시 스프링 설정에서 일반 webSocket 으로 통신할지 SockJS 호환으로 통신할지 결정할 수 있다. 클라이언트쪽은SockJS client를 통해 서버와 통신한다.




    2. 웹 브라우저 이외의 클라이언트 지원

    이번 프로젝트에서 구상한것은 webSocket 서버를 하나 만들고 서로 다른 타입의 각종 클라이언트와 통신할 수 있도록 하는 것이 목표이다. 
    클라이언트는 웹 페이지(모바일, PC), 안드로이드, 아이폰 등이다. webSocket 자체가 웹 브라우저를 위한 기술이지만 일반 네이티브 클라이언트도 스팩만 준수한다면 통신하지 못할것이 없다. github를 뒤저보면 개발자 개인들이 만든 websocket 클리이언트 프로젝트들이 언어 종류별로 몇가지 있다. (참고로 자바의 경우 알려진 대형 벤더에서 공식적으로 만들어진것은 찾지 못했다.)
    이걸로 서버와 통신하면 간단할 것 같지만 위에서 본 내용과 같이 실제로는 구버전 웹 브라우져 때문에 순수 webSocket API 보다 이걸 자체 스팩으로 다시 감싼 다른 솔루션(socket.io, sockjs...) 의 방식을 이용해서 서버를 만들어야 한다는 것이 문제다. socket.io, sockjs등 모드 웹브라우저를 위한 javascript 클라이언트가 기본이기 때문에 다른 언어 지원은 미약하고 java 클라이언트의 경우 spring 기반으로 만들어져 있어서 안드로이드에서 실행시키기는 부적합 하거나.. 하는 등 애로 사항이 많이 있는 상태이다.



    결론

    위와같은 내용을 이해하고 최종 결정된 개발 스팩은 다음과 같다.

    • 서버는 spring을 이용해서 STOMP 규격으로 개발한다.
      • 두개의 접속 경로를 열어서 하나는 순수 stomp 규격으로 오픈
      • 다른 하나는 sockJS + stomp 규격으로 오픈
    • 웹 페이지에서는 sockJS javascript 클라이언트를 이용해서 서버와 접속한다. sockJS를 이용해서 websocket을 지원하지 않는 브라우저까지 커버를 한다.
    • android와 IOS에서는 개인 개발자들이 만들어놓은 stomp  규격의 클라이언트 라이브러리를 이용해서 서버와 접속한다.




    서버에서는 stomp와 sockJS + stomp 로 두개를 오픈하고 각각의 경로로 들어온 같은 message에 대해서 동일한 소스로 message 처리가 가능하다.

    (STOMP 규격과 위설명 내용의 실제 구현은 나중에 다시 포스팅을 할 예정)



      Comments,   0  Trackbacks
    댓글 쓰기