안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
웹 소켓 이용하여 공지 전송하기
header.jsp
<!-- 로그인 했을 때만 웹 소켓 연결! -->
<sec:authorize access="isAuthenticated()">
<script>
const memberId = "<sec:authentication property='principal.username'/>";
</script>
<script src="${pageContext.request.contextPath}/resources/js/ws.js"></script>
</sec:authorize>
</head>
ws.js
// ws.js
const ws = new SockJS(`http://${location.host}/spring2/stomp`);
// stomp 객체 생성 -> 웹소켓 객체를 직접 제어하지 않고 stomp를 통해서 제어
const stompClient = Stomp.over(ws);
// 연결된 이후 호출해주는 핸들러. {} -> 옵션
stompClient.connect({}, (frame) => {
console.log("connect : ", frame);
// 연결 이후 구독 신청
/*
stompClient.subscribe("/topic/a", (message) => {
console.log("/topic/a : ", message);
});
stompClient.subscribe("/app/a", (message) => {
console.log("/app/a : ", message);
})
*/
// 전체 공지
stompClient.subscribe("/app/notice", (message) => {
console.log("/app/notice : ", message);
});
// 개별 공지
stompClient.subscribe(`/app/notice/${memberId}`, (message) => {
console.log(`/app/notice/${memberId} : `, message);
})
});
전체공지는 "/app/notice", 개별공지는 "/app/notice/(memberId)" 구독처리를 하였습니다.
memberId는 ws.js 선언 전에 변수화하였기 때문에 사용할 수 있겠죠.
※ 만일 subscribe를 connect 이후에 호출한다면 작동되지 않음!
stomp 또한 비동기처리이기 때문에 call stack에서 바로바로 처리 되지 않고 백그라운드에 위임시켜버리기 때문에 connect 호출 후 연결이 완료되기 전에 subscribe가 처리 되므로 오류 발생!!!
따라서 subscribe는 connect의 콜백함수 안에 위치해야 함
https://chanychu.tistory.com/281
로그인 시 위와 같이 "/app/notice"와 "/app/notice/(memberId)" 구독처리하는 것을 확인할 수 있습니다.
memberList.jsp
<jsp:include page="/WEB-INF/views/common/header.jsp">
<jsp:param name="title" value="관리자 회원 관리" />
</jsp:include>
<div class="text-center">관리자 회원관리 페이지입니다. 이 페이지를 볼 수 있는 당신은 관리자!!</div>
<div class="w-75 mx-auto">
<button
type="button"
class="btn btn-block btn-outline-primary"
data-toggle="modal" data-target="#adminNoticeModal">공지</button>
</div>
<!-- 관리자용 공지 modal -->
<div class="modal fade" id="adminNoticeModal" tabindex="-1" role="dialog" aria-labelledby="#adminNoticeModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="adminNoticeModalLabel">Notice</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form name="adminNoticeFrm">
<div class="form-group">
<label for="send-to-name" class="col-form-label">받는이 :</label>
<input type="text" class="form-control" id="send-to-name" placeholder="공란인 경우, 전체공지로 전송됩니다.">
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">메세지 :</label>
<textarea class="form-control" id="message-text"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" id="adminNoticeSendBtn">전송</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">닫기</button>
</div>
</div>
</div>
</div>
<script>
document.querySelector("#adminNoticeSendBtn").addEventListener('click', (e) => {
const to = document.querySelector("#send-to-name").value;
const msg = document.querySelector("#message-text").value;
if(!msg) return;
const payload = {
from : '<sec:authentication property="principal.username"/>',
to,
msg,
time : Date.now(),
type : 'NOTICE'
};
const url = to ? `/app/admin/notice/\${to}` : '/app/admin/notice';
stompClient.send(url, null, JSON.stringify(payload)); // 텍스트 기반으로 송부!(객체론 안됨)
document.adminNoticeFrm.reset();
// 모달 숨기기
$("#adminNoticeModal").modal('hide');
});
</script>
받는이를 설정하였기 때문에 '/app/admin/notice/(memberId)'로 잘 전송 된 것을 확인할 수 있습니다.
하지만 messageMapping을 안 했기 때문에 구독자들에게 해당 메세지가 전달 되진 않았습니다.
mapping처리를 하기 위해 Controller로 가보겠습니다.
전체공지
Controller
StompController
/**
* 전체공지
* @RequestBody -> json을 자바 객체로 변환!
*/
@MessageMapping("/admin/notice")
@SendTo("/app/notice")
public Payload notice(@RequestBody Payload payload) {
log.debug("payload = {}", payload);
return payload;
}
@콘솔출력값
DEBUG: com.ce.spring2.ws.controller.StompController - payload = Payload(type=NOTICE, to=, from=admin, msg=전체공지!!, time=1663222906963)
'/app/notice' 구독자들에게 메세지가 잘 전송 된 것을 확인할 수 있습니다.
관리자 또한 '/app/notice'의 구독자이므로 메세지를 보내고, 그대로 돌려받았습니다.
받은 메세지 시각화
ws.js
// 전체 공지
stompClient.subscribe("/app/notice", (message) => {
console.log("/app/notice : ", message);
const {body} = message;
const {msg, time} = JSON.parse(body);
alert(`전체공지
=========================
${msg}
=========================
${new Date(time)}`);
});
개별공지
Controller
StompController
@MessageMapping("/admin/notice/{memberId}")
@SendTo("/app/notice/{memberId}")
public Payload notice(@RequestBody Payload payload, @DestinationVariable String memberId) {
log.debug("payload = {}", payload);
log.debug("memberId = {}", memberId);
return payload;
}
@콘솔출력값
DEBUG: com.ce.spring2.ws.controller.StompController - payload = Payload(type=NOTICE, to=honggd, from=admin, msg=홍지디야 속닥속닥, time=1663223916158)
DEBUG: com.ce.spring2.ws.controller.StompController - memberId = honggd
ws.js
// 개별 공지
stompClient.subscribe(`/app/notice/${memberId}`, (message) => {
console.log(`/app/notice/${memberId} : `, message);
const {body} = message;
const {msg, time} = JSON.parse(body);
alert(`개인공지
=========================
${msg}
=========================
${new Date(time)}`);
});
'/app/notice/honggd'를 구독하고 있는 honggd에게만 메세지가 도착한 것을 알 수 있습니다.
'Java > Spring' 카테고리의 다른 글
Spring) 관리자와 1:1 채팅 - 기본 흐름, 구독 처리 (0) | 2022.10.10 |
---|---|
Spring) Web-Socket - Stomp (게시글 조회 알림 구현) (0) | 2022.09.15 |
Spring) Spring-WebSocket - Stomp, Stomp 환경설정 및 흐름파악 (0) | 2022.09.14 |
Spring) Spring-WebSocket + SockJS (0) | 2022.09.14 |
Spring) Spring-WebSocket (관련 설정) (2) | 2022.09.14 |