스프링

 

1. 라이브러리 설정

	<!-- 알림 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-websocket</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.8.6</version>
		</dependency>			
		

 

2. servlet-context.xml 설정

  <beans:bean id="alarmHandler"
		class="패키지.AlarmHandler" />
	<websocket:handlers allowed-origins="*">
		<websocket:mapping handler="alarmHandler"
			path="/alarm" />
		<websocket:handshake-interceptors>
			<beans:bean
				class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor" />
		</websocket:handshake-interceptors>
		<websocket:sockjs
			client-library-url="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js" />

	</websocket:handlers>

 

 

3.  AlarmHandler

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.google.gson.Gson;
import com.gudi.board.dto.InformDTO;
import com.gudi.board.service.InformService;

@Component
public class AlarmHandler extends TextWebSocketHandler  {
		
	// 로그인 한 전체
	List<WebSocketSession> sessions = new ArrayList<WebSocketSession>();
	// 1대1
	Map<String, WebSocketSession> userSessionsMap = new HashMap<String, WebSocketSession>();
	
	@Autowired
	InformService informService;
	
	

	// 서버에 접속이 성공 했을때
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		//접속한 전체 유저 아이디
		sessions.add(session);
		//로그인한 개별 유저 아이디를 가져온다.
		String senderID = getId(session);
		//userSessionsMap 에 개별유저아이디를 넣는다.
		userSessionsMap.put(senderID, session);
	}

	// 소켓에 메세지를 보냈을때
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
           String userId=getId(session);
           Map<String, Object> httpSession = session.getAttributes();
           String loginId = (String) httpSession.get("loginId"); 
           if(loginId!=null) {
		
		//접속한 해당 유저 읽지않은 알림 데이터 전체 카운트 만 가져올 경우  
		//int countInform=informService.countInform(userId);
		
		//1.해당 유저 알림데이터 전체 가져오기
		//List<InformDTO> getInform= informService.selectInformDTO(userId);
		//for(InformDTO informDTO :getInform) {
			//System.out.println("getInform : "+informDTO.toString());	
		//}
		
		//2.해당 유저 알림데이터 마지막 데이터만 가져올 경우
		InformDTO selectLsatInform= informService.selectLsatInform(userId);
		
		WebSocketSession webSocketSession = userSessionsMap.get(userId);
				
		Gson gson = new Gson();
		//1.해당 유저 알림데이터 전체 가져오기일 경우 JSON 으로 전환 후 TextMessage 변환
		//TextMessage textMessage = new TextMessage(gson.toJson(getInform));		
		
		//2.해당 유저 알림데이터 전체 가져오기일 경우  JSON 으로 전환 후 TextMessage 변환
		TextMessage textMessage = new TextMessage(gson.toJson(selectLsatInform));
		webSocketSession.sendMessage(textMessage);
		
          }
	}

	// 연결 해제될때
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		//System.out.println("afterConnectionClosed " + session + ", " + status);
		//소켓 연결이 끊겼을 때 개별 접속아이디 해체 저리한다.
		userSessionsMap.remove(session.getId());
		//소켓 연결이 끊겼을 때 전체 접속자 아이디 해체 저리한다.
		sessions.remove(session);
	}
	

	// 웹소켓 id 가져오기
	private String getId(WebSocketSession session) {
/*		
		(String)request.getSession().getAttribute("loginId");
		또는 ,
		session.getAttribute("loginId"); 
		이렇게 세션값을 가져오나 여기 웹소켓에서는 세션값을 WebSocketSession session  형태로 가져옵니다.
		따라서 , 다음과 코드 형태로 세션값을 가져옵니다. 
*/		
        
		Map<String, Object> httpSession = session.getAttributes();
		String loginId = (String) httpSession.get("loginId");		
		if (loginId == null) {
			//System.out.println("로그인 loginID 가 널일경우  :" + session.getId());
			//랜덤 아이디 생성, 사이트 접속한 사람 전체
			//ex ) vawpuj5h, 5qw40sff
			return session.getId();
		} else {
                        //로그인한 유저 반환
			return loginId;
		}
	}
	
	 

}

 

 

4. alarm.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
 <div class="toast align-items-center fade" role="alert"  id='alert-toast' aria-live="assertive" aria-atomic="true" style="color: white;background: #dc5151; position: absolute;
    top: 78px;right: 0px;">
  <div class="d-flex">
    <div class="toast-body">
   </div>
    <button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
  </div>
</div>   
<%-- <script src="${pageContext.request.contextPath}/resources/js/sockjs.min.js"></script>  --%>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script> 
<script>
//소켓 전역변수 선언
var socket = null;
//한번만 메시지 알림을 위해 alarmCount 변수 설정 
var alarmCount=0;
$(document).ready(function(){
	connectWs();	
	
	setTimeout(() => {
		//시작후 0.3초후 알림데이터 가져오기
		  sock.send("${loginId}");
	}, 300);
	
	//5초마다 알림데이터 가져오기
	setInterval("autoScript()", 5000);
});

function autoScript() {	
	sock.send("${loginId}");
}
function connectWs(){
	//console.log("getContextPath :" +getContextPath());
	// 웹소켓 주소
   // var wsUri = "ws://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/alarm";
    //var wsUri = "${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/alarm";
	//sock = new SockJS(wsUri);
    sock = new SockJS(getContextPath()+'/alarm');		
	socket = sock;
	sock.onopen = function() {
		//console.log('connect onopen');
	};


  
 sock.onmessage = function(evt) {
	 	var data = evt.data;
	    //console.log("ReceivMessage : " + data + "\n");
		if(data!="null"){
		    const obj =JSON.parse(data);
			
			//멀티 알림일 경우
			//multiAlarmData(obj);
			
			//알림 마지막 데이터만
			alarmData(obj);
	    }else{
	    	$("#alarm-span").hide();	
	    }
 };

 
 sock.onclose = function() {
   //	console.log('connect close');   
 };

sock.onerror = function (err) {console.log('Errors : ' , err);};
}

function alarmListMove(){
	location.href="${pageContext.request.contextPath}/alarmAllRead";
}

function getContextPath() {
    var hostIndex = location.href.indexOf( location.host ) + location.host.length;
    return location.href.substring( hostIndex, location.href.indexOf('/', hostIndex + 1) );
}; 

//멀티 알림일 경우
function multiAlarmData(obj){
	//마지막 것만 알림을 위해 카운트 비교
	if(alarmCount!=obj.length){
		//console.log(obj[obj.length-1]);
		var html='<a style="color:#fff; text-decoration: none;"  href="${pageContext.request.contextPath}/alarmLinkMove?type=fbdetail&postId='+obj[obj.length-1].relatedId+'&informid='+obj[obj.length-1].informId+'">' +obj[obj.length-1].informContent  +'</a>';
		$("#alert-toast .toast-body").html(html);
		$("#alert-toast").addClass("show");
		$("#alert-toast .d-flex").css("display", "block")
		;						
		setTimeout(() => {
			$("#alert-toast").removeClass("show");
			$("#alert-toast .d-flex").css("display", "none");
		}, 5000);
	}
	
	//alarmCount 에 값이 일치하게 만든다.
	alarmCount=obj.length;
	
	//console.dir(obj);
	if(obj.length>0){
		$("#alarm-span").show();
		if(obj.length>1){
			$("#alarm-span-count").text(obj.length);
			if($("#alarm-span-count").hasClass("visually-hidden") === true){
				$("#alarm-span-count").removeClass("visually-hidden");
			}
		}			
	}
}


function alarmData(obj){
	//알람 보이기
	if(parseInt(obj.countInform)>0){
		$("#alarm-span-count").text(obj.countInform);
		$("#alarm-span").show();
		 
		
		//토스트 메시지 보이기
		if(alarmCount==0){
			var html='<a style="color:#fff; text-decoration: none;"  href="${pageContext.request.contextPath}/alarmLinkMove?type=fbdetail&postId='+obj.relatedId+'&informid='+obj.informId+'">' +obj.informContent  +'</a>';
			$("#alert-toast .toast-body").html(html);
			$("#alert-toast").addClass("show");
			$("#alert-toast .d-flex").css("display", "block");						
			
			
			setTimeout(() => {
				$("#alert-toast").removeClass("show");
				$("#alert-toast .d-flex").css("display", "none");
			}, 5000);
		}
		
		alarmCount++;
		
	}else{
		$("#alarm-span").hide();	
	}
	
}
</script>

 

 

기타 참조

 

html

		<button type="button" onclick="alarmListMove()"
				class="btn btn-sm btn-#3c3c3c; position-relative">
				<i class="bi bi-bell-fill" style="font-size: 1.8rem; color: white"></i><span id="alarm-span"
					class="position-absoluteposition-absolute top-0 end-0 translate-middle badge border border-light rounded-circle bg-danger p-2"><span
					class="" id="alarm-span-count"></span></span>
			</button>

 

@Data
public class InformDTO {

	private Integer informId; 
	private String userId;
	private Date informDate;
	private String informField;
	private Integer relatedId;
	private String informContent;
	private String isRead;
	private int countInform;
}


 

 

	<select id="selectLsatInform" resultType="com.gudi.board.dto.InformDTO">
		SELECT R.* , (SELECT count(INFORMID) FROM INFORM  WHERE userid=#{userid} AND ISREAD='N' ) countInform  FROM (
		SELECT  * FROM INFORM i WHERE userid=#{userid} AND ISREAD='N'   ORDER BY INFORMID DESC  
		) R WHERE  ROWNUM = 1
	</select>

 

 

 

about author

PHRASE

Level 60  라이트

참되고 아름다운 모든 것은 언제나 전부를 용서하는 데서만 있을 수 있다. -도스토예프스키

댓글 ( 6)

댓글 남기기

작성