본문 바로가기
개발/Spring

[SpringBoot] WebSocket 채팅 테스트

by 코딩하는 흰둥이 2024. 6. 9.
반응형
dependency - pom
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

 테스트용으로 생성하며 thymeleaf , web , websocket , devtools 만 추가

 

 

application.properties
server.port=7181

#### thymeleaf
spring.thymeleaf.prefix=classpath:templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.cache=false
spring.thymeleaf.order=0

port 및 thymeleaf 설정

templates 폴더 아래 html을 읽어온다

 

html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>

<body>
<div>
    <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="send">전송</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>


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

        const username = [[${ip}]];
        $("#send").on("click", (e) => {
            let msg = document.getElementById("msg");
            websocket.send(username + ":" + msg.value);
            msg.value = '';
        });

	// test로 localhost로 진행함, ip 지정해서 사용하면 됨
        const websocket = new WebSocket("ws://localhost:7181/ws/test");
        websocket.onmessage = onMessage;
        websocket.onopen = onOpen;
        // websocket.onclose = onClose;

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

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

        function onMessage(msg) {
            let data = msg.data;
            let sessionId = null;


            let message = null;
            let arr = data.split(":");

            let cur_session = username;

            //현재 로그인 한 사람
            sessionId = arr[0];

            // arr[1] 은 메세지
            if(arr.length > 1){
                message = arr[1];
            }

            // 자신과 다른 client를 구분해서 메세지 출력 
            if(sessionId == cur_session && message != null){
                let str = "<div class='col-6'>";
                str += "<div style='text-align: right'>";
                str += "<b>" + sessionId + " : " + message + "</b>";
                str += "</div></div>";
                $("#msgArea").append(str);
            }
            else if(sessionId != cur_session && message != null){
                let str = "<div class='col-6'>";
                str += "<div style='text-align: left'>";
                str += "<b>" + sessionId + " : " + message + "</b>";
                str += "</div></div>";
                $("#msgArea").append(str);
            }
            else {
                let str = "<div class='col-6'>";
                str += "<div style='background-color: darksalmon'>";
                str += "<b>" + sessionId + "</b>";
                str += "</div></div>";
                $("#msgArea").append(str);
            }
        }
    })
</script>
</body>
</html>

 

bootstrap 과 jquery를 cdn 으로 사용 중 

 

WebSocketHandler
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.ArrayList;
import java.util.List;

@Component
public class WebSocketHandler extends TextWebSocketHandler {

    // 연결된 client 웹소켓 세션 정보를 담기 위함
    private static List<WebSocketSession> list = new ArrayList<>();

    protected void handleTextMessage(WebSocketSession session , TextMessage message) throws Exception{
        String payload = message.getPayload();
        System.err.println("payload : " + payload);

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

    // client 접속
    public void afterConnectionEstablished(WebSocketSession session) throws Exception{
        list.add(session);
        System.err.println(session + " client 접속");
    }

    // client 접속 해제
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception{
        list.remove(session);
        System.err.println(session + "client 접속 해제");

        // 접속해있는 client들에게 메세지 전송
        for(WebSocketSession sess : list){
            sess.sendMessage(new TextMessage("상대방이 퇴장하였습니다."));
        }
    }
}

 

WebSocketConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;


@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private final WebSocketHandler webSocketHandler;

    public WebSocketConfig(WebSocketHandler webSocketHandler){
        this.webSocketHandler = webSocketHandler;
    }

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

 

 

Controller
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class TestController {

    @GetMapping("/")
    public String index(Model model) throws Exception{

        // 임의의 숫자 ID
        // 로그인 및 이름이나 ID를 지정하는 기능이 없어서 현재 접속하는 Client에게 1~100 사이의 무작위 숫자를 부여
        int a = (int)(Math.random() * 100) + 1;
        model.addAttribute("ip" , a);

        return "index";
    }
}

 

 

 

프로젝트 폴더 구조

 

 

 

테스트

 

 

 

 

 

댓글