웹 소켓 개발

본 장에서는 ProObject에서 웹 소켓을 등록하고 메시지 송수신, 커스터마이징하는 방법에 대하여 설명한다.

1. 개요

HTTP는 요청-응답 모델(R-R Model : Request-Response Model)만을 고려하고 있으므로, 일반적으로 사용하는 폴링(Polling) 모델에는 적합한 모델이다. 다만 모든 서비스가 항상 폴링 모델에 적합하지 않다. 서버가 클라이언트에 문의를 하거나 특정한 이벤트가 발생한 경우는 여러 클라이언트에 이 이벤트를 알려주는 작업이 별도로 필요할 수도 있다.

대표적인 경우가 흔히 볼 수 있는 모니터링 시스템으로 주기적으로 데이터를 서버로부터 받아오거나 특정 이벤트가 발생했을 때 알람을 받아야 하는 경우이다. 이러한 작업들을 폴링 모델만으로 해결하는 것은 매우 비효율적이기 때문에 웹에서는 웹 소켓의 개념이 나타나게 되었다.

웹 소켓은 일반적인 TCP 소켓과 비슷한 기능으로 기존의 HTTP에서 이루어진 단방향 통신의 한계를 극복할 수 있다. 때문에 서버와 주기적으로 통신이 필요한 대부분의 웹 애플리케이션에서는 여러 분야에 웹 소켓이 이용되고 있다.

ProObject에서도 이러한 웹 소켓을 설계 사상에 맞추어 더욱 편리하게 이용할 수 있도록 기능을 제공하고 있다.

다음의 주소로 웹 소켓에 접근할 수 있다.

ws://ServerIP:serverPort/{applicationName}/[WebsocketID]

위의 양식을 제대로 지키지 않았을 경우에는 ProObject의 웹 소켓 연결 자체가 거부되므로 주의하도록 한다.

2. 웹 소켓 등록

ProObject에서 웹 소켓을 사용하기 위해서는 애플리케이션 설정에 웹 소켓을 등록해야 한다.

다음의 애플리케이션 메타 파일에 웹 소켓을 정보를 설정한다.

${APP_HOME}/config/application.xml

다음은 웹 소켓을 등록한 예로 <web-socket> 항목 정보를 설정한다. 상황에 따라서 다수의 웹 소켓을 운용할 수도 있다.

<application>
  <web-socket>
     <path>test</path>
     <class-name>com.tmax.proobject.example.websocket.ExampleWebSocket</class-name>
     <service>test.example.WebSocketTest</service>
  </web-socket>
</application>

다음은 각 설정항목에 대한 설명이다.

태그 설명

<path>

웹 소켓의 아이디를 나타내며, 웹 소켓의 컨텍스트 패스를 의미한다.

예를 들어 'testApp’이라는 애플리케이션에서 test라는 이름의 path를 지니는 웹 소켓을 설정한 경우 다음의 주소로 접속이 가능하다.

ws://ServerIP:ServerPort/testApp/test

<class-name>

ProObjectWebSocket 클래스의 구현체를 지정한다.

구현체를 직접 지정하는 경우에는 패키지 이름을 포함한 모든 클래스 이름을 적어주어야 하며, 클래스는 반드시 이벤트 계층에 배포되어야 한다. 지정하지 않는 경우 ProObject의 기본 웹 소켓 구현체가 지정되며, 일반적인 경우에는 기본 웹 소켓 구현체를 사용할 것을 권장한다.

설정한 클래스는 기본적인 WebSocket과 제공되는 API가 비슷하므로 기존의 웹 소켓을 사용해본 적이 있다면 쉽게 작성할 수 있으나, onMessage 항목은 오버라이딩이 불가능함만 유의하도록 한다. 자세한 내용은 웹 소켓 커스터마이징을 참고한다.

(기본값: com.tmax.proobject.engine.websocket.DefaultProObjectWebSocket)

<service>

웹 소켓으로 메시지가 전달된 경우에 호출될 메시지 처리자 서비스의 이름을 지정한다.

기본적으로 ProObject는 모든 것을 서비스로 인식한다. 따라서 웹 소켓으로 들어오는 모든 메시지를 하나의 서비스로 인식하고 수신되는 메시지 또한 '서비스 요청’으로 처리한다.

ProObject에서는 웹 소켓으로 들어오는 메시지를 처리하기 위해 메시지가 수신되면 호출되는 '메시지 처리자 서비스’를 통해 이러한 요청을 처리할 수 있다. 이때 호출된 서비스의 출력값은 전달되지 않으며 입력값은 다음의 값으로 고정된다.

com.tmax.proobject.engine.websocket.WebSocketMessageDataObject

3. 웹 소켓 메시지 수신

웹 소켓 설정에서 서비스를 설정하면 서비스가 자동으로 호출된다. 호출된 서비스는 일반적인 서비스와 동일하므로 전달받은 메시지를 이용하여 DB 처리를 하거나 다른 서비스와 연동하는 등의 DB 작업을 추가적으로 수행하거나 다른 웹 소켓들에게 브로드캐스팅 작업을 진행하는 것도 가능하다.

4. 웹 소켓 메시지 송신

서비스나 이벤트에서 웹 소켓으로 메시지를 송신하려면 WebSocketManager를 사용한다. 메시지를 송신할 때에는 브로드캐스팅 방식과 개별 전송 방식 두 가지 중에서 하나를 선택하여 전송이 가능하다.

  • 브로드캐스팅

    브로드캐스팅하는 경우 두 개의 매개변수를 넘겨주는 write 메소드를 사용한다.

    WebSocketManager.write(path, message);
    매개변수 설명

    path

    웹 소켓의 아이디를 나타내며, 웹 소켓의 컨텍스트 패스이다.

    test로 설정하였다면 다음의 주소로 접속한 모든 웹 소켓에 메시지를 전달한다.

    ws://ServerIP:ServerPort/{applicationName}/test

    message

    지정한 웹 소켓 패스로 접속한 모든 웹 소켓에게 전달할 메시지를 지정한다.

    전달할 수 있는 타입은 String, byte[], Object이다.

  • 개별 전송

    필요한 경우에는 웹 소켓 세션 중 단 하나의 세션만 골라 메시지를 송신할 수 있다. 브로드캐스팅과 동일하게 write 메소드를 사용하고 세션 키를 추가로 전달한다. path, message는 브로드캐스팅의 설명을 참고한다.

    WebSocketManager.write(path, sessionKey, message);
    매개변수 설명

    sessionKey

    웹 소켓 패스에 접속한 웹 소켓들 중 메시지를 전달할 세션의 키를 입력한다.

5. 웹 소켓 세션

웹 소켓을 통해 접속된 세션은 서비스 내에서는 일반적으로 세션을 얻을 때와 동일하게 RequestContext로부터 세션을 얻어오는 것이 가능하다.

ProObjectSession session = requestContext.getSession();

세션을 얻어온 다음에는 일반적인 웹 세션과 동일한 방식으로 내부의 속성(Attribute)들을 설정하는 것이 가능하다. WebSocket을 통해 세션이 맺어졌다면, 일반적인 웹 세션의 Attribute가 아니라 웹 소켓 세션 내의 UserProperties에 저장되며 API 만 웹 세션 형태로 제공되므로 추후 이용하는데 참고하도록 한다.

이벤트 핸들러나 서비스에서 현재 연결되어 활성화 중인 세션들의 목록을 얻어올 경우에는 다음과 같이 WebSocketManager를 이용한다.

Set<String> sessionKeys = WebSocketManager.getActiveSessionKeySet(path);

6. 웹 소켓 커스터마이징

ProObject의 웹 소켓은 브로드캐스팅이나 개별 전송을 할 수 있도록 코드가 작성되어 있다. 웹 소켓을 통한 메시지 송신이나 세션 관리를 연결하는 경우 이벤트 등에 대한 처리를 하려면 DefaultProObjectWebSocket을 상속받아 구현하는 것을 권장하나, 필요한 경우에는 커스터마이징이 가능하다.

onMessage 같은 경우는 오버라이딩이 불가능하도록 코드가 작성되어 있다. 메시지를 수신하는 경우 마다 작업을 처리하고 싶은 경우에는 웹 소켓의 메시지 처리자 서비스를 지정하도록 한다.

ProObjectWebSocket 인터페이스를 상속받아야 하며, 구현해야 하는 메소드들은 다음과 같다.

  • onCreate

    웹 소켓 객체가 처음 생성되었을 때 불리우는 메소드이다. 각종 초기화 등의 작업을 할 때 사용한다.

    public void onCreate(String path);
  • onInit

    웹 소켓으로 연결 요청이 들어와 세션이 맺어진 경우에 호출되는 메소드이다. 연결된 세션 객체가 함께 전달된다.

    public void onInit(Session session);
  • onWrite

    서비스, 이벤트 등에서 WebSocketManager를 통해 write 메소드를 호출한 경우 호출되는 메소드이다.

    WebSocketWriteBuffer 객체가 전달되며, 해당 객체 내에는 전달할 세션의 키와 전달할 객체(Object, String, byte[])가 모두 전달된다. 세션의 키가 null인 경우에는 브로드캐스팅 메시지이며, 호출한 메소드의 종류에 따라 Object, String, byte[]의 필드가 채워져서 전달된다.

    public void onWrite(WebSocketWriteBuffer event);
  • onError

    웹 세션에서 오류가 발생한 경우 호출되는 메소드이다.

    일반적으로 거의 사용할 일은 적은 편이며, onError가 호출되었다고 해도 웹 소켓 세션이 종료되지 않는다는 점은 주의해야 한다.

    public void onError(Session session, Throwable exception);
  • onClose

    웹 소켓의 연결이 끊어진 경우에 호출되는 메소드이다.

    public void onClose(Session session);

위의 메소드들을 일반적인 웹 소켓을 구현하는 것과 동일하게 커스터마이징해서 사용할 수 있다. 커스터마이징을 후에는 반드시 애플리케이션 설정에서 클래스를 등록하고 클래스는 이벤트로 배포해야 한다.

일반적으로 서블릿 워커 스레드에서 수행되는 웹 소켓과 달리 JEUS에서의 웹 소켓은 Node.JAVA 스레드에서만 수행되므로 항시 싱글 스레드에서만 수행된다. 따라서 웹 소켓에서 세션을 관리할 때 일반적으로 걱정하는 동기화 부분에 대해서는 전혀 고려하지 않아도 된다.