본문 바로가기

서버/Spring
[스프링(Spring)] WebSocket

// Socket
    - 네트워크상에서 호스트 간의 통신하는 규약(약속) - 인터페이스
    - 여러 언어들이 Socket 구현 > 클래스 or 객체 제공

    - 웹통신, Ajax 통신 > 단방향 통신(편지, 무전기)
       <-> 소켓 통신 > 양방향 통신(전화)
   

// WebSocket
    - HTML5 때 생성
    - JavaScript에서 소켓 통신 지원
    - Spring > WebSocket을 지원하는 API 제공


// 기본 설정 및 테스트

~ pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>websocket</artifactId>
	<name>WebSocketTest</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>11</java-version>
		<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
		<org.aspectj-version>1.6.10</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<!-- Servlet / JSP -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.3</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<!-- Lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.28</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- JSON -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.15.0</version>
		</dependency>

		<dependency>
			<groupId>com.fasterxml.jackson.dataformat</groupId>
			<artifactId>jackson-dataformat-xml</artifactId>
			<version>2.15.0</version>
		</dependency>

		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.10.1</version>
		</dependency>
		
		<!-- WebSocket -->
		<dependency>
	        <groupId>org.springframework</groupId>
	        <artifactId>spring-websocket</artifactId>
	        <version>${org.springframework-version}</version>
      	</dependency>
      	
      	<dependency>
       		<groupId>javax.websocket</groupId>
        	<artifactId>javax.websocket-api</artifactId>
        	<version>1.1</version>
     	</dependency>
		
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>2.9</version>
				<configuration>
					<additionalProjectnatures>
						<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
					</additionalProjectnatures>
					<additionalBuildcommands>
						<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
					</additionalBuildcommands>
					<downloadSources>true</downloadSources>
					<downloadJavadocs>true</downloadJavadocs>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.5.1</version>
				<configuration>
					<source>11</source>
					<target>11</target>
					<compilerArgument>-Xlint:all</compilerArgument>
					<showWarnings>true</showWarnings>
					<showDeprecation>true</showDeprecation>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<configuration>
					<mainClass>org.test.int1.Main</mainClass>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>


~ web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>

	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>*</url-pattern>
	</filter-mapping>

</web-app>

// 연결하기

~ "com.test.controller" > "SocketController.java"

package com.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SocketController {

	@GetMapping("/test.do")
	public String test() {
		
		return "test";
	}
    
}


~ "com.test.server" > "SocketServer.java"

~ sevlet-context.xml > scan 하기

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.test.websocket" />
	<context:component-scan base-package="com.test.controller" />
	<context:component-scan base-package="com.test.server" />
	
	
	
</beans:beans>


~ views > "test.jsp"

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<h1>WebSocket <small>연결 테스트</small></h1>
	
	<div>
		<button type="button" class="in" id="btnConnect">연결하기</button>
		<button type="button" class="out" id="btnDisConnect">종료하기</button>
	</div>
	
	<hr>
	
	<div>
		<input type="text" class="long" id="msg">
		<button type="button" class="add" id="btnEcho">에코 테스트</button>
	</div>
	
	<hr>
	
	<div class="message full"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

	//1. 소켓 생성
	//2. 서버 접속(연결)
	//3. 통신
	//4. 서버 접속 종료
	
	const uri = 'ws://localhost/websocket/server.do'
	
	let ws;
	
	$('#btnConnect').click(function() {
	    
	    //1. 웹소켓 생성 > 자동으로 연결 요청
	    ws = new WebSocket(uri);	  
	    
	    //웹소켓 서버에게 연결 요청이 성공하면 발생하는 이벤트 > 콜백 함수
	    ws.onopen = function(evt) {
	        log('서버와 연결되었습니다.');
	    };
	    
	    ws.onclose = function(evt) {
	        log('서버와 종료되었습니다.');
	    }
	    
	    ws.onmessage = function(evt) {
	        log(evt.data); //서버가 보낸 메시지
	    }	    
	    
	    ws.onerror = function(evt) {
	        log('에러가 발생했습니다.')
	    }
	    
	});
	
	$('#btnDisConnect').click(function() {
	    
	    //연결된 소켓을 중단
	    ws.close();
	    log('연결을 종료합니다.');
	    
	});
	
	$('#btnEcho').click(function() {
	  
	    //연결된 소켓으로 서버에게 데이터 전송하기
	   
	    //ws.CONNECTING : 연결중
	    //ws.OPEN : 열림 > 통신 가능 상태
	    //ws.CLOSING : 닫는중
	    //ws.CLOSED : 닫힘
	    
	    
	    if(ws.readyState == ws.OPEN) {
	        //ws.send('안녕하세요.');
	        ws.send($('#msg').val());
		    log('메시지를 전달했습니다.');
	    }   
	    
	});
	
	function log(msg) {	    
	    $('.message').prepend(`<div>[\${new Date().toLocaleTimeString()}] \${msg}</div>`);
	}

</script>
</body>
</html>

// 알림 서비스
- 관리자가 접속자에게 공지 > 알림 표시 구현


~ com.test.controller > SocketController.java > admin(), user() 추가

package com.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SocketController {
	
	@GetMapping("/admin.do")
	public String admin() {
		
		return "admin";
	}
	
	@GetMapping("/user.do")
	public String user() {
		
		return "user";
	}
	
}


~ com.test.server > "NoticeServer.java" > 메소드 선언

package com.test.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint("/noticeserver.do")
public class NoticeServer {	
	
	@OnOpen
	public void handleOpen() {
	}
	
	@OnMessage
	public void handleMessage() {		
	}
	
	@OnClose
	public void handleClose() {		
	}
	
	@OnError
	public void handleError(Throwable e) {		
	}	

}


~ views > "admin.jsp" > ws.open 구현
~ com.test.server > NoticeServer.java > handleOpen 구현

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<h1>알림 서비스<small>관리자</small></h1>
	
	<div>
		<textarea id="msg"></textarea>
		<button type="button" id="btnSend" class="add">보내기</button>
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

	const uri = 'ws://localhost/websocket/noticeserver.do';
	
	let ws;
	
	$(document).ready(function() {//페이지 초기화
	   
	    ws = new WebSocket(uri);
	
		ws.onopen = function(evt) {
		    console.log('서버와 연결됨')
		};
		
		ws.onmessage = function(evt) { };
		
		ws.onclose = function(evt) { };
		
		ws.onerror = function(evt) { };
	    
	});

</script>
</body>
</html>

 

package com.test.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint("/noticeserver.do")
public class NoticeServer {
	
	@OnOpen
	public void handleOpen() {
		System.out.println("연결 성공");
	}
	
	@OnMessage
	public void handleMessage() {
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}

}


~ views > admin.jsp > 메시지(버튼 클릭 이벤트) 구현
~ com.test.server > NoticeServer.java > handleMessgae 구현

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<h1>알림 서비스<small>관리자</small></h1>
	
	<div>
		<textarea id="msg"></textarea>
		<button type="button" id="btnSend" class="add">보내기</button>
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

	const uri = 'ws://localhost/websocket/noticeserver.do';
	
	let ws;
	
	$(document).ready(function() {//페이지 초기화
	   
	    ws = new WebSocket(uri);
	
		ws.onopen = function(evt) {
		    console.log('서버와 연결됨')
		};
		
		ws.onmessage = function(evt) { };
		
		ws.onclose = function(evt) { };
		
		ws.onerror = function(evt) { };
	    
	});
	
	$('#btnSend').click(function() {
	   
	    ws.send($('#msg').val());
	    
	});
	

</script>
</body>
</html>

 

package com.test.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint("/noticeserver.do")
public class NoticeServer {
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
		
		System.out.println("메시지: " + msg);		
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}

}


~ views > "user.jsp" > ready 구현

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>
	#msgBox {
		border: 1px solid var(--border-color);
		width: 250px;
		height: 150px;
		display: flex;
		justify-content: center;
		align-items: center;
		padding: 0;
		background-color: #FFF;
		border-radius: 5px;
		position: fixed;
		right: 20px;
		bottom: -200px;
		opacity: 0;
		transition: all 1s;
	}
</style>
</head>
<body>

	<h1>알림 서비스 <small>유저</small></h1>
	
	<div id="msgBox"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
	
	const  uri = "ws://localhost/websocket/noticeserver.do";
	
	let ws;
	
	$(document).ready(function() {
	    
	    ws = new WebSocket(uri);
	    
	    ws.onopen  = function(evt) {
	        console.log('연결 성공');
	    };
			
		ws.onmessage = function(evt) {
		};
			
		ws.onclose = function(evt) {
	    };
			
		ws.onerror = function(evt) { };
		     	
	});

</script>
</body>
</html>


~ com.test.server > NoticeServer.java > Session 연결
                                                             > sessionList 선언 및 handleOpen에서 활용
                                                             > handleMessage

package com.test.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint("/noticeserver.do")
public class NoticeServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
    
		System.out.println("메시지: " + msg);
		
		//관리자 > (전송) > 공지사항 > (수신) > 서버 > (전송) > 모든 유저
		
		for(Session s : sessionList) {
			
			if(session != s) { //관리자 제외한 나머지 유저
				try {
					s.getBasicRemote().sendText(msg); //상대방의 소켓에 메세지 전송
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		}
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}
}


~ views > user.jsp > onmessage 메소드 구현

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>
	#msgBox {
		border: 1px solid var(--border-color);
		width: 250px;
		height: 150px;
		display: flex;
		justify-content: center;
		align-items: center;
		padding: 0;
		background-color: #FFF;
		border-radius: 5px;
		position: fixed;
		right: 20px;
		bottom: -200px;
		opacity: 0;
		transition: all 1s;
	}
</style>
</head>
<body>

	<h1>알림 서비스 <small>유저</small></h1>
	
	<div id="msgBox"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
	
	const  uri = "ws://localhost/websocket/noticeserver.do";
	
	let ws;
	
	$(document).ready(function() {
	    
	    ws = new WebSocket(uri);
	    
	    ws.onopen  = function(evt) {
	        console.log('연결 성공');
	    };
			
		ws.onmessage = function(evt) { 
		    console.log(evt.data);
		    
		};
			
		ws.onclose = function(evt) {
	        console.log('연결 종료');
	    };
			
		ws.onerror = function(evt) { };
		     	
	});

</script>
</body>
</html>


~ com.test.server > NoticeServer.java > clearSession 구현 + handleMessage에서 호출

package com.test.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;


@ServerEndpoint("/noticeserver.do")
public class NoticeServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
		
		clearSession();
		
		System.out.println("메시지: " + msg);
		
		//관리자 > (전송) > 공지사항 > (수신) > 서버 > (전송) > 모든 유저
		
		for(Session s : sessionList) {
			
			if(session != s) { //관리자 제외한 나머지 유저
				try {
					s.getBasicRemote().sendText(msg); //상대방의 소켓에 메세지 전송
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		}
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}
	
	public void clearSession() {
		Iterator<Session> iter = sessionList.iterator();
		
		while(iter.hasNext()) {
			if(!(iter.next()).isOpen())  {
				iter.remove(); //접속이 끊긴 클라이언트 소켓(세션)을 제거
			}
		}
	}
	

}



~ views > user.jsp > showMsgBox 수정 + ready(onmessage 호출)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>
	#msgBox {
		border: 1px solid var(--border-color);
		width: 250px;
		height: 150px;
		display: flex;
		justify-content: center;
		align-items: center;
		padding: 0;
		background-color: #FFF;
		border-radius: 5px;
		position: fixed;
		right: 20px;
		bottom: -200px;
		opacity: 0;
		transition: all 1s;
	}
</style>
</head>
<body>

	<h1>알림 서비스 <small>유저</small></h1>
	
	<div id="msgBox"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

	function showMsgBox(msg) {
	    
	    $('#msgBox').text(msg);
	    
	    $('#msgBox').css({
	        bottom: '20px',
	        opacity: 1
	    });
	    
	    setTimeout(function() {
	        $('#msgBox').css({
	            bottom: '-200px',
	            opacity: 0
	        });
	    }, 5000);
	}
	
	//showMsgBox();
	
	const  uri = "ws://localhost/websocket/noticeserver.do";
	
	let ws;
	
	$(document).ready(function() {
	    
	    ws = new WebSocket(uri);
	    
	    ws.onopen  = function(evt) {
	        console.log('연결 성공');
	    };
			
		ws.onmessage = function(evt) { 
		    console.log(evt.data);
		    
		    showMsgBox(evt.data);
		};
			
		ws.onclose = function(evt) {
	        console.log('연결 종료');
	    };
			
		ws.onerror = function(evt) { };
		     	
	});

</script>
</body>
</html>

// 채팅 서비스


~ com.test.controller > SocketController.java > chat() , index() 추가

package com.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SocketController {
	
	@GetMapping("/index.do")
	public String index() {
		
		return "index";
	}
	
	
	@GetMapping("/chat.do")
	public String chat() {
		
		return "chat";
	}
	
}


~ views > "index.jsp" : 입장 페이지  > 화면 설계
              > "chat.jsp"  : 대화 페이지  > 화면 설계

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<h1>WebSocket <small>Chat</small></h1>
	
	<div>
		<div class="group">
			<label>대화명</label>
			<input type="text" name="name" id="name" class="short">
		</div>
	</div>
	<div>
		<button type="button" class="in">들어가기</button>
		
		<button type="button" class="in" data-name="강아지">강아지</button>
		<button type="button" class="in" data-name="고양이">고양이</button>		
		<button type="button" class="in" data-name="토끼">토끼</button>		
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
</script>
</body>
</html>

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>
</script>
</body>
</html>


~ com.test.server > "ChatServer.java"

package com.test.server;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;
import com.test.domain.ChatDTO;


@ServerEndpoint("/chatserver.do")
public class ChatServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}
}


~ views > index.jsp > 이미 열려있을 경우 새로고침 되지 않게 버튼 막아주기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<h1>WebSocket <small>Chat</small></h1>
	
	<div>
		<div class="group">
			<label>대화명</label>
			<input type="text" name="name" id="name" class="short">
		</div>
	</div>
	<div>
		<button type="button" class="in">들어가기</button>
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

	$('.in').click(function() {
	    
	    let name = $(event.target).data('name');
	    
	    if(name == null || name == undefined) {
	        name = $('#name').val();
	    }
		
	    let child = window.open('/websocket/chat.do', 'chat', 'width=404 height=510');
	    
	    $('.in').css('opacity', .5);
	    $('.in').prop('disabled', true);	    
	    
	});

</script>
</body>
</html>


~ views > chat.jsp > 새로고침 막기 + 웹소켓 연결

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>

	window.onkeydown = function() {
	  
	    if(event.keyCode == 116) {
	        event.preventDefault();
	        return false;
	    }
	    
	};	
	
	/*
	
		101#hong#lee#안녕하세요.#2023... > 프로토콜 설계
	
		서버	<- (대화) -> 클라이언트
		
		- code: 상태코드
			1. 새로운 유저가 들어옴
			2. 기존 유저가 나감
			3: 메시지 전달
			4. 이모티콘 전달			
		- sender: 보내는 유저명
		- receiver: 받는 유저명
		- content: 멧지
		- regdate: 날짜/시간
	
	*/	

	const url = 'ws://localhost/websocket/chatserver.do';
	
	let ws;
	
	var name;
	
	function connect(name) {
	    
		window.name = name;
		$('#header small').text(name);
		
		//연결하기
		ws = new WebSocket(url);
		
		ws.onopen = function(evt) {
		    log('서버 연결 성공');		    
		};
	}
	
	function log(msg) {
	    console.log(`[\${new Date().toLocaleTimeString()}]` + msg);
	}
	
		

</script>
</body>
</html>


~ views > index.jsp > child 채팅방 연결

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<style>

</style>
</head>
<body>

	<h1>WebSocket <small>Chat</small></h1>
	
	<div>
		<div class="group">
			<label>대화명</label>
			<input type="text" name="name" id="name" class="short">
		</div>
	</div>
	<div>
		<button type="button" class="in">들어가기</button>
		
		<button type="button" class="in" data-name="강아지">강아지</button>
		<button type="button" class="in" data-name="고양이">고양이</button>		
		<button type="button" class="in" data-name="토끼">토끼</button>		
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>

	$('.in').click(function() {
	    
	    let name = $(event.target).data('name');
	    
	    if(name == null || name == undefined) {
	        name = $('#name').val();
	    }
		
	    let child = window.open('/websocket/chat.do', 'chat', 'width=404 height=510');
	    
	    $('.in').css('opacity', .5);
	    $('.in').prop('disabled', true);
	    
	    child.addEventListener('load', function() {
		    child.connect(name);
	    });
	    
	    
	});

</script>
</body>
</html>


~ views > chat.jsp > onopen -> 내가 들어온 사실 알리기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>

	window.onkeydown = function() {
	  
	    if(event.keyCode == 116) {
	        event.preventDefault();
	        return false;
	    }
	    
	};	
	
	/*
	
		101#hong#lee#안녕하세요.#2023... > 프로토콜 설계
	
		서버	<- (대화) -> 클라이언트
		
		- code: 상태코드
			1. 새로운 유저가 들어옴
			2. 기존 유저가 나감
			3: 메시지 전달
			4. 이모티콘 전달			
		- sender: 보내는 유저명
		- receiver: 받는 유저명
		- content: 멧지
		- regdate: 날짜/시간
	
	*/
	

	const url = 'ws://localhost/websocket/chatserver.do';
	
	let ws;
	
	var name;
	
	function connect(name) {
	    
		window.name = name;
		$('#header small').text(name);
		
		//연결하기
		ws = new WebSocket(url);
		
		ws.onopen = function(evt) {
		    log('서버 연결 성공');
		    
		    //내가 접속했다고 다른 사람들에게 알리기		
		    let chat = {
		    	code: '1',
		    	sender: window.name,
		    	receiver: '',
		    	content: '',
		    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
		    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
		    };
		    
		    ws.send(JSON.stringify(chat));
		    print('', '입장했습니다.', 'me', 'state', chat.regdate);
		    
		    $('#msg').focus();
		    
		};
		
		ws.onmessage = function(evt) {
		    
		};
		
		ws.onclose = function(evt) {
		    
		};
		
		ws.onoerror = function(evt) {
		    
		};
		
	}
	
	function log(msg) {
	    console.log(`[\${new Date().toLocaleTimeString()}]` + msg);
	}	

</script>
</body>
</html>


~ "com.test.domain" > "ChatDTO.java"

package com.test.domain;

import lombok.Data;

@Data
public class ChatDTO {

	private String code;
	private String sender;
	private String receiver;
	private String content;
	private String regdate;
	
}


~ com.test.server > ChatServer.java > handleMessage 구현
~ views > chat.jsp > 메시지 수신(onmessage)

package com.test.server;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;
import com.test.domain.ChatDTO;


@ServerEndpoint("/chatserver.do")
public class ChatServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
		
		System.out.println(msg);
		
		//JSON <- (변환) -> Java Object
		// - GSON
		
		Gson gson = new Gson();
		
		ChatDTO dto = gson.fromJson(msg, ChatDTO.class);
		
		if (dto.getCode().equals("1")) {
			
			//새로운 유저가 접속했습니다. > 모든 사람에게 알림 (현재 접속자 제외)
			for(Session s : sessionList) {
				if(s != session) {
					try {
						s.getBasicRemote().sendText(msg);
						
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			
		} 
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}	
	
}

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>	
	
	window.onkeydown = function() {
	  
	    if(event.keyCode == 116) {
	        event.preventDefault();
	        return false;
	    }
	    
	};	
	
	/*
	
		101#hong#lee#안녕하세요.#2023... > 프로토콜 설계
	
		서버	<- (대화) -> 클라이언트
		
		- code: 상태코드
			1. 새로운 유저가 들어옴
			2. 기존 유저가 나감
			3: 메시지 전달
			4. 이모티콘 전달			
		- sender: 보내는 유저명
		- receiver: 받는 유저명
		- content: 멧지
		- regdate: 날짜/시간
	
	*/
	

	const url = 'ws://localhost/websocket/chatserver.do';
	
	let ws;
	
	var name;
	
	function connect(name) {
	    
		window.name = name;
		$('#header small').text(name);
		
		//연결하기
		ws = new WebSocket(url);
		
		ws.onopen = function(evt) {
		    log('서버 연결 성공');
		    
		    //내가 접속했다고 다른 사람들에게 알리기		
		    let chat = {
		    	code: '1',
		    	sender: window.name,
		    	receiver: '',
		    	content: '',
		    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
		    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
		    };
		    
		    ws.send(JSON.stringify(chat));
		    print('', '입장했습니다.', 'me', 'state', chat.regdate);
		    
		    $('#msg').focus();
		    
		};
		
		ws.onmessage = function(evt) {
		    
		    log('메시지 수신')
		    
		    let chat = JSON.parse(evt.data);
		    
		    log(chat.code + ':' + chat.sender);
		    
		    if(chat.code == '1') {
		        //다른 사람 입장
		        print('', `[\${chat.sender}]님이 입장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } 
		};
		
		ws.onclose = function(evt) {
		    
		};
		
		ws.onoerror = function(evt) {
		    
		};
		
	}
	
	function log(msg) {
	    console.log(`[\${new Date().toLocaleTimeString()}]` + msg);
	}
		

</script>
</body>
</html>


~ com.test.server > ChatServer.java > else if (dto.getCode().equals("2")) {} 구현
~ views > chat.jsp > 메시지 수신(onmessage)

package com.test.server;

import java.util.ArrayList;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;
import com.test.domain.ChatDTO;


@ServerEndpoint("/chatserver.do")
public class ChatServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
		
		System.out.println(msg);
		
		//JSON <- (변환) -> Java Object
		// - GSON
		
		Gson gson = new Gson();
		
		ChatDTO dto = gson.fromJson(msg, ChatDTO.class);
		
		if (dto.getCode().equals("1")) {
			
			//새로운 유저가 접속했습니다. > 모든 사람에게 알림 (현재 접속자 제외)
			for(Session s : sessionList) {
				if(s != session) {
					try {
						s.getBasicRemote().sendText(msg);
						
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			
		} else if (dto.getCode().equals("2")) {

			//누군가가 퇴장 > 나머지 사람들에게 알려줌
			sessionList.remove(session); //서버 측에서의 퇴장
			
			for(Session s : sessionList) {
				
				try {
					s.getBasicRemote().sendText(msg);
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
			
		}
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}
	
}

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>

	$(window).on('beforeunload', function() {
		
	    $(opener.document).find('.in').css('opacity', 1);
	    $(opener.document).find('.in').prop('disabled', false);
	    
	  	//내가 나간다고 서버에게 메시지 보내기
	    let chat = {
	    	code: '2',
	    	sender: window.name,
	    	receiver: '',
	    	content: '',
	    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
	    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
	    };
	    
	  	ws.send(JSON.stringify(chat));
	    
	});
	
	window.onkeydown = function() {
	  
	    if(event.keyCode == 116) {
	        event.preventDefault();
	        return false;
	    }
	    
	};	
	
	/*
	
		101#hong#lee#안녕하세요.#2023... > 프로토콜 설계
	
		서버	<- (대화) -> 클라이언트
		
		- code: 상태코드
			1. 새로운 유저가 들어옴
			2. 기존 유저가 나감
			3: 메시지 전달
			4. 이모티콘 전달			
		- sender: 보내는 유저명
		- receiver: 받는 유저명
		- content: 멧지
		- regdate: 날짜/시간
	
	*/
	

	const url = 'ws://localhost/websocket/chatserver.do';
	
	let ws;
	
	var name;
	
	function connect(name) {
	    
		window.name = name;
		$('#header small').text(name);
		
		//연결하기
		ws = new WebSocket(url);
		
		ws.onopen = function(evt) {
		    log('서버 연결 성공');
		    
		    //내가 접속했다고 다른 사람들에게 알리기		
		    let chat = {
		    	code: '1',
		    	sender: window.name,
		    	receiver: '',
		    	content: '',
		    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
		    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
		    };
		    
		    ws.send(JSON.stringify(chat));
		    print('', '입장했습니다.', 'me', 'state', chat.regdate);
		    
		    $('#msg').focus();
		    
		};
		
		ws.onmessage = function(evt) {
		    
		    log('메시지 수신')
		    
		    let chat = JSON.parse(evt.data);
		    
		    log(chat.code + ':' + chat.sender);
		    
		    if(chat.code == '1') {
		        //다른 사람 입장
		        print('', `[\${chat.sender}]님이 입장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } else if (chat.code == '2') {
		        //다른 사람 퇴장
		        print('', `[\${chat.sender}]님이 퇴장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } 
		};
		
		ws.onclose = function(evt) {
		    
		};
		
		ws.onoerror = function(evt) {
		    
		};
		
	}
	
	function log(msg) {
	    console.log(`[\${new Date().toLocaleTimeString()}]` + msg);
	}

</script>
</body>
</html>


~ views > chat.jsp > 엔터 입력해서 메시지 전송하기
~ com.test.server > ChatServer.java > else if (dto.getCode().equals("3")) {} 구현
~ views > chat.jsp > 수신한 내용 출력

package com.test.server;

import java.util.ArrayList;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;
import com.test.domain.ChatDTO;


@ServerEndpoint("/chatserver.do")
public class ChatServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
		
		System.out.println(msg);
		
		//JSON <- (변환) -> Java Object
		// - GSON
		
		Gson gson = new Gson();
		
		ChatDTO dto = gson.fromJson(msg, ChatDTO.class);
		
		if (dto.getCode().equals("1")) {
			
			//새로운 유저가 접속했습니다. > 모든 사람에게 알림 (현재 접속자 제외)
			for(Session s : sessionList) {
				if(s != session) {
					try {
						s.getBasicRemote().sendText(msg);
						
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			
		} else if (dto.getCode().equals("2")) {

			//누군가가 퇴장 > 나머지 사람들에게 알려줌
			sessionList.remove(session); //서버 측에서의 퇴장
			
			for(Session s : sessionList) {
				
				try {
					s.getBasicRemote().sendText(msg);
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
			
		} else if (dto.getCode().equals("3")) {
			
			//대화 메시지를 모두에게 전달
			for (Session s : sessionList) {
				if(s != session) {
					
					try {
						s.getBasicRemote().sendText(msg);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
				}
			}
		} 
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}
	
}

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>

	$(window).on('beforeunload', function() {
		
	    $(opener.document).find('.in').css('opacity', 1);
	    $(opener.document).find('.in').prop('disabled', false);
	    
	  	//내가 나간다고 서버에게 메시지 보내기
	    let chat = {
	    	code: '2',
	    	sender: window.name,
	    	receiver: '',
	    	content: '',
	    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
	    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
	    };
	    
	  	ws.send(JSON.stringify(chat));
	    
	});
	
	window.onkeydown = function() {
	  
	    if(event.keyCode == 116) {
	        event.preventDefault();
	        return false;
	    }
	    
	};	
	
	/*
	
		101#hong#lee#안녕하세요.#2023... > 프로토콜 설계
	
		서버	<- (대화) -> 클라이언트
		
		- code: 상태코드
			1. 새로운 유저가 들어옴
			2. 기존 유저가 나감
			3: 메시지 전달
			4. 이모티콘 전달			
		- sender: 보내는 유저명
		- receiver: 받는 유저명
		- content: 멧지
		- regdate: 날짜/시간
	
	*/
	

	const url = 'ws://localhost/websocket/chatserver.do';
	
	let ws;
	
	var name;
	
	function connect(name) {
	    
		window.name = name;
		$('#header small').text(name);
		
		//연결하기
		ws = new WebSocket(url);
		
		ws.onopen = function(evt) {
		    log('서버 연결 성공');
		    
		    //내가 접속했다고 다른 사람들에게 알리기		
		    let chat = {
		    	code: '1',
		    	sender: window.name,
		    	receiver: '',
		    	content: '',
		    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
		    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
		    };
		    
		    ws.send(JSON.stringify(chat));
		    print('', '입장했습니다.', 'me', 'state', chat.regdate);
		    
		    $('#msg').focus();
		    
		};
		
		ws.onmessage = function(evt) {
		    
		    log('메시지 수신')
		    
		    let chat = JSON.parse(evt.data);
		    
		    log(chat.code + ':' + chat.sender);
		    
		    if(chat.code == '1') {
		        //다른 사람 입장
		        print('', `[\${chat.sender}]님이 입장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } else if (chat.code == '2') {
		        //다른 사람 퇴장
		        print('', `[\${chat.sender}]님이 퇴장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } else if (chat.code == '3') {
		        //대화 수신
		        print(chat.sender, chat.content, 'other', 'msg', chat.regdate);
		        
		    } 
		};
		
		ws.onclose = function(evt) {
		    
		};
		
		ws.onoerror = function(evt) {
		    
		};
		
	}
	
	function log(msg) {
	    console.log(`[\${new Date().toLocaleTimeString()}]` + msg);
	}
	
	function print(name, msg, side, state, time) {
	    let temp = `
	        <div class="item \${state} \${side}">
				<div>
					<div>\${name}</div>
					<div>\${msg}</div>
				</div>			
				<div>\${time}</div>
			</div>
	    `;
	    $('#list').append(temp);
	    scrollList();
	}
	
	$('#msg').keydown(function(evt) {
	   
	    if(evt.keyCode == 13) {
	        
	        let chat = {
			    	code: '3',
			    	sender: window.name,
			    	receiver: '',
			    	content: $('#msg').val(),
			    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
			    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
			    };
	        
        	ws.send(JSON.stringify(chat));
        	
        	if(chat.code == '3') {
        		print(window.name, chat.content, 'me','msg', chat.regdate);
        	}        	
        	
        	$('#msg').val('').focus();
	        
	    }
	        
	});
	
	function scrollList() {
	    $('#list').scrollTop($('#list')[0].scrollHeight + 500);
	}
		

</script>
</body>
</html>


~ views > chat.jsp > 이모티콘 전송
~ com.test.server > ChatServer.java > else if (dto.getCode().equals("4")) {} 구현
~ views > chat.jsp > printImoticon() 구현

package com.test.server;

import java.util.ArrayList;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;
import com.test.domain.ChatDTO;


@ServerEndpoint("/chatserver.do")
public class ChatServer {
	
	private static List<Session> sessionList;
	
	static {
		sessionList = new ArrayList<Session>();
	}
	
	@OnOpen
	public void handleOpen(Session session) {
		System.out.println("연결 성공");
		sessionList.add(session);
	}
	
	@OnMessage
	public void handleMessage(String msg, Session session) {
		
		System.out.println(msg);
		
		//JSON <- (변환) -> Java Object
		// - GSON
		
		Gson gson = new Gson();
		
		ChatDTO dto = gson.fromJson(msg, ChatDTO.class);
		
		if (dto.getCode().equals("1")) {
			
			//새로운 유저가 접속했습니다. > 모든 사람에게 알림 (현재 접속자 제외)
			for(Session s : sessionList) {
				if(s != session) {
					try {
						s.getBasicRemote().sendText(msg);
						
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			
		} else if (dto.getCode().equals("2")) {

			//누군가가 퇴장 > 나머지 사람들에게 알려줌
			sessionList.remove(session); //서버 측에서의 퇴장
			
			for(Session s : sessionList) {
				
				try {
					s.getBasicRemote().sendText(msg);
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
			
		} else if (dto.getCode().equals("3")) {
			
			//대화 메시지를 모두에게 전달
			for (Session s : sessionList) {
				if(s != session) {
					
					try {
						s.getBasicRemote().sendText(msg);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
				}
			}
		} else if (dto.getCode().equals("4")) {
			
			//이모티콘 메시지를 모두에게 전달
			for (Session s : sessionList) {
				if(s != session) {
					
					try {
						s.getBasicRemote().sendText(msg);
					} catch (Exception e) {
						e.printStackTrace();
					}
					
				}
			}
		}
		
	}
	
	@OnClose
	public void handleClose() {
		
	}
	
	@OnError
	public void handleError(Throwable e) {
		
	}
	
}

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
<link rel="stylesheet" href="/websocket/resources/css/chat.css">
<style>

</style>
</head>
<body>

	<div id="main">
		<div id="header">
			<h2>WebSocket <small></small></h2>
		</div>
		<div id="list">			
		</div>
		<input type="text" id="msg" placeholder="대화 내용을 입력하세요.">
	</div>


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.8/dayjs.min.js"></script>
<script>

	$(window).on('beforeunload', function() {
		
	    $(opener.document).find('.in').css('opacity', 1);
	    $(opener.document).find('.in').prop('disabled', false);
	    
	  	//내가 나간다고 서버에게 메시지 보내기
	    let chat = {
	    	code: '2',
	    	sender: window.name,
	    	receiver: '',
	    	content: '',
	    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
	    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
	    };
	    
	  	ws.send(JSON.stringify(chat));
	    
	});
	
	window.onkeydown = function() {
	  
	    if(event.keyCode == 116) {
	        event.preventDefault();
	        return false;
	    }
	    
	};	
	
	/*
	
		101#hong#lee#안녕하세요.#2023... > 프로토콜 설계
	
		서버	<- (대화) -> 클라이언트
		
		- code: 상태코드
			1. 새로운 유저가 들어옴
			2. 기존 유저가 나감
			3: 메시지 전달
			4. 이모티콘 전달			
		- sender: 보내는 유저명
		- receiver: 받는 유저명
		- content: 멧지
		- regdate: 날짜/시간
	
	*/
	

	const url = 'ws://localhost/websocket/chatserver.do';
	
	let ws;
	
	var name;
	
	function connect(name) {
	    
		window.name = name;
		$('#header small').text(name);
		
		//연결하기
		ws = new WebSocket(url);
		
		ws.onopen = function(evt) {
		    log('서버 연결 성공');
		    
		    //내가 접속했다고 다른 사람들에게 알리기		
		    let chat = {
		    	code: '1',
		    	sender: window.name,
		    	receiver: '',
		    	content: '',
		    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
		    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
		    };
		    
		    ws.send(JSON.stringify(chat));
		    print('', '입장했습니다.', 'me', 'state', chat.regdate);
		    
		    $('#msg').focus();
		    
		};
		
		ws.onmessage = function(evt) {
		    
		    log('메시지 수신')
		    
		    let chat = JSON.parse(evt.data);
		    
		    log(chat.code + ':' + chat.sender);
		    
		    if(chat.code == '1') {
		        //다른 사람 입장
		        print('', `[\${chat.sender}]님이 입장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } else if (chat.code == '2') {
		        //다른 사람 퇴장
		        print('', `[\${chat.sender}]님이 퇴장했습니다.`, 'other', 'state', chat.regdate);
		        
		    } else if (chat.code == '3') {
		        //대화 수신
		        print(chat.sender, chat.content, 'other', 'msg', chat.regdate);
		        
		    } else if (chat.code == '4') {
		        //이모티콘 수신
		        printEmoticon(chat.sender, chat.content, 'other', 'msg', chat.regdate);
		        
		    }
		};
		
		ws.onclose = function(evt) {
		    
		};
		
		ws.onoerror = function(evt) {
		    
		};
		
	}
	
	function log(msg) {
	    console.log(`[\${new Date().toLocaleTimeString()}]` + msg);
	}
	
	function print(name, msg, side, state, time) {
	    let temp = `
	        <div class="item \${state} \${side}">
				<div>
					<div>\${name}</div>
					<div>\${msg}</div>
				</div>			
				<div>\${time}</div>
			</div>
	    `;
	    $('#list').append(temp);
	    scrollList();
	}
	
	function printEmoticon(name, msg, side, state, time) {
	    let temp = `
	        <div class="item \${state} \${side}">
				<div>
					<div>\${name}</div>
					<div style="background-color:#FFF;border:0;"><img src="/websocket/resources/emoticon/\${msg}.png"></div>
				</div>			
				<div>\${time}</div>
			</div>
	    `;
	    $('#list').append(temp);
	    
	    setTimeout(scrollList, 100);
	}
	
	$('#msg').keydown(function(evt) {
	   
	    if(evt.keyCode == 13) {
	        
	        let chat = {
			    	code: '3',
			    	sender: window.name,
			    	receiver: '',
			    	content: $('#msg').val(),
			    	//JavaScript 날짜 시간 라이브러리(moment.js, day.js)
			    	regdate: dayjs().format('YYYY-MM-DD HH:mm:ss')
			    };
	        
	        //이모티콘 전송
	        if($('#msg').val().startsWith('/')) {
	        	chat.code = '4';
	        	chat.content = chat.content.split(' ')[0];
	        }
	            
	        
        	ws.send(JSON.stringify(chat));
        	
        	if(chat.code == '3') {
        		print(window.name, chat.content, 'me','msg', chat.regdate);
        	} else if (chat.code == '4') {
        		printEmoticon(window.name, chat.content, 'me','msg', chat.regdate);
        	}
        	
        	
        	$('#msg').val('').focus();
	        
	    }
	        
	});
	
	function scrollList() {
	    $('#list').scrollTop($('#list')[0].scrollHeight + 500);
	}
		

</script>
</body>
</html>