고래씌
[Spring] 10-1. 채팅방 목록, 채팅방 생성 본문
1. 채팅방 서비스 하기 위한 세팅
- 메이븐 레파지토리에서 복사
=> 복붙 후 일단 버전은 지워주고 ${org.springframework-version} 작성(pom.xml 맨 위에 가면 properties 에서 키값 복사)
이것은 웹소켓의 자료파일 묶음임!
▶ pom.xml
<!-- 웹소켓 의존성 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-websocket -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${org.springframework-version}</version>
</dependency>
▶ servlet-context.xml
<!-- ==============웹소켓 시작 ====================== -->
<!-- 웹소켓 요청시 해당 요청내용들을 처리해줄 클래스. (메세지 전송, 커넥션 연결요청, 해제 -->
<beans:bean id="chatHandler" class="com.kh.spring.chat.model.websocket.ChatWebsocket"></beans:bean>
<websocket:handlers>
<!-- 웹 소켓 요청 주소를 처리할 bean객체 -->
<websocket:mapping handler="chatHandler" path="/chat"/>
<!--
http통신에서 request와 response를 가로챈 후 httpSession정보를 webSocketSession에 넣어준다.
-->
<websocket:handshake-interceptors>
<beans:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"></beans:bean>
</websocket:handshake-interceptors>
<!-- SockJs라이브러리를 이용해서 만들어진 웹소켓 객체임을 의미한다.* -->
<websocket:sockjs></websocket:sockjs>
</websocket:handlers>
<!-- ==============웹소켓 끝 ====================== -->
=> Chat관련 클래스 추가
▶ ChatWebsocket.java
1) 동기화 처리
- HashSet 같은 경우 동기화가 안되어있음 => 그래서 synchronizedSet 를 이용하면 동기화된 set 객체를 반환할 수 있음.
- synchronizedSet : 동기화된 set을 반환해주는 메소드. 멀티스레드에서 하나의 컬렉션요소에 스레드가 동시에 접근하게 되면 충돌이 발생할 수 있으므로 동기화 처리를 진행함
private Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<WebSocketSession>()); |
2) 클라이언트와 웹소켓 연결을 위한 메소드 3개 추가
- TextWebSocketHandler : 웹소켓을 위한 메소드를 지원하는 인터페이스, 웹소켓 핸들러 인터페이스를 구현한 클래스이며 문자열(text)과 관련된 기능을 다룰 때 사용
- alt + shift + s 누른 후, Override/Implement Method 클릭후, 아래 3개 선택하여 추가
/* 클라이언트와 웹소켓 연결이 완료된 이후, 통신할 준비가 되면 실행되는 함수 */
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 매개변수로 전달받은 WebSocketSession : 웹소켓에 접속요청을 한 "클라이언트"의 세션
log.info("session ?? {}" + session.getId()); // 세션 id 확인
sessions.add(session);
}
/* 클라이언트로부터 메시지(message)가 도착했을 시 실행되는 함수 */
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
super.handleMessage(session, message);
}
/* 클라이언트와 웹소켓 연결이 종료되면 실행되는 함수 */
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session); // 웹소켓 연결이 종료되는 경우 sessions내부에 있는 클라이언트의 session정보를 삭제할 예정
}
3) views/chat 폴더 아래에 chatRoom.jsp 파일 생성 후, css 파일 넣음
▶ chatRoomList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link href="${contextPath }/resources/css/chat-style.css" rel="stylesheet">
<link href="${contextPath }/resources/css/main-style.css" rel="stylesheet">
<style>
.content{
height : 100%;
}
</style>
</head>
<body>
<jsp:include page="/WEB-INF/views/common/header.jsp" />
<div class="content">
<br><br>
<div class="innerOuter" style="padding: 5% 10%; width: 100%;">
<h2>채팅방목록</h2>
<br>
<br>
<table class="table table-hover" align="center">
<thead>
<tr>
<th>방번호</th>
<th>채팅방 제목(주제)</th>
<th>개설자</th>
<th>참여인수</th>
</tr>
</thead>
<tbody>
<c:choose>
<c:when test="${empty list}">
<tr>
<td colspan="4">존재하는 채팅방이 없습니다.</td>
</tr>
</c:when>
<c:otherwise>
<c:forEach items="${list}" var="chatRoom">
<tr>
<td>${chatRoom.chatRoomNo}</td>
<td>
${chatRoom.title}
<c:if test='${!empty loginUser}'>
<button class="btn btn-primary"
onclick="location.href='${contextPath}/chat/room/${chatRoom.chatRoomNo}'">참여</button>
</c:if>
</td>
<td>${chatRoom.userName}</td>
<td>${chatRoom.cnt}</td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</tbody>
</table>
<!-- 로그인이 되어있는 경우 보이는 버튼 -->
<c:if test="${!empty loginUser }">
<div class='btn-area'>
<button data-toggle="modal" data-target="#chatModal"
class="btn btn-danger">채팅방 만들기</button>
</div>
</c:if>
</div>
</div>
<div class="modal fade" id="chatModal">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<!-- 모달 해더 -->
<div class="modal-header">
<h4 class="modal-title">채팅방 만들기</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<form action="${contextPath}/chat/openChatRoom"
method="post">
<!-- 모달 바디 -->
<div class="modal-body">
<label for="title" class="mr-sm-2">제목</label> <input type="text"
class="form-controll mb-2 mr-sm-2" placeholder="채팅방 제목"
id="chatRoomTitle" name="title">
</div>
<!-- 모달 푸터 -->
<div class="modal-footer">
<button type="submit" id="open-form" class="btn btn-primary">만들기</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
</div>
</form>
</div>
</div>
</div>
<jsp:include page="/WEB-INF/views/common/footer.jsp" />
</body>
</html>
4) vo 클래스 추가
▶ ChatRoom.java
package com.kh.spring.chat.model.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ChatRoom {
private int chatRoomNo;
private String title;
private String status;
private int userNo;
// 사용자의 이름과 참여한 수도 표시하기 위해 추가해야함(DB에는 없지만..)
private String userName;
private int cnt;
}
▶ ChatRoomJoin.java
package com.kh.spring.chat.model.vo;
import lombok.Data;
@Data
public class ChatRoomJoin {
private int userNo;
private int chatRoomNo;
}
▶ ChatMessage.java
package com.kh.spring.chat.model.vo;
import lombok.Data;
@Data
public class ChatMessage {
private int cmNo;
private String message;
private String createDate;
private int chatRoomNo;
private int userNo;
// DB상에서는 없는 데이터이지만 채팅할때 이름을 가져오기 위해 추가
private String userName;
}
2. 채팅방 목록 조회 / 채팅방 생성
▶ ChatController.java
package com.kh.spring.chat.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.kh.spring.chat.model.service.ChatService;
import com.kh.spring.chat.model.vo.ChatRoom;
import com.kh.spring.member.model.vo.Member;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping("/chat")
@SessionAttributes({"loginUser"})
public class ChatController {
@Autowired
private ChatService chatService;
// 채팅방 목록
@GetMapping("chatRoomList") // "/chat/chatRoomList"
public String selectChatRoomList(Model model) {
// 1) DB에서 채팅방 목록데이터 조회
List<ChatRoom> list = chatService.selectChatRoomList();
// 2) 조회된 데이터를 model안에 추가
model.addAttribute("list", list);
// 3) view페이지 포워딩
return "chat/chatRoomList";
}
// 채팅방 생성
@PostMapping("/openChatRoom")
public String openChatRoom(
ChatRoom room,
RedirectAttributes ra,
@ModelAttribute("loginUser") Member loginUser
// 모델안(세션스콥데이터)에 저장되어있는 값중에 loginUser를 꺼내온다!
// ModelAttribute는 SessionAttributes와 함께 써야한다
) {
room.setUserNo(loginUser.getUserNo());
int chatRoomNo = chatService.openChatRoom(room); // 채팅방(chatRoom) 생성 및 생성된 채팅방 내부로 이동.
String path = "redirect:/chat/";
if(chatRoomNo > 0) {
ra.addFlashAttribute("alertMsg", "채팅방생성 성공");
// path += "room/" + chatRoomNo;
path += "chatRoomList";
}else {
ra.addFlashAttribute("alertMsg", "채팅방생성 실패");
path += "chatRoomList"; // 목록페이지 그대로
}
return path;
}
}
▶ ChatService.java
package com.kh.spring.chat.model.service;
import java.util.List;
import com.kh.spring.chat.model.vo.ChatRoom;
public interface ChatService{
// 채팅방 목록
List<ChatRoom> selectChatRoomList();
// 채팅방 생성
int openChatRoom(ChatRoom room);
}
▶ ChatServiceImpl.java
@Service 어노테이션 추가
package com.kh.spring.chat.model.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.kh.spring.chat.model.dao.ChatDao;
import com.kh.spring.chat.model.vo.ChatRoom;
@Service
public class ChatServiceImpl implements ChatService{
@Autowired
private ChatDao dao;
// 채팅방 목록
@Override
public List<ChatRoom> selectChatRoomList() {
return dao.selectChatRoomList();
}
// 채팅방 생성
@Override
public int openChatRoom(ChatRoom room) {
return dao.openChatRoom(room);
}
}
▶ ChatDao.java
@Repository 어노테이션 추가
package com.kh.spring.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.kh.spring.chat.model.vo.ChatRoom;
@Repository
public class ChatDao {
@Autowired
private SqlSessionTemplate sqlSession;
// 채팅방 목록
public List<ChatRoom> selectChatRoomList() {
return sqlSession.selectList("chatMapper.selectChatRoomList");
}
// 채팅방 생성
public int openChatRoom(ChatRoom room) {
int result = sqlSession.insert("chatMapper.openChatRoom", room);
if(result > 0) {
result = room.getChatRoomNo(); // 추가된 pk값
}
return result;
}
}
▶ chat-mapper.xml
<?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">
<!-- 채팅방 목록 -->
<select id="selectChatRoomList" resultType="chatRoom">
SELECT
CHAT_ROOM_NO ,
TITLE,
USER_NAME,
(SELECT COUNT(*) FROM CHAT_ROOM_JOIN CRJ WHERE CRJ.CHAT_ROOM_NO = CR.CHAT_ROOM_NO) AS CNT
FROM CHAT_ROOM CR
JOIN MEMBER USING(USER_NO)
WHERE CR.STATUS = 'Y'
ORDER BY CHAT_ROOM_NO DESC
</select>
<!-- 채팅방 생성 -->
<insert id="openChatRoom" parameterType="chatRoom" useGeneratedKeys="true">
INSERT INTO CHAT_ROOM VALUES
(SEQ_CR_NO.NEXTVAL, #{title}, DEFAULT, #{userNo})
<selectKey keyProperty="chatRoomNo" resultType="int" order="AFTER"> <!-- AFTER : 메인쿼리문이 이후에 실행되도록 조절 -->
<!-- BEFORE이면 SEQ_CR_NO.NEXTVAL 이지만 AFTER라서 CURRVAL을 사용 -->
<!-- CURRVAL : 마지막으로 호출된 NEXTVAL을 가져오는 예약어 -->
SELECT SEQ_CR_NO.CURRVAL FROM DUAL
</selectKey>
</insert>
</mapper>
▶ 결과
'Server > Spring' 카테고리의 다른 글
[Spring] 10-3. 채팅방 통신(채팅방 메세지 추가) (0) | 2024.01.30 |
---|---|
[Spring] 10-2. 채팅방 참여, 인원수 증가, 메세지 조회 (0) | 2024.01.29 |
[Spring] 9. 댓글 목록, 등록, 수정, 삭제(비동기식) (0) | 2024.01.26 |
[Spring] 8-5. 게시판 수정하기(+ 사진삭제, 사진수정, 사진추가) (0) | 2024.01.26 |
[Spring] 8-4. 첨부파일 사진 다운로드 (0) | 2024.01.25 |