2023. 7. 25. 20:20ㆍSPRING
(1) 웹 소켓이란?
- 소켓은 웹에서 동작하는 Socket을 의미한다. 웹은 기본적으로 비연결통신이다. 한번 수신하고 끝내는 식이다. 전화처럼 계속 연결되어있고, 실시간으로 소통하는 것이 아니라 문자 한번 보내면 연결은 끝난다. 웹은 이런 식으로 비연결형 통신이다.
- 웹은 일회성 연결만 가능하다.
- 소켓은 계속 연결되어 있다. 마치 종이컵 두 개를 가지고, 전화할 수 있는 것이 웹소켓이다. 채팅할 때도 필요하고, 실시간 데이터가 관리되어야 하는 빗썸과 같은 가상자산 플랫폼에서는 소켓을 많이 활용한다.
- 웹소켓을 활용하면 서버가 클라한테 마음대로 데이터를 받으라 할 수 있다.
- 클라이언트가 서버한테 "정보 좀 주세요~" 하지 않아도 서버가 알아서 데이터를 보내줄 수 있다. 이게 엄청난 장점.
- 소켓을 사용하지 않는다면, ajax를 사용해야 하지만 그건 비효율적이다.
- 양방향으로 실시간으로 소통 가능하려면 .... 서버 측에서도 메시지를 보낼 수 있어야 한다.
(2) 스프링에서 웹소켓 사용
- pom.xml 수정을 해줘야함
- dependency 추가
- dependency를 추가한 다음에, 라이브러리를 사용해야함
- 라이브러리를 사용하는 방법은 bean을 등록해서 사용하거나 import 해서 사용할 수 있음(스프링 자체의 버전과 동일하게 맞춰줘야함)
1.상속
2.메소드 오버라이딩
- KhChat 클래스 만들어서 라이브러리를 먼저 활용하여 코드 작성함.
- bean으로 등록하긴 할건데, KhChat 빈 등록함. -> servletContext에 담기
- 맵핑(해당 bean이 언제 동작할지 맵핑 설정)
(3) 실습 코드
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>채팅 메인</title>
</head>
<body>
<h1>채팅</h1>
<input type="text" id="msg">
<button onclick="f01();">전송</button>
<hr>
<div id="result"></div>
<script type="text/javascript">
const resultDiv = document.querySelector("#result");
//웹소켓 만들기
//()안에 연결할 경로 지정하기
//http:// 는 비연결형 통신방식이기 때문에 이 경로를 사용하면 안됨
//그래서 웹소켓 프로토콜을 사용해야 한다.
//그래서 앞에 ws:를 다는 것이다.
let ws = new WebSocket("ws://200.200.200.3:8888/app/test");
//웹소켓이 오픈되었을때
ws.onopen = funcOpen;
//웹소켓 연결이 끝났을때
ws.onclose = funcClose;
//웹소켓 에러가 날때
ws.onerror = funcError;
//웹소켓 메시지 받았을때
ws.onmessage = funcMessage;
//웹소켓 오픈되었을때 실행되는 함수
function funcOpen() {
console.log("소켓 연결됨");
}
//웹소켓 닫았을때 실행되는 함수
function funcClose() {
console.log("소켓 닫힘");
}
//웹소켓 에러가 날 때 실행되는 함수
function funcError() {
console.log("소켓 에러남");
}
//웹소켓 메시지 받았을때 실행되는 함수
function funcMessage(event) {
console.log("소켓 통해서 메시지 받음");
//받은 데이터를 파싱하기
const data = JSON.parse(event.data);
resultDiv.innerHTML += '<div>'+"<strong>["+ data.nick +"]</strong>"+"<span>"+ data.msg+"</span>"+"<sub>"++"</sub>"+'</div>';
}
//버튼 클릭시 웹소켓 메시지 보내기
function f01() {
const userMsg = document.querySelector("#msg").value;
ws.send(userMsg);
}
</script>
</body>
</html>
java
servlet-context.xml
<!-- 웹소켓 bean 등록 -->
<beans:bean id="khWebsocketServerBean" class="com.kh.app.websocket.server.WebsocketServer"></beans:bean>
<!-- 웹소켓 핸들러 맵핑 -->
<websocket:handlers>
<websocket:mapping handler="khWebsocketServerBean" path="/test"/>
<!-- 웹소켓 세션에다가 httpsession을 옮기도록 도와주는 handshaker -->
<websocket:handshake-interceptors>
<beans:bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"></beans:bean>
</websocket:handshake-interceptors>
</websocket:handlers>
webSocketServer.java
package com.kh.app.websocket.server;
import java.sql.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
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 lombok.extern.slf4j.Slf4j;
@Slf4j
public class WebsocketServer extends TextWebSocketHandler {
//유저들의 세션을 담을 set
private Set<WebSocketSession> sessionSet = new HashSet<WebSocketSession>();
//connection이 되었을때 되고 난이후에 동작하는 메소드
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("called...afterConnectionEstablished");
//sessionSet에 세션 저장(여러명이 연결)
sessionSet.add(session);
}
//connection이 closed되고 난 이후에 동작하는 메소드
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("called...afterConnectionClosed");
//sessionsSet에 저장된 세션 삭제
sessionSet.remove(session);
}
//텍스트 메시지를 다루는 메소드
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
log.info("called...handleTextMessage");
String nick = (String)session.getAttributes().get("loginMember");
//json으로 데이터를 파싱하기
Gson gson = new Gson();
//맵 만들어서, 키 밸류 형태로 데이터를 준비하기
//만들어진 맵을 JSON 형태로 변환
//변환된 데이터를 send하기
Map<String, String> msgVo = new HashMap<>();
msgVo.put("nick", nick);
msgVo.put("msg", message.getPayload());
//이 시각이 발신시각은 아님
//발신시각을 받으려면 클라이언트 측에서 시간 값도 같이 보내줘야함
//long 타입이라서 String type으로 변환
msgVo.put("time", new Date() + "");
String jsonStr = gson.toJson(msgVo);
//전달받은 메세지를 모두에게 뿌려주기
//연결된 모든 세션을 가져와서 send 해주기
for(WebSocketSession s : sessionSet) {
s.sendMessage(new TextMessage(jsonStr));
}
}
}
pom.xml
<!-- 웹소켓 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
(4) 데이터 해석
INFO : com.kh.app.websocket.server.WebsocketServer - 클라이언트로부터 전달받은 메시지 : TextMessage payload=[ㅋㅋㅋ 안녕~~~!], byteCount=20, last=true]
- send()해서 받은 데이터를 찍어보면 이렇다. last는 이 메시지가 마지막인지를 알려준다. 엄청난 양의 데이터를 보내면 그 데이터가 여러번에 끊어서 갈 것이다. 마지막 데이터가 들어왔는지를 확인하기 위한 목적으로 'last'가 존재한다.
(5) 그룹채팅
- 서버에서는 연결된 모든 사람들을 알아야함
- 그리고 메세지를 받으면, 연결된 모든 곳에 뿌리기(broadCast)
- HTTPSESSION 정보를 WebSocket 세션이 넘기기
1. servlet-context에 handshaker를 추가해주면 된다.
'SPRING' 카테고리의 다른 글
스프링 설정 파일 template (0) | 2023.06.19 |
---|---|
생명주기(Life Cycle) (0) | 2023.04.19 |
의존객체 선택 (0) | 2023.03.23 |
의존객체 자동 주입 (0) | 2023.03.23 |
스프링 설정 파일 분리 (0) | 2023.03.22 |