[Spring / WebSocket] Java Websocket을 이용한 1:1 채팅방, 단체 채팅방 만들기 - 2 [VO / Service / DAO / Mapper]

리트리버J

·

2021. 5. 25. 10:01

728x90

채팅에 관한 문의사항이 많아져 추가로 VO / Service / DAO를 공유하며 상세한 설명을 덧붙여 보겠습니다.

사실 HandShake를 사용하지 않은 코드라, 완벽하진 않아서

다시 채팅부분만 그대로 붙여 쓸 수 있게 Git과 게시글을 준비중에 있는데

일단 급하신 분들을 위해 작성해보겠습니다.

 

※ 패키지가 다르기 때문에 그대로 복붙하시면 오류가 날 수 있습니다.

꼭 코드를 직접 한줄한줄 파악하시면서 작성해 주시길 바랍니다.

 

1. ChatSession : 

현재 로그인 되어 있는 유저들을 저장하는 VO

DB 저장 X / 온라인 오프라인 확인용

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
import java.util.ArrayList;
 
import org.springframework.stereotype.Component;
 
@Component("cSession")
public class ChatSession {
    
    // static으로 필드변수를 설정하여 같은 ArrayList를 이용 할 수 있도록 선언합니다.
    private static ArrayList<String> loginUser = new ArrayList<String>();
    
    // 로그인 시 ArrayList에 추가합니다.
    public void addLoginUser(String email) {
        loginUser.add(email);
    }
    
    // 로그아웃 시 ArrayList에서 제거합니다.
    public void removeLoginUser(String email) {
        loginUser.remove(email);
    }
 
    // 현재 로그인 되어 있는 유저 목록을 가져옵니다.
    public static ArrayList<String> getLoginUser() {
        return loginUser;
    }
 
    // 자동 setter. 사용하진 않았습니다.
    public static void setLoginUser(ArrayList<String> loginUser) {
        ChatSession.loginUser = loginUser;
    }
}
cs

 

 

 

2. ChatRoom :

채팅방 VO

방번호와 사용자 - 상대방의 정보가 담겨있다.

roomId로 채팅방을 구분하며,

unReadCount를 통해 읽었는지 파악 할 수 있게 해준다.

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
package com.fp.neezit.chat.model.vo;
 
public class ChatRoom {
    
    private String roomId;        // 방 번호
    
    private String userEmail;    // 사용자 이메일
    private String userName;    // 사용자 이름
    private String userPic;        // 사용자 사진
    
    private String masterEmail; // 상대방 이메일
    private String masterName;    // 상대방 이름
    private String masterPic;    // 상대방 사진
    
    private int unReadCount;    // 안 읽은 메세지 수
    
    public ChatRoom() {
        super();
    }
 
    public ChatRoom(String roomId, String userEmail, String userName, String userPic, String masterEmail,
            String masterName, String masterPic, int unReadCount) {
        super();
        this.roomId = roomId;
        this.userEmail = userEmail;
        this.userName = userName;
        this.userPic = userPic;
        this.masterEmail = masterEmail;
        this.masterName = masterName;
        this.masterPic = masterPic;
        this.unReadCount = unReadCount;
    }
 
    public String getRoomId() {
        return roomId;
    }
 
    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }
 
    public String getUserEmail() {
        return userEmail;
    }
 
    public void setUserEmail(String userEmail) {
        this.userEmail = userEmail;
    }
 
    public String getUserName() {
        return userName;
    }
 
    public void setUserName(String userName) {
        this.userName = userName;
    }
 
    public String getUserPic() {
        return userPic;
    }
 
    public void setUserPic(String userPic) {
        this.userPic = userPic;
    }
 
    public String getMasterEmail() {
        return masterEmail;
    }
 
    public void setMasterEmail(String masterEmail) {
        this.masterEmail = masterEmail;
    }
 
    public String getMasterName() {
        return masterName;
    }
 
    public void setMasterName(String masterName) {
        this.masterName = masterName;
    }
 
    public String getMasterPic() {
        return masterPic;
    }
 
    public void setMasterPic(String masterPic) {
        this.masterPic = masterPic;
    }
 
    public int getUnReadCount() {
        return unReadCount;
    }
 
    public void setUnReadCount(int unReadCount) {
        this.unReadCount = unReadCount;
    }
 
    @Override
    public String toString() {
        return "ChatRoom [roomId=" + roomId + ", userEmail=" + userEmail + ", userName=" + userName + ", userPic="
                + userPic + ", masterEmail=" + masterEmail + ", masterName=" + masterName + ", masterPic=" + masterPic
                + ", unReadCount=" + unReadCount + "]";
    }
}
cs

3. ChatMessage : 

본격적인 메세지 VO

방번호와 메세지번호 PK

 

sessionCount는 현재 1:1 채팅방에 들어와있는 유저 수이다.

sessionCount가 2이면 unReadCount는 0으로 DB에 집어넣을 수 있겠다.

[ => 2명이 들어와있으니 읽지않은 메세지는 없다.]

sessionCount가 1잉면 unReadCount는 1이 되는 것이다.

 

※ 이전 게시글의 WebSocketHandler에 나와 있다.

 

 

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
package com.fp.neezit.chat.model.vo;
 
public class ChatMessage {
    
    private String roomId;            // 방 번호
    private String messageId;        // 메세지 번호
    private String message;            // 메세지 내용
    private String name;            // 보낸이 이름
    private String email;            // 보낸이 이메일
    private int unReadCount;        // 안 읽은 메세지 수
    private int sessionCount;        // 현재 세션 수
    
    public ChatMessage() {
        super();
    }
 
    public ChatMessage(String roomId, String messageId, String message, String name, String email, int unReadCount,
            int sessionCount) {
        super();
        this.roomId = roomId;
        this.messageId = messageId;
        this.message = message;
        this.name = name;
        this.email = email;
        this.unReadCount = unReadCount;
        this.sessionCount = sessionCount;
    }
 
    public String getRoomId() {
        return roomId;
    }
 
    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }
 
    public String getMessageId() {
        return messageId;
    }
 
    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public int getUnReadCount() {
        return unReadCount;
    }
 
    public void setUnReadCount(int unReadCount) {
        this.unReadCount = unReadCount;
    }
 
    public int getSessionCount() {
        return sessionCount;
    }
 
    public void setSessionCount(int sessionCount) {
        this.sessionCount = sessionCount;
    }
 
    @Override
    public String toString() {
        return "ChatMessage [roomId=" + roomId + ", messageId=" + messageId + ", message=" + message + ", name=" + name
                + ", email=" + email + ", unReadCount=" + unReadCount + ", sessionCount=" + sessionCount + "]";
    }
}
cs

 

4. 로그인 시 ChatSession에 추가해주기.

다른 나머지 로직들은 다 제거하고, 채팅에 관한 로그인 로직만 작성했으니

자신의 코드에 추가하면 됩니다.

 

사실 HandShake-Interceptor를 통해 Handler에서 전부 처리 할 수 있지만,

현재 코드는 HandShake기법을 사용하지 않았으므로 로그인 Controller에 따로 구성했습니다.

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
@SessionAttributes({"loginUser","master2","rankPic","admin"}) // Model에 loginUser라는 키값으로 객체가 추가되면 자동으로 세션에 추가하라는 의미의 어노테이션
@Controller
public class UserContoller {
    @Autowired
    private UserService uService;
    /* 채팅 */
    @Autowired
    private ChatSession cSession;
    /**
     * 1. 로그인 세션 메소드 ( 암호화 처리 )
     * 
     * @param u
     * @param model
     * @return
     */
    @RequestMapping(value = "login.do", method = {RequestMethod.GET, RequestMethod.POST})
    public String userLogin(User u, Model model, HttpServletRequest request) { // view에 전달하는 데이터를 Model에 담는다.
        
            User loginUser = uService.loginUser(u);
            /* 채팅 */
            // 현재 로그인 한 User 채팅 Session ArrayList에 추가.
            cSession.addLoginUser(loginUser.getEmail());
    }
    /**
     * 2. 로그아웃 세션 메소드 (@SessionAttributes가 있기 때문에 session.invalidate()가 먹히지 않으므로)
     * 
     * @param status
     * @return
     */
    @RequestMapping("logout.do")
    public String logout(HttpSession session) {
        
        /* 채팅 */
        User u = (User)session.getAttribute("loginUser");
                
        /* 채팅 */
        // 로그아웃한 User를 채팅 Session ArrayList에서 삭제.
        cSession.removeLoginUser(u.getEmail());
    }
cs

 

5. Service

Service는 interface이기 때문에 따로 Impl을 작성해주셔야 합니다.

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 com.fp.neezit.chat.model.service;
 
import java.util.List;
 
import com.fp.neezit.chat.model.vo.ChatMessage;
import com.fp.neezit.chat.model.vo.ChatRoom;
 
public interface ChatService {
 
    /**
     * 방 번호를 선택하는 메소드
     * @param roomId
     * @return
     */
    ChatRoom selectChatRoom(String roomId);
 
    /**
     * 채팅 메세지 DB 저장
     * @param chatMessage
     * @return 
     */
    int insertMessage(ChatMessage chatMessage);
 
    /**
     * 메세지 내용 리스트 출력
     * @param roomId
     * @return
     */
    List<ChatMessage> messageList(String roomId);
 
    /**
     * 채팅 방 DB 저장
     * @param room
     * @return
     */
    int createChat(ChatRoom room);
 
    /**
     * DB에 채팅 방이 있는지 확인
     * @param room
     * @return
     */
    ChatRoom searchChatRoom(ChatRoom room);
 
    /**
     * 채팅 방 리스트 출력
     * @param userEmail
     * @return
     */
    List<ChatRoom> chatRoomList(String userEmail);
 
    /**
     * 채팅 읽지 않은 메세지 수 출력
     * @param message
     * @return
     */
    int selectUnReadCount(ChatMessage message);
 
    /**
     * 읽은 메세지 숫자 0으로 바꾸기
     * @param message
     * @return
     */
    int updateCount(ChatMessage message);
 
}
cs

6. DAO

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
package com.fp.neezit.chat.model.dao;
 
import java.util.List;
 
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
 
import com.fp.neezit.chat.model.vo.ChatMessage;
import com.fp.neezit.chat.model.vo.ChatRoom;
 
@Repository
public class ChatDao{    
    
    @Autowired
    SqlSessionTemplate sqlSession;
 
    public ChatRoom selectChatRoom(String roomId) {
        return sqlSession.selectOne("chatMapper.selectChatRoom", roomId);
    }
    
    public int insertMessage(ChatMessage chatMessage) {
        return sqlSession.insert("chatMapper.insertMessage", chatMessage);
    }
    
    public List<ChatMessage> messageList(String roomId) {
        return sqlSession.selectList("chatMapper.messageList", roomId);
    }
 
    public int createChat(ChatRoom room) {
        return sqlSession.insert("chatMapper.createChat", room);
    }
 
    public ChatRoom searchChatRoom(ChatRoom room) {
        return sqlSession.selectOne("chatMapper.searchChatRoom", room);
    }
 
    public List<ChatRoom> chatRoomList(String userEmail) {
        return sqlSession.selectList("chatMapper.chatRoomList", userEmail);
    }
 
    public int selectUnReadCount(ChatMessage message) {
        return sqlSession.selectOne("chatMapper.selectUnReadCount",message);
    }
 
    public int updateCount(ChatMessage message) {
        return sqlSession.update("chatMapper.updateCount", message);
    };
 
}
cs

 

7. Mapper.xml

 

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="chatMapper">
 
    <resultMap type="ChatRoom" id="chatRoomResultSet">
        <id property="roomId" column="ROOM_ID" />
        <result property="userEmail" column="USER_EMAIL" />
        <result property="userName" column="USER_NAME" />        
        <result property="userPic" column="USER_PIC" />
        <result property="masterEmail" column="MASTER_EMAIL" />
        <result property="masterName" column="MASTER_NAME" />
        <result property="masterPic" column="MASTER_PIC" />
    </resultMap>
 
    <resultMap type="ChatMessage" id="chatMessageResultSet">
        <id property="messageId" column="MESSAGE_ID" />
        <result property="roomId" column="ROOM_ID" />
        <result property="message" column="MESSAGE_CONTENT" />
        <result property="name" column="USER_NAME" />
        <result property="email" column="USER_EMAIL" />
        <result property="unReadCount" column="UNREAD_COUNT" />
    </resultMap>
    
    <!-- 신규 채팅방일 때 채팅 방 생성 -->
    <insert id="createChat" parameterType="ChatRoom">
        insert into CHATROOM values(SEQ_CHATROOM_ID.NEXTVAL, #{userEmail}, #{userName}, DEFAULT, #{masterEmail}, #{masterName}, #{masterPic})
    </insert>
 
    <select id="selectChatRoom" parameterType="string" resultMap="chatRoomResultSet">
        SELECT * FROM CHATROOM 
        WHERE ROOM_ID = #{roomId} 
    </select>
    
    <insert id="insertMessage" parameterType="ChatMessage">
        <if test="sessionCount == 1">
        INSERT INTO CHATMESSAGE VALUES(#{roomId}, SEQ_CHATMESSAGE_ID.NEXTVAL, #{message}, #{name}, #{email}, DEFAULT)
        </if>
        <if test="sessionCount == 2">
        INSERT INTO CHATMESSAGE VALUES(#{roomId}, SEQ_CHATMESSAGE_ID.NEXTVAL, #{message}, #{name}, #{email}, 0)
        </if>
    </insert>
    
    <select id="messageList" parameterType="string" resultMap="chatMessageResultSet">
        SELECT * FROM CHATMESSAGE
        WHERE ROOM_ID = #{rooomId}
    </select>
    
    <select id="searchChatRoom" parameterType="ChatRoom" resultMap="chatRoomResultSet">
        SELECT * FROM CHATROOM
        WHERE USER_EMAIL = #{userEmail} AND MASTER_EMAIL = #{masterEmail}
    </select>
    
    <select id="chatRoomList" parameterType="string" resultMap="chatRoomResultSet">
        SELECT * FROM CHATROOM
        WHERE USER_EMAIL = #{userEmail} OR MASTER_EMAIL = #{userEmail}
    </select>
    
    <select id="selectUnReadCount" parameterType="ChatMessage" resultType="_int">
        SELECT COUNT(*) FROM CHATMESSAGE
        WHERE ROOM_ID = #{roomId} AND USER_EMAIL != #{email} AND UNREAD_COUNT = 1
    </select>
    
    <update id="updateCount" parameterType="ChatMessage">
        UPDATE CHATMESSAGE
        SET UNREAD_COUNT = 0
        WHERE ROOM_ID = #{roomId} AND USER_EMAIL != #{email} AND UNREAD_COUNT = 1
    </update>
</mapper>
cs

 

끝으로, 다시한번 HandShake-Interceptor를 사용하지 않은 코드임을 알려드리며,

현재 채팅 부분만 프로젝트에 붙여 쓰실 수 있도록 

 

업그레이드 된 채팅 기능을 GitHub 공유와 게시글 준비중에 있습니다.

 

급하신 분들은 이번 코드를 보시며 참고하시길 바라며,

이번주 내로 깔끔한 코드를 작성해 공유해드리겠습니다.

728x90