프로그램/JAVA

[JAVA] Spring Boot에서 WebSocket 구축 및 사용하기

프뚜 2022. 10. 27. 10:00
728x90
반응형
SMALL

안녕하세요!

프뚜(프로그래머 뚜)입니다!

 

[Brower > Server] 기존에 통신 방법의 HTTP는 사용자 요청(Client Request) > 서버 응답(Server Response)을 받으면 이어져있던 연결이 끊어집니다. 또한 항상 페이지 이동을 합니다.

 

[XMLHttpRequest > Server] XMLHttpRequest객체가 서버에 요청하는 방식으로 서버와의 연결을 잡아둡니다. Json이나 xml형태로 필요한 데이터만 주고 받을 수 있습니다. 조그만한 변경이 필요할 때, 해당 페이지 내에서 변경이 가능합니다.

 

[WebSocket]은 HTTP와 달리 Stateful Protocol이기 때문에 서버와 클라이언트 간의 연결을 항상 유지해야 하며 만약 비정상적으로 연결이 끊어졌을때 적절하게 대응해야 합니다. 이는 기존의 HTTP 사용시와 비교했을때 코딩의 복잡성을 가중시키는 요인이 될 수 있습니다.

 

Spring Boot에서 Websocket을 사용한 채팅을 구현해 보려고 합니다. WebSocket을 사용하기 위한 @ServerEndpoint Anntation이 선언 되어있는 클래스와 WebSocket에 관한 환경 설정 파일로 구현할 수 있습니다.


[개발 환경]

 - OS: windows 10 64bit

 - JAVA: v1.8


1. websocket dependency 추가하기


2. WebSocketConfig > Bean 생성하기

@Component
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        /*
            2022.10.26[프뚜]:
                Spring에서 Bean은 싱글톤으로 관리되지만,
                @ServerEndpoint 클래스는 WebSocket이 생성될 때마다 인스턴스가 생성되고
                JWA에 의해 관리되기 때문에 Spring의 @Autowired가 설정된 멤버들이 초기화 되지 않습니다.
                연결해주고 초기화해주는 클래스가 필요합니다.
         */
        return new ServerEndpointExporter();
    }

}


3. WebSocket 구현하기

@ServerEndpoint(value = "/chatt")
@Service
public class WebSocketChatting {

    private static Set<Session> CLIENTS = Collections.synchronizedSet(new HashSet<>());

    @OnOpen
    public void onOpen(Session session) {
        System.out.println(session.toString());

        if (CLIENTS.contains(session)) {
            System.out.println("[프뚜] 이미 연결된 세션입니다. > " + session);
        } else {
            CLIENTS.add(session);
            System.out.println("[프뚜] 새로운 세션입니다. > " + session);
        }
    }

    @OnClose
    public void onClose(Session session) throws Exception {
        CLIENTS.remove(session);
        System.out.println("[프뚜] 세션을 닫습니다. : " + session);
    }

    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        System.out.println("[프뚜] 입력된 메세지입니다. > " + message);

        for (Session client : CLIENTS) {
            System.out.println("[프뚜] 메세지를 전달합니다. > " + message);
            client.getBasicRemote().sendText(message);
        }
    }

}

@ServerEndpoint, @Service > WebSocketChatting 클래스를 서비스(WebSocket)로 등록합니다.

 

사용자를 관리할 수 있는 Set 전역변수를 선언합니다.

 

@OnOpen > 사용자가 접속하면 session을 추가합니다.

 

@OnClose > 사용자가 종료되면 session을 제거합니다.

 

@OnMessage > 사용자가 입력한 메세지를 받고 접속되어있는 사용자에게 메세지를 보냅니다.


4. webSocket과 연동되는 js 구현하기

let ws;
const mid = getId('mid');
const btnLogin = getId('btnLogin');
const btnSend = getId('btnSend');
const talk = getId('talk');
const msg = getId('msg');

// 2022.10.26[프뚜]: 전송 데이터(JSON)
const data = {};

function getId(id) {
    return document.getElementById(id);
}

btnLogin.onclick = function() {
    // 2022.10.26[프뚜]: 서버와 webSocket 연결 
    ws = new WebSocket("ws://" + location.host + "/chatt");

    // 2022.10.26[프뚜]: 서버에서 받은 메세지 처리
    ws.onmessage = function(msg) {
        const data = JSON.parse(msg.data);
        let css;

        if (data.mid === mid.value) {
            css = 'class=me';
        } else {
            css = 'class=other';
        }

        const item = `<div ${css} >
		                <span><b>${data.mid}</b></span> [ ${data.date} ]<br/>
                      <span>${data.msg}</span>
						</div>`;

        talk.innerHTML += item;

        // 2022.10.26[프뚜]: 스크롤바 하단으로 이동
        talk.scrollTop=talk.scrollHeight;
    }
}

msg.onkeyup = function(ev) {
    if (ev.keyCode === 13) {
        send();
    }
}

btnSend.onclick = function() {
    send();
}

function send() {
    if (msg.value.trim() !== '') {
        data.mid = getId('mid').value;
        data.msg = msg.value;
        data.date = new Date().toLocaleString();
        const temp = JSON.stringify(data);
        ws.send(temp);
    }
    
    msg.value = '';
}

@ServerEndpoint의 value값으로 연결합니다. 프뚜는 ws://localhost:8080/chatt로 되어있습니다.

 

Server에서 sendText를 받을 수 있는 function을 선언합니다.

 

사용자가 server에 메세지를 보내는 send function을 선언합니다.


5. 정상 작동 확인하기

프뚜와 먹뚜로 로그인을 시도합니다. > @OnOpen 메서드 호출

 

정상적으로 새로운 세션이 감지되었습니다.

 

안녕하세요 프뚜입니다.

안녕하세요 먹뚜입니다.

로 메세지를 주고 받았습니다. > @OnMessage 메서드 호출

 

입력된 메세지 > 2명의 사용자에게 메세지 전달됩니다.


전체적인 소스코드는 github에 공유되어있습니다.

(제목과 git history는 1:1 매칭입니다.)

 

https://github.com/JeongSeongSoo/spring-tistory.git

 

GitHub - JeongSeongSoo/spring-tistory

Contribute to JeongSeongSoo/spring-tistory development by creating an account on GitHub.

github.com

728x90
반응형
LIST