실습 : 로그인 회원가입 api 설계, (Spring security 적용 및 JWT Token 발행) 가능하면 해보세요.
이론 : Websocket이란? 개념과 동작방식, Spring boot에서 사용 방법

= 서버와 클라이언트 간의 메시지 교환을 위한 통신규약(프로토콜)
양방향 통신(Full-Duplex)
= 데이터 송수신을 동시에 처리할 수 있는 방법으로, 클라이언트와 서버가 서로에게 원할 때 데이터를 주고 받을 수 있다.
실시간 네트워킹(Real Time-Networking)
= 웹 환경에서 연속된 데이터를 빠르게 노출이 가능하다.
ex) 채팅, 주식, 비디오 데이터
= 웹 소켓도 핸드 쉐이킹이 필요하고, Socket 프로토콜이 아닌 HTTP 또는 HTTPS 프로토콜을 통해 이루어진다.


이렇게 위와 같이 핸드 쉐이크가 완료되면 프로토콜이 ws로 변경된다.
또는 wss와 같이 데이터보안을 위해 SSL을 적용한 프로토콜로 변경된다.

implementation 'org.springframework.boot:spring-boot-starter-websocket'
= 어떤 요청에 대한 어떤 응답을 할 것인지에 대한 정의를 결정한다.
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
private final WebSocketHandler webSocketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/아무거나해요").setAllowedOrigins("*");
}
}
➡️ /아무거나해요 이부분은 나중에 서버 세팅을 끝난 후 실제로 테스트해볼 때 정해지는 url이다.
ex) ws://127.0.0.1:PORT/아무거나해요
그리고 setAllowedOrigins("*")는 웹소켓 cors 정책으로 인해 허용 도메인으로 인해 허용 도메인을 지정해줘야 한다.
'*'는 와일드 카드로 모든 도메인을 열어준다. 실제 개발에는 사용하지 않지만(보안문제) 현재는 테스트이기 때문에 사용한다.
@Component
public class WebSocketHandler extends TextWebSocketHandler {
private static final ConcurrentHashMap<String, WebSocketSession> CLIENTS = new ConcurrentHashMap<String, WebSocketSession>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
CLIENTS.put(session.getId(), session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
CLIENTS.remove(session.getId());
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String id = session.getId(); //메시지를 보낸 아이디
CLIENTS.entrySet().forEach( arg->{
if(!arg.getKey().equals(id)) { //같은 아이디가 아니면 메시지를 전달합니다.
try {
arg.getValue().sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
TextWebSocketHandler를 상속받게 되면, 3개의 메소드를 오버라이딩 해야한다.
private static final ConcurrentHashMap<String, WebSocketSession> CLIENTS = new ConcurrentHashMap<String, WebSocketSession>();
해당 코드는 CLIENTS라는 변수에 세션을 담아두기위한 맵 형식의 공간이다.
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
CLIENTS.put(session.getId(), session);
}
사용자가 웹소켓 서버에 접속하게 되면 동작하는 메소드이다.
이때 WebSocketSession 값이 생성되는데 그 값을 위에서 미리 만들어준, CLIENTS 변수에 put으로 담아준다. (키 값은 세션의 고유값이다.)
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
CLIENTS.remove(session.getId());
}
해당 코드는 웹소켓 서버접속이 끝났을 때 동작하는 메소드이다.
이때 CLIENTS 변수에 있는 해당 세션을 제거한다.
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String id = session.getId(); //메시지를 보낸 아이디
CLIENTS.entrySet().forEach( arg->{
if(!arg.getKey().equals(id)) { //같은 아이디가 아니면 메시지를 전달합니다.
try {
arg.getValue().sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
해당 코드는 사용자의 메시지를 받게되면 동작한는 메소드이다.
CLIENTS 변수에 담긴 세션값들을 가져와서 반목문으로 돌려서, 위처럼 메세지를 발송해주면, 본인 이외의 사용자에게 메시지를 보낼 수 있는 코드가 된다.