웹 소켓(WebSockets)
- 클라이언트와 서버 간의 실시간 양방향 통신을 가능하게 하는 프로토콜입니다.
- 일반적인 HTTP 요청과 달리, 웹 소켓은 연결이 열리면 계속 열려 있어, 클라이언트와 서버 간의 데이터 전송이 빠르고 지속적으로 이루어질 수 있습니다.
Stromp 프로토콜(Stomp over WebSockets)
- 웹 소켓을 기반으로 한 메시징 프로토콜로, 클라이언트와 서버 간의 실시간 메시징을 지원합니다.
- Stomp은 간단하고 효율적인 텍스트 기반 프로토콜로, 다양한 클라이언트와 서버에서 쉽게 구현할 수 있습니다.
1. View
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
<!-- stromp 외부 스크립트 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
</head>
<body>
<div>
<ul>
<li><a href="/">채팅목록</a></li>
<li><a href="/save-form">채팅 메시지쓰기</a></li>
</ul>
</div>
<h1>채팅 목록</h1>
<hr>
<ul id="chat-box">
{{#models}}
<li>{{msg}}</li>
{{/models}}
</ul>
<script>
// 1. 웹소켓 연결 세팅 및 연결 완료
let socket = new WebSocket('ws://ip주소/connect');
let stompClient = Stomp.over(socket);
stompClient.connect({}, (frame)=>{
console.log("1", "Connected");
stompClient.subscribe("/sub/chat", (response)=>{
console.log("2", response);
let body = JSON.parse(response.body);
console.log("3", body);
attack(body.msg);
});
});
function attack(msg){
// 1. body 초기화
document.querySelector("body").innerHTML = "";
// 2. body 스타일 설정
document.querySelector("body").style.cssText = `
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: black;
`;
// 3. 글자 요소 생성
const textElement = document.createElement("div");
textElement.textContent = msg; // 텍스트 내용
textElement.style.cssText = `
color: white;
font-size: 50px;
font-family: Arial, sans-serif;
`;
// 4. body에 추가
document.querySelector("body").appendChild(textElement);
}
</script>
</body>
</html>
2. Form
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<div>
<ul>
<li><a href="/">채팅목록</a></li>
<li><a href="/save-form">채팅 메시지쓰기</a></li>
</ul>
</div>
<h1>채팅 메시지 쓰기</h1>
<hr>
<form action="//ip주소:8080/chat" method="post">
<input type="text" name="msg">
<button type="submit">전송</button>
</form>
</body>
</html>
3. 웹 소켓 설정
WebSocketConfig.java
@EnableWebSocketMessageBroker
@Configuration
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// 웹소켓 연결 엔드포인트 설정
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 구독, 발행 전 최초 소켓 연결을 위한 주소
registry.addEndpoint("/connect")
.setAllowedOrigins("*"); // 모든 주소에서 CORS 차단 해제
}
// 구독, 발행 엔드포인트 설정
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 판매자 (메세지 브로커)
registry.enableSimpleBroker("/sub"); // 구독 주소, 응답내용이 /sub로 시작하면 발행
registry.setApplicationDestinationPrefixes("/pub"); // 발행 주소, pub로 시작하는 모든 주소(prefix 설정)
}
}
4. 컨트롤러
@RequiredArgsConstructor
@Controller
public class ChatController {
private final ChatService chatService;
private final SimpMessageSendingOperations sms;
@GetMapping("/save-form")
public String saveForm(){
return "save-form";
}
@GetMapping("/")
public String index(Model model) {
model.addAttribute("models", chatService.findAll());
return "index";
}
@PostMapping("/chat")
public String save(String msg) {
Chat chat = chatService.save(msg);
sms.convertAndSend("/sub/chat", chat); // 객체로 넣으면 JSON으로 바꿔서 보낸다.
return "redirect:/";
}
// /pub/room
@MessageMapping("/room") // pub/r1에서 pub 생략
public void pubTest(String number) {
System.out.println("나 요청돼? " + number);
// private final SimpMessageSendingOperations sms; 생성자 주입
// "/sub/" + number -> 특정 번호의 사람에게 알림 보내기 가능
sms.convertAndSend("/sub/" + number, "hello world " + number);
}
}
5. 그 외
Chat.java
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "chat_tb")
@Entity
public class Chat {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String msg;
@Builder
public Chat(Integer id, String msg) {
this.id = id;
this.msg = msg;
}
}
ChatRepository.java
@Repository
public interface ChatRepository extends JpaRepository<Chat, Integer> { }
ChatService.java
@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class ChatService {
private final ChatRepository chatRepository;
@Transactional
public Chat save(String msg) {
Chat chat = Chat.builder().msg(msg).build();
return chatRepository.save(chat);
}
public List<Chat> findAll(){
// 순서를 내림차순으로 받기 위해서
Sort desc = Sort.by(Sort.Direction.DESC, "id");
return chatRepository.findAll(desc);
}
}
6. 실제 화면

- 웹 소켓으로 양방향 통신이 연결되면 상대방이 보여주는 메세지를 그대로 봐야 합니다.
→ 스팸 문자 주소를 클릭하면 안 되는 이유입니다.
Share article