상세 컨텐츠

본문 제목

초간단 WebSocket 채팅

스프링

by e7e 2022. 4. 28. 00:20

본문

Http(Https) 프로토콜은 단방향이당. 곧  클라이언트 request(요청)에  서버가 response(응답)

하면 끝이당. 클라이언트만 요청할 수 있공, 서버는 요청할 수 없당. 뭔가 불편하당.

그래서 나왔당.  WebSocket 양방향 통신, 프로토콜은 ws(wss)로 적는당.

최초에는 클라이언트가 연결 요청을 하지만, 일단 연결이 되면, 서버도 클라이언트에게

연락을 보낼 수 있당.  왜인지 재미가 이슬꺼 같당.(취향이 아니라면 할 수 없당!)

 

websocket은 실시간 알림 기능(댓글 달렸어용~ 종이 딸랑딸랑 움직임등)에 사용하면 있어보인당.

setTimeInterval로 억지로 주기적으로 돌리는 건(pooling) 좋지 않앙.

 

설정부터 가보장.  먼저 스프링에서 websocket을 사용하려면,

pom.xml 에 sprin-websocket 라이브러리를 추가해야 함은 기본이당.

 

pom.xml

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

 

라이브러리를 추가했으닝,  자 중계탑(나 만의 용어)을 설정해 보장.

servlet-context.xml에   그냥 넣어도 아무도 뭐라 안하지만, 보기도 좋아야 하닝

websocket-context.xml로 맹글고, import 구문을 이용하여 넣어본당.

 

websocket-context.xml

	<bean id="chatHandler"  class="패키지명.ChatHandler" />
	<websocket:handlers allowed-origins="*">
		<websocket:mapping handler="chatHandler" path="/ws-chat" />
		<websocket:handshake-interceptors>
			<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor" />
		</websocket:handshake-interceptors>
	</websocket:handlers>

servlet-context.xml

	<context:component-scan base-package="com.e7e.chat" />
    <!-- component-scan 뒤에 넣어야 낸중에 autowired 쓸때 편하당 -->
	<beans:import resource="websocket-context.xml"/>

참고롱 websocket-context.xml을 root-context.xml에 추가하면

@Service등의 어노테이션을 사용할 때, 순서에 영향을 받아, component-scan이 낭비될 수 있당.

(결론은 그냥 component-scan 아래에서 websocket-context.xml을 import하면 좋다는 이야깅!)

 

END-POINT  /ws-chat(이름은 내맘)으로 들어오고 나가는 메세지를 컨트롤할 핸들러 맹금

HttpSessionHandshakeInterceptor WebSocketSession과 HttpSession이 별도로 존재해성

아주 불편한뎅, 엔드포인트로 가기전에  중간에 인터셉트해성  HttpSession에 있는 값들을

WebSocketSession의 attribute로 넣어준당. 

따라서 값을 꺼낼 때는 getAttribute 메소드를 이용해야 한당.(느낌이 안 올 쑤 있당. 괜찮당!) 

 

END-POINT  /ws-chat 로 밀려오는 연락들을 처리해 줄 핸들러 ChatHandler를 맹글장

 

ChatHandler.java


@Slf4j
public class ChatHandler extends TextWebSocketHandler {
	private static List<WebSocketSession> list = new ArrayList<WebSocketSession>();
	
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		log.info("## 누군가 접속");
		list.add(session);
	}

	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		//String uMsg = message.getPayload();
		for (WebSocketSession webSocketSession : list) {
			webSocketSession.sendMessage(message);
		}
	}
	
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		log.info("## 누군가 떠남");
		list.remove(session);
	}
}

 

myChat.jsp를 불러 줄 Controller를 1개 맹글어 주장.

ChatController.java

@Controller
@RequestMapping("/")
public class ChatController {

	@GetMapping("/chat")
	public String chatChat() {
		return "myChat";
	}
}

 

서버쪽은 되었고,  클라이언트 웹소켓 코드를 가진 myChat.jsp를 맹글어 주장.

myChat.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>초우간단 채팅</title>
<style>
	#id_chatwin {
		width:300px;
		height:300px;
		background-color:black;
		border:1px solid pink;
		color:yellow;
	}
</style>
</head>
<body>
	<h1>간단히 억지롱 대화라도 할깡</h1>
	<div>
		<div id="id_chatwin"></div>
		<input type="text" id="id_message" /> 
		<input type="button" id="id_send" value="떤쏭"> 
		<input type="button" id="id_exit" value="나갈령">
	</div>
</body>
<script>
        //그냥 띰띰해서 맹근 랜덤 아이디 맹그는 함쑹
	    const c_alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		const f_ranID=()=>{
			let ranID ="";
			for(let i=1; i< (Math.ceil(Math.random()*5)+4); i++){
				ranID += c_alpha[Math.floor(Math.random()*c_alpha.length)];
			}
			return ranID;
		}

		let webSocket; // 페이지 바뀌면 변수가 사라진다는 사실에 주목할 필요가 있음
		let myId = f_ranID();
		const c_chatWin = document.querySelector("#id_chatwin");
		const c_message = document.querySelector("#id_message");		
		const c_send = document.querySelector("#id_send");
		const c_exit = document.querySelector("#id_exit");

		c_send.addEventListener("click", ()=>{
			send();
		});
		// 연결 끊깅
		c_exit.addEventListener("click", function () {
			disconnect();
		});

		//연결
		connect();
		function connect() {
			webSocket = new WebSocket("ws://localhost:8080/e7e/ws-chat"); // End Point
			//이벤트에 이벤트핸들러 뜽록 
			webSocket.onopen = fOpen; // 소켓 접속되면 짜똥 실행할 함수(fOpen)
			webSocket.onmessage = fMessage; // 써버에서 메쎄징 오면  짜똥 실행할 함수(fMessage) 
		}

		//연결 시
		function fOpen() {
			webSocket.send(myId + "님 이프짱.");
		} 
		function send() {  // 써버로 메쎄찡 떤쏭하는 함수
			let msg = c_message.value;
			webSocket.send(myId + ":" + msg);
			c_message.value = "";
		}
		function fMessage() {
			let data = event.data;
			c_chatWin.innerHTML = c_chatWin.innerHTML + "<br/>" + data;
		}
		function disconnect() { //써버와 인연 끊는 함쑹
			webSocket.send(myId + "님이 뛰쳐나갔쪙");
			webSocket.close();
		}
</script>
</body>
</html>

 

여기선 그냥 문자열을 주고 받았는뎅,  실제로 프로그램을 만들려면

JSON <-> 자바VO 변환을 위해서 Jackson 라이브러의 ObjectMapper 클래스의 

writeValueAsString(자바VO)         ->  자바 VO를 JSON문자열로 변환

readValue(json문자열변수, 자바VO.class)   ->  JSON문자열을 자바VO로 변환

메소드를 사용하고, 주고받는 메세지의 필요한 속성들을 모아서 

VO 클래스를 맹글어 준다면,  통신을 아주 편하게 할 수 있으며,

 

핵심포인트는 웹소켓과 AJAX를 잘 섞어써야 필요한 기능을 쉽게 구현할 수

있는 것시당.

 

참고롱 부트롱 설정한다면 대략 아래와 같을 꺼시당

@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
   
   @Autowired
   private final ChatHandler chatHandler;
   
   @Override
   public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
      registry.addHandler(chatHandler, "/ws-chat").setAllowedOrigins("*")
      .addInterceptors(new HttpSessionHandshakeInterceptor());
   }
   
}

 

주고받는 데이터구조(모습, 간략히 무엇 무엇을 담아서 주고받을 지?)를 결정하는 

시행착오를 꼬옥 겪어보는 시간을 가져보길 초강력 추천한당.(얻는 거시 분명 마늘거당) 

 

느낌이 오길 기도가 막히도록 기도 해본당!

 

https://www.youtube.com/watch?v=RsELrcVNzG0 

 

'스프링' 카테고리의 다른 글

추가 기본 annotation  (2) 2022.04.29
WYSIWYG 에디터 CKEditor4 이미지 업 설정  (1) 2022.04.29
MVC이해  (0) 2022.04.27
HttpClient  (5) 2022.04.27
SQL로그 출력  (0) 2022.04.17

관련글 더보기