| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
- js
- 배열
- SpringBoot
- 자바문법
- Eclipse
- Visual Studio Code
- 정규식
- 자바
- html
- 단위테스트
- 문자열
- math
- ArrayList
- HashMap
- Array
- input
- string
- Java
- 테스트자동화
- java테스트
- 인텔리제이
- 자바스크립트
- CSS
- junit5
- 스프링부트
- junit
- vscode
- javascript
- list
- IntelliJ
- Today
- Total
어제 오늘 내일
[Spring Boot] "새로고침 없이 알림이 오네요?" WebSocket과 STOMP로 실시간 채팅방 만들기 본문
[Spring Boot] "새로고침 없이 알림이 오네요?" WebSocket과 STOMP로 실시간 채팅방 만들기
hi.anna 2026. 3. 30. 01:58
카카오톡, 슬랙, 주식 시세창의 공통점은 무엇일까요?
내가 가만히 있어도 새로운 정보가 화면에 뿅! 하고 나타난다는 것입니다.
HTTP가 무전기(단방향)라면, WebSocket은 전화기(양방향)입니다.
오늘은 스프링 부트에서 이 웹소켓을 이용해 간단한 실시간 채팅방을 구현해 보겠습니다.
1. WebSocket만 쓰면 안 되나요? (STOMP의 필요성)
웹소켓은 그냥 "통신 파이프"만 뚫어줄 뿐입니다.
빨대만 꽂아놓고 "안녕?"이라고 보내면, 서버는 이게 귓속말인지, 전체 공지인지, 채팅방 1번인지 알 방법이 없습니다.
그래서 우리는 규칙(프로토콜)이 필요합니다. 그게 바로 STOMP (Simple Text Oriented Messaging Protocol)입니다.
STOMP는 "주소(Topic)" 개념을 도입해서 메시지를 배달해 줍니다.
- Pub (발행): "1번 채팅방(/topic/room/1)에 메시지 보내줘!"
- Sub (구독): "나 1번 채팅방(/topic/room/1) 듣고 있을게!"
2. 설정하기 (의존성 추가)
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-websocket'
}
3. WebSocket 설정 (WebSocketConfig)
"어디로 연결해야 대화가 시작되는지(Endpoint)"와 "메시지 브로커(우체부)"를 설정합니다.
@Configuration
@EnableWebSocketMessageBroker // ★ STOMP 활성화
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 1. 연결 주소: ws://localhost:8080/ws-chat
registry.addEndpoint("/ws-chat")
.setAllowedOriginPatterns("*") // CORS 허용
.withSockJS(); // 구형 브라우저 지원 (필수!)
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 2. 메시지 구독 요청(Sub) 주소 접두사
// 클라이언트가 "/sub/chat/room/1"을 구독하면 브로커가 잡아서 처리함
registry.enableSimpleBroker("/sub");
// 3. 메시지 발행 요청(Pub) 주소 접두사
// 클라이언트가 "/pub/chat/message"로 보내면 @MessageMapping이 잡아서 처리함
registry.setApplicationDestinationPrefixes("/pub");
}
}
4. 메시지 DTO 만들기
주고받을 편지지를 정의합니다.
@Getter
@Setter
public class ChatMessage {
public enum MessageType {
ENTER, TALK
}
private MessageType type; // 입장인지, 대화인지
private String roomId; // 방 번호
private String sender; // 보낸 사람
private String message; // 내용
}
5. 컨트롤러 만들기 (ChatController)
@RequestMapping 대신 @MessageMapping을 씁니다. HTTP 컨트롤러랑 비슷해서 아주 쉽습니다.
@Controller
@RequiredArgsConstructor
public class ChatController {
private final SimpMessageSendingOperations messagingTemplate; // 메시지 보내는 도구
// 클라이언트가 "/pub/chat/message"로 보내면 이 메서드가 실행됨
@MessageMapping("/chat/message")
public void message(ChatMessage message) {
if (ChatMessage.MessageType.ENTER.equals(message.getType())) {
message.setMessage(message.getSender() + "님이 입장하셨습니다.");
}
// ★ 핵심: "/sub/chat/room/{roomId}"를 구독 중인 사람들에게 다 뿌리기!
messagingTemplate.convertAndSend("/sub/chat/room/" + message.getRoomId(), message);
}
}
6. 프론트엔드 (JavaScript) 맛보기
서버는 준비됐습니다. 클라이언트는 SockJS와 Stomp.js 라이브러리를 써서 연결합니다.
// 1. 연결
var sock = new SockJS("/ws-chat");
var ws = Stomp.over(sock);
// 2. 접속 시도
ws.connect({}, function(frame) {
// 3. 구독 (Subscribe) - 1번 방
ws.subscribe("/sub/chat/room/1", function(message) {
var recv = JSON.parse(message.body);
console.log("받은 메시지: " + recv.message);
});
// 4. 메시지 전송 (Publish)
ws.send("/pub/chat/message", {}, JSON.stringify({
type: 'TALK',
roomId: '1',
sender: '홍길동',
message: '안녕하세요!'
}));
});
이제 홍길동이 메시지를 보내면, 1번 방을 구독하고 있는 모든 사람(철수, 영희)의 콘솔창에 "안녕하세요!"가 동시에 뜹니다. 새로고침 없이요!
7. 더 나아가기 (External Broker)
스프링 부트 내장 브로커(SimpleBroker)는 메모리 기반이라서, 서버가 여러 대(Scale-out)가 되면 1번 서버에 붙은 홍길동의 말을 2번 서버의 영희가 못 듣습니다.
이때는 RabbitMQ나 Kafka 같은 외부 브로커(External Broker)를 붙여서 메시지를 공유해야 합니다. (이게 바로 대규모 채팅 시스템의 핵심입니다.)
마치며
오늘의 결론입니다.
- WebSocket은 실시간 양방향 통신을 가능하게 한다.
- STOMP를 쓰면 "구독(Sub)"과 "발행(Pub)" 구조로 쉽게 채팅방을 만들 수 있다.
@MessageMapping을 통해 HTTP 컨트롤러처럼 익숙하게 개발할 수 있다.
