본문 바로가기

카테고리 없음

websocket

WebSocket 이란?


기존의 단방향 HTTP 프로토콜과 호환되어 양방향 통신을 제공하기 위해 개발된 프로토콜.
일반 Socket통신과 달리 HTTP 80 Port를 사용하므로 방화벽에 제약이 없으며 통상 WebSocket으로 불린다.
접속까지는 HTTP 프로토콜을 이용하고, 그 이후 통신은 자체적인 WebSocket 프로토콜로 통신하게 된다.

 

 

언제 쓰면 좋을까?


Spring Reference을 참조하면, 자주 + 많은 양의 + 지연이 짧아야 하는 통신을 할 수록 WebSocket이 적합하다고 설명하고 있다. 주로 채팅이나 게임이 이러한 요구 사항을 가질 것이다. 단순한 알림 성격의 뉴스 피드 같은 정보에는 polling이나 streaming 방식이 더욱 단순하고 효율적인 솔루션이 될 수 있다.

 

 

websocket에 대해 아주 잘 정리 해놓은 두개의 블로그를 공유해 드립니다.

자세한 내용은 아래 블로그를 방문하셔서 확인 부탁드립니다.

블로그1, 블로그2

 

구현


build.gradle

아래 프로젝트를 실행하기 위해서는 

thymeleaf, websocket, lombok 에 대한 dependency만 설치하면 됩니다.

 

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
   implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
   implementation 'org.springframework.boot:spring-boot-starter-web'
   compileOnly 'org.projectlombok:lombok'
   runtimeOnly 'com.h2database:h2'
   runtimeOnly 'mysql:mysql-connector-java'
   annotationProcessor 'org.projectlombok:lombok'
   testImplementation 'org.springframework.boot:spring-boot-starter-test'

   // websocket //
   implementation 'org.springframework.boot:spring-boot-starter-web'
   implementation 'org.springframework.boot:spring-boot-starter-websocket'
}

 

@Configuration
@RequiredArgsConstructor
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private final ChatHandler chatHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    
        registry.addHandler(chatHandler, "ws/chat").setAllowedOrigins("*");
    }
}

 

WebSocketHandlerRegistry WebSocketHandler의 구현체를 등록한다.

핸들러를 이용해 WebSocket을 활성화하기 위한 Config를 작성할 것이다.

@EnableWebSocket 어노테이션을 사용해 WebSocket을 활성화 하도록 한다.

WebSocket에 접속하기 위한 Endpoint는 /chat으로 설정하고, 

도메인이 다른 서버에서도 접속 가능하도록 CORS : setAllowedOrigins("*"); 를 추가해준다.

이제 클라이언트가 ws://localhost:8080/chat으로 커넥션을 연결하고 메세지 통신을 할 수 있는 준비를 마쳤다.

 

다음은 websocket을 test할 view를 rendering 해주는 controller를 먼저 만들어줍니다.

 

@Controller
@Log4j2
public class ChatController {
    
    @GetMapping("/chat")
    public String chatGET(){

        log.info("@ChatController, chat GET()");
        
        return "chat";
    }
}

 

익명의 사용자가 localhost:8080/chat 으로 접속하게 되면

ChatController를 통해 아래 chat.html로 이동하게 됩니다.

 

chat.html

 

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
  <th:block th:fragment="content">

    <div class="container">
      <div class="col-6">
        <label><b>채팅방</b></label>

      </div>
      <div>
        <div id="msgArea" class="col"></div>
        <div class="col-6">
          <div class="input-group mb-3">
            <input type="text" id="msg" class="form-control" aria-label="Recipient's username" aria-describedby="button-addon2">
            <div class="input-group-append">
              <button class="btn btn-outline-secondary" type="button" id="button-send">전송</button>
            </div>
          </div>
        </div>
      </div>
    </div>

  </th:block>
</th:block>


<script th:inline="javascript">
            $(document).ready(function(){

            const username = (1 + Math.random()) * 0x10000

            $("#disconn").on("click", (e) => {
                disconnect();
            })

            $("#button-send").on("click", (e) => {
                send();
            });

            const websocket = new WebSocket("ws://localhost:8080/ws/chat");

            websocket.onmessage = onMessage;
            websocket.onopen = onOpen;
            websocket.onclose = onClose;

            function send(){

                let msg = document.getElementById("msg");

                console.log(username + ":" + msg.value);
                websocket.send(username + ":" + msg.value);
                msg.value = '';
            }

            //채팅창에서 나갔을 때
            function onClose(evt) {
                var str = username + ": 님이 방을 나가셨습니다.";
                websocket.send(str);
            }

            //채팅창에 들어왔을 때
            function onOpen(evt) {
                var str = username + ": 님이 입장하셨습니다.";
                websocket.send(str);
            }

            function onMessage(msg) {
                var data = msg.data;
                var sessionId = null;
                //데이터를 보낸 사람
                var message = null;
                var arr = data.split(":");

                for(var i=0; i<arr.length; i++){
                    console.log('arr[' + i + ']: ' + arr[i]);
                }

                var cur_session = username;

                //현재 세션에 로그인 한 사람
                console.log("cur_session : " + cur_session);
                sessionId = arr[0];
                message = arr[1];

                console.log("sessionID : " + sessionId);
                console.log("cur_session : " + cur_session);

                //로그인 한 클라이언트와 타 클라이언트를 분류하기 위함
                if(sessionId == cur_session){
                    var str = "<div class='col-6'>";
                    str += "<div class='alert alert-secondary'>";
                    str += "<b>" + sessionId + " : " + message + "</b>";
                    str += "</div></div>";
                    $("#msgArea").append(str);
                }
                else{
                    var str = "<div class='col-6'>";
                    str += "<div class='alert alert-warning'>";
                    str += "<b>" + sessionId + " : " + message + "</b>";
                    str += "</div></div>";
                    $("#msgArea").append(str);
                }
            }
            })
</script>
</html>

 

  • new WebSocket("ws://localhost:8080/ws/chat") : handshake를 한다. (위에서 우리는WebsocketConfig에서 handshake를 요청받을 준비를 완료해 놓은 상태이다.)
  • websocket.onoepn : handshake가 완료되고 connection이 맺어지면 실행된다.
  • websocket.send(string) : socket을 대상으로 문자열을 전송한다.
  • websocket.onmessage : socket에서 정보를 수신했을 때 실행된다

 

front에서 websocket을 이용하여 data를 계속 보낼 것이다.

이제는 서버에서 받은 data를 이용하여 처리해보자.

 

@Component
@Log4j2
public class ChatHandler extends TextWebSocketHandler {

    private static List<WebSocketSession> list = new ArrayList<>();

    @Override
    // 채팅을 전송 할 때 호출되는 메서드
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        log.info("payload : " + payload);

        for(WebSocketSession sess: list) {
            sess.sendMessage(message);
        }
    }

    /* Client가 접속 시 호출되는 메서드 */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        list.add(session);

        log.info(session + " 클라이언트 접속");
    }

    /* Client가 접속 해제 시 호출되는 메서드드 */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

        log.info(session + " 클라이언트 접속 해제");
        list.remove(session);
    }
}

 

ChatHandler는 WebSocketHandler의 구현체이다. 

WebSocketHandler는 다음 메서드를 가지고 있다.

  • afterConnectionEstablished(WebSocketSession) : connection이 맺어진 후 실행된다.
  • handleMessage(WebSocketSession, WebSocketMessage<?>) : session에서 메시지를 수신했을 때 실행된다.
    • message 타입에 따라 handleTextMessage(), handleBinaryMessage()를 실행한다.
  • afterConnectionClosed(WebSocketSession, CloseStatus) : close 이후 실행된다.

 

결과


Reference


https://dev-gorany.tistory.com/212

 

[Spring Boot] WebSocket과 채팅 (1)

일전에 WebSocket(웹소켓)과 SockJS를 사용해 Spring 프레임워크 환경에서 간단한 하나의 채팅방을 구현해본 적이 있다. [Spring MVC] Web Socket(웹 소켓)과 Chatting(채팅)  기존 공부 용도의 게시판(?)에 여러.

dev-gorany.tistory.com

 

https://supawer0728.github.io/2018/03/30/spring-websocket/

 

Spring WebSocket 소개

서론Web Browser에서 Request를 보내면 Server는 Response를 준다. HTTP 통신의 기본적인 동작 방식이다. 하지만 Server에서 Client로 특정 동작을 알려야 하는 상황도 있다. 예를 들어 Browser로 Facebook에 접속해

supawer0728.github.io

 

https://ko.javascript.info/websocket

 

웹소켓

 

ko.javascript.info