본문 바로가기

개발/BACK

[SpringFramework] Socket을 통한 스프링 채팅 기능 구현

728x90

스프링 환경에서 채팅 기능을 구현할 수 있다.

TCP/IP 프로토콜을 사용하여 서버와 통신하는 클라이언트 프로그램을 구현할 수 있는 Socket 통신을 이용할 것이다.


채팅 및 쪽지 관련 포스팅 예제

 

https://hdhdeveloper.tistory.com/38

 

[SpringFramework] WebSocket통신을 이용한 간단한 쪽지(메세지) 기능 구현 예제[1]

이번에는 저번에 Socket을 구현하여 1대1 및 다중 채팅 기능을 구현했다면, 이번에는 웹소켓을 이용해 다른 이에게 쪽지를 보내고, 확인할 수 있는 기능을 구현할 예정이다. 웹소캣은 http 프로토콜

hdhdeveloper.tistory.com

https://hdhdeveloper.tistory.com/39

 

[SpringFramework] WebSocket통신을 이용한 간단한 쪽지(메세지) 기능 구현 예제[1]

이번에는 저번에 Socket을 구현하여 1대1 및 다중 채팅 기능을 구현했다면, 이번에는 웹소켓을 이용해 다른 이에게 쪽지를 보내고, 확인할 수 있는 기능을 구현할 예정이다. 웹소캣은 http 프로토콜

hdhdeveloper.tistory.com


채팅을 구현하기 위해서는 Socket 통신의 방법부터 보자

 

  1. 클라이언트(요청자)가 호스트 이름 / IP 주소 및 포트 번호로 지정된 서버에 대한 연결 시작

     -테스트 과정에서 IP를 확인하여 진행한다.


  2. OutputStream을 사용하여 서버에 데이터 전송한다.


  3. InputStream을 사용하여 서버에서 데이터를 읽어서 데이터를 Output 해준다.
 

  4. 연결을 종료한다.

 

  연결을 종료하지 않으면, 사용자가 직접 끊을 때까지, 채팅이 지속된다.

 

  내가 테스트 했던 개발 환경이다.

  JDK 버젼 : 1.8

  SPRING VERSION : 4.3.4 RELEASE

 

먼저 SpringFramework 에서 Socket을 사용할 수 있도록 pom.xml 설정파일에 dependency를 추가하자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1.1</version>
        </dependency>
cs

 

해당 dependency를 추가해주면 자동으로 메이븐을 다운 받는다.

그 후, Socket 프로그래밍을 구현할 패키지를 하나 생성한다.

나의 패키지 구성이다.

 

첫번째로 진행할 것은

servlet-context.xml에 echo라는 요청이 들어왔을 때,

EchoHandler.java 에서 해당 요청을 처리하겠다는 bean 객체 설정이 필요하다.

 

servlet-context.xml 파일을 열어 다음과 같은 사항을 입력해주자.

 

가린 부분은 내 PC의 아이피 정보와 포트번호를 입력해주면 된다

IP정보로 접속했을 때의 요청도 처리하게끔 해주는 것이다

 

 

 

WebSocket 을 구성하기위해서 config 파일을 만들어서 WebSocket 구성을 해주면 된다

해당 WebSocketConfig.java 파일 소스코드이다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package common.socket.server;
 
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
 
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
    @Autowired
    private EchoHandler echoHandler;
 
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(echoHandler, "/echo")  .setAllowedOrigins("*")
        .withSockJS()
        .setClientLibraryUrl(
          "https://cdn.jsdelivr.net/sockjs/latest/sockjs.min.js")
        .setInterceptors(new HttpSessionHandshakeInterceptor());
    }
    
    
}
cs

ClientLibraryUrl에 SockJs CDN을 입력해줌으로써, SockJS를 사용하도록 설정한다.

 

 

다음은 EchoHandler.java 파일이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package common.socket.server;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
 
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
 
 
 
 
public class EchoHandler extends TextWebSocketHandler {
    
    
    private static List<WebSocketSession> sessionList = new ArrayList<WebSocketSession>();
    
    
    
//    Map<String,WebSocketSession> us ers = new HashMap<>();
    
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String user_name = searchUserName(session);
//        for(WebSocketSession sess : sessionList) {
//            sess.sendMessage(new TextMessage(user_name+"님이 접속했습니다."));
//        }
        sessionList.add(session);
    }
    
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String user_name= searchUserName(session);
 
        
//        //사용자가 접속중인지 아닌지
//        WebSocketSession chatwritingSession =users.get("user_name");
//        if(chatwritingSession != null) {
//            TextMessage textMessage = new TextMessage(user_name+ " 님이 메세지를 보냈습니다.");
//            chatwritingSession.sendMessage(textMessage);
//        }
        for(WebSocketSession sess: sessionList) {
            sess.sendMessage(new TextMessage(user_name+": "+message.getPayload()));
        }
    }
    
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String user_name = searchUserName(session);
        System.out.println("연결 끊어짐");
        for(WebSocketSession sess : sessionList) {
            sess.sendMessage(new TextMessage(user_name+"님의 연결이 끊어졌습니다."));
        }
        sessionList.remove(session);
    }
    
    public String searchUserName(WebSocketSession session)throws Exception {
        String user_name;
        Map<String, Object> map;
        map = session.getAttributes();
        user_name = (String) map.get("user_name");
        return user_name;
    }
}
cs

afterConnectionEstablished 메소드는 해당 IP포트로 클라이언트가 접속했을 때 실행되는 메소드다

handleTextMessage메소드는 클라이언트가 메세지를 보냈을 때, 나타나는 메소드다

afterConnectionClosed메소드는 연결이 끊어지면 실행되는 메소드이다

 

제일 하단 searchUserName 메소드는 세션 객체에 저장해둔 user_name을 가져와서 사용하려고 만든 메소드이다

 

 

--echo-ws.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<%@ page session="true" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<script src="../resources/js/sockjs.min.js"></script>
<html>
<head>
    <title>Home</title>
    <meta charset="UTF-8"/>
</head>
<body>
    <form id="chatForm">
        <div class="chat_start_main">
            상담 CHAT
        </div>
        <div class="chat_main" style="display:none;">
            <div class="modal-header" style="height:20%;">
                상담 CHAT 
            </div>
            <div class="modal-content" id="chat" style="height:60%;">
                
            </div>
            <div class="modal-footer">
                <input type="text" id="message" class="form-control" style="height:20%;" placeholder="메세지를 입력하세요"/>    
            </div>
        </div>
<!--         <button class="">send</button> -->
    </form>
    <script>
//전역변수 선언-모든 홈페이지에서 사용 할 수 있게 index에 저장
var socket = null;
$(document).ready(function(){
    if(!isEmpty($("#session_id").val()))
            connectWS();
});
    $(".chat_start_main").click(function(){
        $(this).css("display","none");
        $(".chat_main").css("display","inline");
    })
    $(".chat_main .modal-header").click(function(){
        $(".chat_start_main").css("display","inline");
        $(".chat_main").css("display","none");
    });
 
    function connectWS(){
        var sock = new SockJS("/echo");
            socket =sock;
        sock.onopen = function() {
               console.log('info: connection opened.');
        };
        sock.onmessage = function(e){
//             console.log(e);
//             var strArray = e.data.split(":");
//             if(e.data.indexof(":") > -1){
//                 $(".chat_start_main").text(strArray[0]+"님이 메세지를 보냈습니다.");
//             }
//             else{
//             }
            $("#chat").append(e.data + "<br/>");
        }
        sock.onclose = function(){
            $("#chat").append("연결 종료");
//              setTimeout(function(){conntectWs();} , 10000); 
        }
        sock.onerror = function (err) {console.log('Errors : ' , err);};
 
        $("#chatForm").submit(function(event){
            event.preventDefault();
                sock.send($("#message").val());
                $("#message").val('').focus();    
        });
    }
</script>
</body>
</html>
cs

<script src="../resources/js/sockjs.min.js"></script> 해당 javascript 파일은 아래 링크를 통해 다운로드 받을 수 있다.

github.com/sockjs/sockjs-client

 

sockjs/sockjs-client

WebSocket emulation - Javascript client. Contribute to sockjs/sockjs-client development by creating an account on GitHub.

github.com

 

아주 간단하게 Socket 통신 예제로 채팅 기능을 구현해봤다.

 

결과물은 이렇다.

상단에 보면 서로 다른 두개의 아이디로 접속해, 채팅을 하는 모습이다.

해당 기능을 테스트 하는 방법은 창을 두개로 띄워서 테스트를 진행해보면 될 것이다.

 

나는 echo-ws.jsp를 따로 페이지를 만들어 / 경로의 메인페이지에 include 하는 방식으로 구현했다.

메세지 옆 이름에는 아까 EchoHandler에서 session을 이용해 담아온 객체 user_name이 표기 된다.

 

728x90