뷰 소개

 

home.jsp 는 수정된 부분이 없습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<h1>대화방</h1>
<h2>사용중인 id는 "${my_id}" 입니다.</h2>
<div id="chatLog">
<%
    List<chatLog> list = (List<chatLog>) request.getAttribute("list");
        for(chatLog l : list){
        pageContext.setAttribute("l", l);%>
        <p>
        ${l.userId}: ${l.message}
        <c:if test="${l.unread != 0}"><span class="ids" id="${l.chatId}" style="color: green">${l.unread}</span></c:if>
        <c:if test="${l.unread == 0}"><span class="ids" id="${l.chatId}" style="display: none">${l.unread}</span></c:if>
        </p>    
<%} %>
    
</div>
<textarea id="textarea"></textarea>
<input type="button" value="전송" onclick="seqCnctingSaveSend()">
</body>
cs

room.jsp의 <body>태그 부분입니다.

room 컨트롤러에서 모델로 받은 list 변수를 사용합니다.

list 변수에는 접속한 방의 메시지가 저장돼있습니다.

 

메시지 구조는

<p>

메시지 작성자:  메시지

<span class="ids" id="메시지-id">안 읽은 사람의 수</span>

</p>

입니다.

 

class, id 속성은 비실시간 -> 실시간 대화를 위해 사용합니다.

 

유저가 메시지 읽는 상황은 3가지 입니다.

 

1) a 와 b 의 실시간 대화

2) a 와 b 의 비실시간 대화

3) a 와 b의 비실시간 -> 실시간 대화

 

1) a 와 b 의 실시간 대화 란?

둘이 오프라인에서 대화하듯, 온라인에서 동일한 시간에 메시지를 주고받는 상황

 

2) a 와 b 의 비실시간 대화

 

a가 메시지를 보내고, 방을 나간후, b가 방에 들어와서 메시지를 읽는 상황

혹은 b가 메시지를 보내고, 방을 나간후, a가 방에 들어와서 메시지를 읽는 상황

 

3) a 와 b의 비실시간 -> 실시간 대화

 

a가 메시지를 보내고, 아직 방에서 나가지 않은 상태에서, b가 방에 들어와서 메시지를 읽는 상황

(상대방이 칼답, 즉답, 메시지를 보내자마자 바로 읽는 경우 )

 

 

1,2번 상황이 class, id 속성이 필요하지 않은 이유

 

메시지를 화면에 띄울때 표시되는 읽음 표시 숫자는

현재 방에 접속한 사람의 수를 계산해서 결정됩니다.

 

1번 상황의 경우는 모두 방에 접속하였기 때문에, 

현재 방에 접속한 사람의 수는 2입니다.

2를 0으로 바꾸어 화면에 띄웁니다.

 

2번 상황의 경우는 한명만 방에 접속하였기 때문에

현재 방에 접속한 사람의 수는 1입니다.

1은 그대로 화면에 띄웁니다.

 

3번 상황이 class, id 속성이 필요한 이유

1,2번 상황은 접속한 사람의 수를 계산해서 결정됩니다.

그리고 이후에 변동사항이 없습니다.

++) 2번의 경우 상대방이 메시지를 읽으면 읽음 처리 숫자를 -1을 합니다.

그러나, 내가 나중에 방에 들어가서 메시지를 읽을때는 db에서 -1이된 uread칼럼을 가저오기때문에

내가 방에 들어갔을때는 자동으로 변동사항이 반영되어있습니다.

 

하지만 3번의 경우는 a가 메시지를 보내면, a 혼자 방에 있기 때문에, 일단 읽음 처리 숫자가 1 입니다.

그리고 a가 나가지않은 상태에서 b가 방에들어와서 메시지를 읽습니다.

그러면 a는 b가 메시지를 읽은 것을

바로 확인 할 수 있어야 합니다.

 

 2번 상황처럼  db에서 -1이된 uread칼럼을 가저오려면 새로고침을 해야합니다.

하지만, 사용자가 새로고침없이 바로 확인 할수 있어야합니다.

이런 상황을 위해 b는 방에 들어올때 안읽은 메시지가 있는지 확인하고

안읽은 메시지를 특정해서 a에게 알려주어야합니다.

그러므로 id속성에 있는 채팅id를 sock.send로 a에게 보냅니다. 

그리고 a는 전달받은 채팅id로 b가 방금 읽은 메시지를 특정합니다.

그러면 내 화면에서도 그 메시지의 읽음 처리 숫자를 -1해서 0으로 바꿀 수 있습니다.

 

++)  중요★

웹소켓으로 표현된 메시지(chatlog 테이블에서 가져오지 않은)

와 db로 표현된 메시지 (chatlog 테이블에서 가져온)의 구조가 같아야합니다.

 

이제 js부분을 소개하겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function seqCnctingSaveSend(){
    var size;
    var seq;
    var data = new FormData();
    data.append("roomId", roomId);
    
    fetch('getChatSeq', {     
        method: 'POST',
        headers: {},  
    }).then(res => res.json())
    .then(res => {seq = res;})
    .then(res =>
        fetch('getCncting', {     
            method: 'POST',
            headers: {},  
            body: data
        }).then(res => res.json())
          .then(res => {if(res == 2){size = 0}else(size = 1)} )
    )
    .then(res => saveMessage(size, seq))
    .then(res => sendMessage(size, seq));
        
}
cs

웹소켓 메시지와 db메시지 구조가 같아야하기 때문에,

saveMessage 함수와 sendMessage 함수는 같은 매개변수를 사용합니다.

if(res == 2){size = 0}은 1번 상황을 위해 사용됩니다.

else(size = 1)는 2번 상황을 위해 사용됩니다.

 

++) then이 잘 작동하려면 res => 를 꼭 써야합니다.

혹은 함수식으로 response인자를 넣어야 합니다.

res => 다음에 오는 중괄호{} 는 if절, 대입 연산자가 있으면

꼭 사용해야합니다.

then(res => res.json())후에 오는

then(res => )에서 res는 res.json() 입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function saveMessage(size, seq){
    var data = new FormData();
    var textarea = document.getElementById("textarea");
    var myMessage = textarea.value;
    
    data.append("message", myMessage);
    data.append("roomId", roomId);
    data.append("chat_id", seq);
    data.append("size", size);
 
    fetch('saveMessage', {     
        method: 'POST',
        headers: {},
        body: data       
    });
}
cs

saveMessage 함수 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void saveMessage(String userid, String message, String roomId, int chatId, int size) {
        String sql = "insert into chatLog(user_id, message, room_id, unread, who_read, chat_id, regdate) values(?, ?, ?, ?, ?, ?, sysdate)";
        
        try {
            conn = dao.getConnection();
            PreparedStatement st = conn.prepareStatement(sql);
            st.setString(1, userid);
            st.setString(2, message);
            st.setString(3, roomId);
            st.setInt(4, size);
            st.setString(5, userid);
            st.setInt(6, chatId);
            st.executeUpdate();
        
            st.close();
            conn.close();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
cs

service 클래스의 saveMessage 메소드 입니다.

중요한점은 who_read 칼럼에 작성자 이름을 남기면서, 메시지를 저장합니다.

 

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
var chatLog = document.getElementById("chatLog");     
        var content = JSON.parse(e.data);
        var message = content.message;
        var type = content.type;
        var chatId = content.chatId;
        var writer = content.writer;
        var size = content.size;
        var data = new FormData();
        data.append("roomId", roomId);
        
        if(type == "JOIN"){
            var chat_ids = document.getElementsByClassName("ids");
            
            fetch('getWhoRead', {     
                method: 'POST',
                headers: {},
                body: data       
            }).then(res => res.json())
            .then(whos => {
                 for(var i=0; i<chat_ids.length; i++){        
                     if(!(whos[i].whoRead).includes(my_id)){  
                         if(parseInt(chat_ids[i].innerHTML) == 0)
                             continue;
                         chat_ids[i].innerHTML = parseInt(chat_ids[i].innerHTML) - 1;
                         chat_ids[i].innerHTML = "";
                        /*방에 접속해있는 모든 사람들(나 포함)에게 내가 이 채팅을 읽지 않았다는것을 보내준다. 그리고 if(type =="READ")에서 나를 제외한 사람들의 읽음 표시를 -1 한다  */
                        sock.send(JSON.stringify({chatRoomId: roomId, type: "READ", chatId: chat_ids[i].id, writer: my_id}));                  
                        decreaseUnread(chat_ids[i].id);                  
                        leaveId(chat_ids[i].id);
                    }
                 }                  
            })
            
        }
cs

 sock.onmessage 부분의  if(type == "JOIN") 부분입니다.

 

getWhoRead 클래스에서 chatlog테이블의 who_read 칼럼 리스트(접속해있는 방)를 가져옵니다.

getWhoRead 클래스는 Java파일에서는 List를 반환(return)합니다.

ajax로 이 List를 받게되면 문자열로 표현된 배열의 인덱스마다 객체(js object)가 넣어져서 옵니다.

현재 접속한 방의 첫번째 메시지부터 끝까지, who_read 칼럼에 내 이름이 포함되어있는지 확인합니다.

포함되어있지 않다면, 내가 메시지를 안 읽었던 것 이므로, 내화면에서 읽음 처리 숫자를 -1합니다.

내가 메시지를 읽었다는 것을 상대방에게 알리기위해

안읽은 메시지 수만큼 sock,send(type: "READ", chatId: chat_ids[i].id)를 합니다.

그리고 db에 unread 칼럼을 -1합니다.

who_read 칼럼에 내 id를 남깁니다.

 

++) 읽음 처리 숫자가 0이면, 메시지 안 읽음 검사를 건너뜁니다.

1
2
3
4
5
6
7
8
if(type == "READ"){
            if(!(writer == my_id)){
                 var chat_element = document.getElementById(chatId);
                   chat_element.innerHTML = parseInt(chat_element.innerHTML) - 1;
                if(parseInt(chat_element.innerHTML) == 0)
                    chat_element.innerHTML = "";
            }
        }    
cs

 sock.onmessage 부분의  if(type == "READ") 부분입니다.

sock,send(type: "READ")는 나를 포함해서 방에 접속해있는 모두에게 메시지를 전달합니다.

그러므로 메시지 전송자가 나면 코드가 실행되지 않게합니다.

코드가 실행되면, 전달받은 chatId로 상대방의 읽지않은 메시지를 특정해서 읽음처리 숫자를 -1합니다.

 

github.com/burnaby033/multiRoomV2

 

burnaby033/multiRoomV2

스프링 다중채팅방, 메신저 읽음 표시, 1 사라짐. Contribute to burnaby033/multiRoomV2 development by creating an account on GitHub.

github.com

프로젝트 다운 주소입니다.

+ Recent posts