[Spring] WebSocket을 이용한 1:1 채팅 / 실시간 알림 - [2]

리트리버J

·

2021. 1. 3. 22:55

728x90

먼저 전체 코드입니다.

백엔드 작업에 앞서 jsp 코드를 분석해보겠습니다.

 

< talk.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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- Noto-Sans 폰트-->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap" rel="stylesheet">
<!-- JUA 폰트-->
<link href="https://fonts.googleapis.com/css2?family=Jua&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="resources/css/talk.css">
<audio id='audio_play' src='resources/pop.mp3'></audio>
<script type="text/javascript"> 
function play() { 
    var audio = document.getElementById('audio_play'); 
    if (audio.paused) { 
        audio.play(); 
    }else
        audio.pause(); 
        audio.currentTime = 0 
    } 
</script>
</head>
<body>
    <!-- 채팅 아이콘 -->
    <div class="chatIcon font_jua">
        <img src="resources/img/chat-icon.png" class="iconImg">
    </div>
    <!-- 채팅 리스트 / 채팅 방 OPEN / CLOSE -->
    <script>
         $(document).on("click"".chatIcon"function(){                // 채팅 Icon 클릭 시,
            if($('.chatContainer').hasClass("display-none")){           // if ) 채팅방이 열려있지 않을 때,
                $('.chatListContainer').toggleClass('display-none');    // 리스트를 연다.
            }else{                                                      // else ) 채팅방이 열려있다면,
                $('.chatContainer').toggleClass('display-none');        // 채팅방을 닫는다.
                websocket.close();
            }
             
             if(!$('.chatListContainer').hasClass('display-none')){         // 채팅 리스트가 닫혀 있을 때
                getRoomList();                                            // 채팅 방 목록을 불러온다.
             }
         });
         
         $(document).on("click""img.close"function(){                // X 버튼 클릭 시,
             $('.chatContainer').toggleClass('display-none');           // 채팅방을 닫는다.
             websocket.close();                                            // socket 연결 종료
         });
         
         $(document).on("click""img.down"function(){                 // - 버튼 클릭 시,
             $('.chatContainer').toggleClass('display-none');           // 채팅방을 닫고,
             $('.chatListContainer').toggleClass('display-none');       // 리스트를 연다.
             websocket.close();                                            // socket 연결 종료
         });
    </script>
    <!-- 채팅 창 -->
    <div class="chatContainer display-none">
        <div class="chatTop">
            <div class="floatLeft" id="loginOn">
                <img class="profile_img" id="setPic"><!-- src 사진 경로 동적 생성 -->
            </div>
            <div class="name_container font_noto" id="setName"><!-- 이름 동적 생성 --></div>
            <div class="floatRight">
                <img src="resources/img/chat-close.png" class="btnImg close">
            </div>
            <div class="floatRight">
                <img src="resources/img/chat-minus.png" class="btnImg down">
            </div>
        </div>
        <div class="chatMiddle">
            <ul>
                <!-- 동적 생성 -->
            </ul>
        </div>
        <div class="chatBottom">
            <textarea placeholder="메세지를 입력해 주세요."></textarea>
        </div>
    </div>
    
    <!-- format -->
    <div class="chatMiddle format">
        <ul>
            <li>
                <div class="sender">
                    <span></span>
                </div>
                <div class="message">
                    <span></span>
                </div>
            </li>
        </ul>
    </div>
 
    <!-- 채팅 리스트 -->
    <div class="chatListContainer font_jua display-none">
        <div class="chatTop">
            <div style="padding: 10px; margin-left: 4px;">니즈톡 리스트</div>
        </div>
        <div class="chatList">
            <!-- 동적 생성 -->
        </div>
    </div>
    
    <!-- 채팅 목록 관련 -->
    <script>
        // 총 읽지 않은 갯수
        let countAll = 0;
        
        function getRoomList(){
            // 채팅 방 목록 가져오기
             $.ajax({
                url:"chatRoomList.do",
                data:{
                    userEmail:"${loginUser.email}"
                },
                dataType:"json",
                async:false// async : false를 줌으로써 비동기를 동기로 처리 할 수 있다.
                success:function(data){
                    
                    // 현재 로그인 된 User들
                    let loginList = "";
                      
                    // 로그인 된 User들을 가져온다.
                    $.ajax({
                        url:"chatSession.do",
                        data:{
                        },
                        async:false,
                        dataType:"json",
                        success:function(data){
                            for(var i = 0; i < data.length; i++){
                                loginList += data[i];
                            }
                        }
                    });
                      
                     $chatWrap = $(".chatList");
                    $chatWrap.html("");
                    
                    var $div;     // 1단계
                    var $img;     // 2단계
                    var $divs;     // 2단계
                    var $span;    // 2단계
                    
                    if(data.length > 0){
                        // 읽지 않은 메세지 초기화
                        countAll = 0;
                        
                        // 태그 동적 추가
                        for(var i in data){
                        
                            // 자신이 구매자 입장일 때
                            if(data[i].userEmail == "${loginUser.email}"){
                                // 현재 판매자가 로그인 상태 일 때
                                if(loginList.indexOf(data[i].masterEmail) != -1){
                                    $div = $("<div class='chatList_box enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].masterEmail);
                                }
                                // 현재 판매자가 로그아웃 상태 일 때
                                else{
                                    $div = $("<div class='chatList_box2 enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].masterEmail);
                                }
                                $img = $("<img class='profile_img'>").attr("src""resources/masterImg/" + data[i].masterPic);
                                $divs = $("<div class='userNameId'>").text(data[i].masterName);
                            }
                            // 자신이 판매자 입장일 때
                            else{                        
                                // 현재 구매자가 로그인 상태 일 때
                                if(loginList.indexOf(data[i].userEmail) != -1){
                                    $div = $("<div class='chatList_box enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].userEmail);
                                }
                                // 현재 구매자가 로그아웃 상태 일 때
                                else{
                                    $div = $("<div class='chatList_box2 enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].userEmail);
                                }                                
                                $img = $("<img class='profile_img'>").attr("src""resources/img/" + data[i].userPic);
                                $divs = $("<div class='userNameId'>").text(data[i].userName);
                            }
                            
                            // 읽지 않은 메세지가 0이 아닐 때
                            if(data[i].unReadCount != 0){
                                $span = $("<span class='notRead'>").text(data[i].unReadCount);
                            }else{
                                $span = $("<span>");
                            }
                            
                            $div.append($img);
                            $div.append($divs);
                            $div.append($span);
                            
                            $chatWrap.append($div);
                            
                            // String을 int로 바꿔주고 더해준다.
                            countAll += parseInt(data[i].unReadCount);
                        }
                    }
                }
            });
        }
        
        // 화면 로딩 된 후
        $(window).on('load'function(){
            
            // 2초에 한번씩 채팅 목록 불러오기(실시간 알림 전용)
            setInterval(function(){
                // 방 목록 불러오기
                getRoomList();
                
                // 읽지 않은 메세지 총 갯수가 0개가 아니면
                if(countAll != 0){
                    // 채팅 icon 깜빡거리기
                    $('.chatIcon').addClass('iconBlink');
                    play();
                }else{
                    // 깜빡거림 없애기
                    $('.chatIcon').removeClass('iconBlink');
                }
            },2000);
        });
    </script>
    
    <!-- 채팅 방 관련 -->
    <script>
        let roomId;
        // enter 키 이벤트
        $(document).on('keydown''div.chatBottom textarea'function(e){
             if(e.keyCode == 13 && !e.shiftKey) {
                 e.preventDefault(); // 엔터키가 입력되는 것을 막아준다.
                 const message = $(this).val();  // 현재 입력된 메세지를 담는다.
                   
                 let search3 = $('div.chatBottom textarea').val();
                  
                 if(search3.replace(/\s|  /gi, "").length == 0){
                       return false;
                       $('div.chatBottom textarea').focus();
                    }
                 
                 sendMessage(message);
                 // textarea 비우기
                 clearTextarea();
             }
        });
 
        // 채팅 방 클릭 시 방번호 배정 후 웹소켓 연결
        function enterRoom(obj){
            // 현재 html에 추가되었던 동적 태그 전부 지우기
            $('div.chatMiddle:not(.format) ul').html("");
            // obj(this)로 들어온 태그에서 id에 담긴 방번호 추출
            roomId = obj.getAttribute("id");
             // 해당 채팅 방의 메세지 목록 불러오기
              $.ajax({
                url:roomId + ".do",
                data:{
                    userEmail:"${loginUser.email}"
                },
                async:false,
                dataType:"json",
                success:function(data){
                    for(var i = 0; i < data.length; i++){
                        // 채팅 목록 동적 추가
                        CheckLR(data[i]);
                    }
                }
            });
             // 웹소켓 연결
             connect();
             console.log("enterRoom");
        }
        
        // 채팅 방 열어주기
        $(document).on("click"".enterRoomList",function(){
             $(".chatContainer").toggleClass("display-none");
             $(this).parent().parent().toggleClass("display-none");
             // 이름 추가
             $("#setName").html($(this).children('div').html());
             // 사진 추가
             $("#setPic").attr("src",$(this).children('img').attr('src'));
             // 스크롤바 아래 고정
            $('div.chatMiddle').scrollTop($('div.chatMiddle').prop('scrollHeight'));
             // 로그인 상태 일 때 
             if($(this).hasClass('chatList_box')){
                 // 점 표시
                $('#loginOn').addClass('profile_img_Container');
             }
             // 로그아웃 상태 일 때
             else{
                 // 점 빼기
                 $('#loginOn').removeClass('profile_img_Container');
             }
        });
        
        // 웹소켓
         let websocket;
     
         //입장 버튼을 눌렀을 때 호출되는 함수
         function connect() {
             // 웹소켓 주소
             var wsUri = "ws://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/websocket/echo.do";
             // 소켓 객체 생성
             websocket = new WebSocket(wsUri);
             //웹 소켓에 이벤트가 발생했을 때 호출될 함수 등록
             websocket.onopen = onOpen;
             websocket.onmessage = onMessage;
             console.log(websocket.send);
         }
         
         //웹 소켓에 연결되었을 때 호출될 함수
         function onOpen() {
             // ENTER-CHAT 이라는 메세지를 보내어, Java Map에 session 추가
             const data = {
                    "roomId" : roomId,
                    "name" : "${ loginUser.name }",
                    "email" : "${ loginUser.email }",
                 "message" : "ENTER-CHAT"
            };
            let jsonData = JSON.stringify(data);
             websocket.send(jsonData);
             console.log(websocket);
         }
         
        // * 1 메시지 전송
        function sendMessage(message){
            
            const data = {
                "roomId" : roomId,
                "name" : "${ loginUser.name }",
                "email" : "${ loginUser.email }",
                "message"   : message 
            };
              
            CheckLR(data);
             
            let jsonData = JSON.stringify(data);
             
             websocket.send(jsonData);
         }
        
         // * 2 메세지 수신
         function onMessage(evt) {
             
            let receive = evt.data.split(",");
             
            const data = {
                    "name" : receive[0],
                    "email" : receive[1],
                 "message" : receive[2]
            };
             
             if(data.email != "${ loginUser.email }"){
                CheckLR(data);
             }
        }
         
        // * 2-1 추가 된 것이 내가 보낸 것인지, 상대방이 보낸 것인지 확인하기
        function CheckLR(data) {
            // email이 loginSession의 email과 다르면 왼쪽, 같으면 오른쪽
            const LR = (data.email != "${ loginUser.email }") ? "left" : "right";
             // 메세지 추가
            appendMessageTag(LR, data.email, data.message, data.name);
        }
         
        // * 3 메세지 태그 append
        function appendMessageTag(LR_className, email, message, name) {
             
            const chatLi = createMessageTag(LR_className, email, message, name);
         
            $('div.chatMiddle:not(.format) ul').append(chatLi);
         
            // 스크롤바 아래 고정
            $('div.chatMiddle').scrollTop($('div.chatMiddle').prop('scrollHeight'));
        }
         
        // * 4 메세지 태그 생성
        function createMessageTag(LR_className, email, message, name) {
         
             // 형식 가져오기
             let chatLi = $('div.chatMiddle.format ul li').clone();
         
             chatLi.addClass(LR_className);              // left : right 클래스 추가
             // find() : chatLi의 하위 요소 찾기
             chatLi.find('.sender span').text(name);      // 이름 추가
             chatLi.find('.message span').text(message); // 메세지 추가
         
             return chatLi;
        };
         
        // * 5 - 채팅창 비우기
        function clearTextarea() {
             $('div.chatBottom textarea').val('');
             return false;
        };
    </script>
</body>
</html>
cs

 

가장 첫번째 script인 알람 audio 기능입니다.

<audio>태그를 이용하여

재생중이라면 정지,

정지중이라면 재생을 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
<audio id='audio_play' src='resources/pop.mp3'></audio>
<script type="text/javascript"> 
function play() { 
    var audio = document.getElementById('audio_play'); 
    if (audio.paused) { 
        audio.play(); 
    }else
        audio.pause(); 
        audio.currentTime = 0 
    } 
</script>
cs

 

$(window).on()을 사용하여 화면이 로딩 될때 ajax로 방 목록을 불러오고,

setInterval()을 통해 2초에 한번씩 불러옵니다.

읽지 않은 메세지가 있다면 1초짜리 audio가 재생됩니다.

setInterval()에 포함되어 있기 때문에 메세지를 다 읽기 전까지 알람이 울립니다.

 

두번째로 채팅 리스트 및 채팅방 OPEN / CLOSE 입니다.

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
    <!-- 채팅 리스트 / 채팅 방 OPEN / CLOSE -->
    <script>
         $(document).on("click"".chatIcon"function(){                // 채팅 Icon 클릭 시,
            if($('.chatContainer').hasClass("display-none")){           // if ) 채팅방이 열려있지 않을 때,
                $('.chatListContainer').toggleClass('display-none');    // 리스트를 연다.
            }else{                                                      // else ) 채팅방이 열려있다면,
                $('.chatContainer').toggleClass('display-none');        // 채팅방을 닫는다.
                websocket.close();
            }
             
             if(!$('.chatListContainer').hasClass('display-none')){         // 채팅 리스트가 닫혀 있을 때
                getRoomList();                                            // 채팅 방 목록을 불러온다.
             }
         });
         
         $(document).on("click""img.close"function(){                // X 버튼 클릭 시,
             $('.chatContainer').toggleClass('display-none');           // 채팅방을 닫는다.
             websocket.close();                                            // socket 연결 종료
         });
         
         $(document).on("click""img.down"function(){                 // - 버튼 클릭 시,
             $('.chatContainer').toggleClass('display-none');           // 채팅방을 닫고,
             $('.chatListContainer').toggleClass('display-none');       // 리스트를 연다.
             websocket.close();                                            // socket 연결 종료
         });
    </script>
cs

1. 채팅 ICON 클릭 시,

 

채팅방이 열려있지 않다면 채팅 리스트를 엽니다.

채팅방이 열려있다면 채팅방을 닫고, webSocket을 연결 해제합니다.

 

채팅 리스트가 열려있지 않다면, getRoomList()를 통하여 채팅 방 목록을 불러옵니다.

 

2. X 버튼 클릭 시,

 

채팅방을 닫고 webSocket 연결 해제합니다.

 

3. - 버튼 클릭 시,

채팅방을 닫고 채팅 리스트를 엽니다. 그리고 webSocket을 해제합니다.

 

*** 채팅방에 들어갈 때 webSocket에 연결됩니다!! ( 1:1 개인 채팅방 연결 )

 

채팅 ICON
채팅 리스트
채팅방

세번째로 채팅 목록 불러오기 입니다.

간단하게 ajax를 통하여 json 타입의 데이터형태로 들어오는 채팅 메세지를 분리하여,

 

로그인 상태의 유저와 비로그인 상태의 유저를 분리하고,

구매자 입장일 때와 판매자 입장일 때를 분리합니다.

 

또한 $변수명을 통하여 jQuery변수로 만들어 동적으로 태그들을 생성해줍니다.

 

특징으로는 async:false를 이용하여 비동기 → 동기처리를 했습니다. 

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<!-- 채팅 목록 관련 -->

        // 총 읽지 않은 갯수
        let countAll = 0;
        
        function getRoomList(){
            // 채팅 방 목록 가져오기
             $.ajax({
                url:"chatRoomList.do",
                data:{
                    userEmail:"${loginUser.email}"
                },
                dataType:"json",
                async:false// async : false를 줌으로써 비동기를 동기로 처리 할 수 있다.
                success:function(data){
                    
                    // 현재 로그인 된 User들
                    let loginList = "";
                      
                    // 로그인 된 User들을 가져온다.
                    $.ajax({
                        url:"chatSession.do",
                        data:{
                        },
                        async:false,
                        dataType:"json",
                        success:function(data){
                            for(var i = 0; i < data.length; i++){
                                loginList += data[i];
                            }
                        }
                    });
                      
                     $chatWrap = $(".chatList");
                    $chatWrap.html("");
                    
                    var $div;     // 1단계
                    var $img;     // 2단계
                    var $divs;     // 2단계
                    var $span;    // 2단계
                    
                    if(data.length > 0){
                        // 읽지 않은 메세지 초기화
                        countAll = 0;
                        
                        // 태그 동적 추가
                        for(var i in data){
                        
                            // 자신이 구매자 입장일 때
                            if(data[i].userEmail == "${loginUser.email}"){
                                // 현재 판매자가 로그인 상태 일 때
                                if(loginList.indexOf(data[i].masterEmail) != -1){
                                    $div = $("<div class='chatList_box enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].masterEmail);
                                }
                                // 현재 판매자가 로그아웃 상태 일 때
                                else{
                                    $div = $("<div class='chatList_box2 enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].masterEmail);
                                }
                                $img = $("<img class='profile_img'>").attr("src""resources/masterImg/" + data[i].masterPic);
                                $divs = $("<div class='userNameId'>").text(data[i].masterName);
                            }
                            // 자신이 판매자 입장일 때
                            else{                        
                                // 현재 구매자가 로그인 상태 일 때
                                if(loginList.indexOf(data[i].userEmail) != -1){
                                    $div = $("<div class='chatList_box enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].userEmail);
                                }
                                // 현재 구매자가 로그아웃 상태 일 때
                                else{
                                    $div = $("<div class='chatList_box2 enterRoomList' onclick='enterRoom(this);'>").attr("id",data[i].roomId).attr("email",data[i].userEmail);
                                }                                
                                $img = $("<img class='profile_img'>").attr("src""resources/img/" + data[i].userPic);
                                $divs = $("<div class='userNameId'>").text(data[i].userName);
                            }
                            
                            // 읽지 않은 메세지가 0이 아닐 때
                            if(data[i].unReadCount != 0){
                                $span = $("<span class='notRead'>").text(data[i].unReadCount);
                            }else{
                                $span = $("<span>");
                            }
                            
                            $div.append($img);
                            $div.append($divs);
                            $div.append($span);
                            
                            $chatWrap.append($div);
                            
                            // String을 int로 바꿔주고 더해준다.
                            countAll += parseInt(data[i].unReadCount);
                        }
                    }
                }
            });
        }
cs

 

728x90