카이스트 몰입캠프 2주차 프로젝트 결과물인 몰 봐(MV) 서비스의 핵심인 "댓글 기반 팟 모집 -> 채팅방 생성 -> 실시간 채팅" 로직을 바탕으로 웹소켓(WebSocket) 구현 성공 기록기...
단순 웹소켓만 쓰면 로우 레벨이라 메시지 형식을 일일이 다 정해야 함
그래서 우리는 STOMP 규격 사용했음
구조: 메일함처럼 특정 주소를 '구독(Sub)'하고, 그 주소로 메시지를 '발행(Pub)'하는 방식임.
장점: 메시지 브로커를 통해 채팅방별로 메시지를 꽂아주기가 훨씬 편함.
Publisher(발행자): 채팅 메시지를 보내는 사람 (클라이언트)
Subscriber(구독자): 채팅방에 들어와서 메시지를 받는 사람 (클라이언트)
Broker(중개자): 메시지를 받아서 구독 중인 사람들에게 뿌려주는 서버
먼저 의존성에 spring-boot-starter-websocket 추가되어 있어야 함.
[WebSocketConfig.java]
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 메시지 받을 때: /sub/chat/room/1 이런 식으로 구독함
config.enableSimpleBroker("/sub");
// 메시지 보낼 때: /pub/chat/message 이런 식으로 보냄
config.setApplicationDestinationPrefixes("/pub");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws") // 프론트가 접속할 엔드포인트
.setAllowedOrigins("https://www.example.com") // 프론트 도메인 필수
.withSockJS(); // 낮은 버전 브라우저 대응
}
}
글쓴이가 게시글(Post) 댓글에서 선택해서 팟 참여 확정 -> 채팅방 생성 구조로 기획을 했음..
ChatRoom 만들고 선택된 인원들을 ChatMember로 등록함.roomId를 받아 해당 경로(/sub/chat/room/{id})를 구독함./pub/chat/message로 JSON 데이터를 전송[ChatController.java]
@Controller
@RequiredArgsConstructor
public class ChatController {
private final SimpMessageSendingOperations messagingTemplate;
@MessageMapping("/chat/message") // 프론트가 /pub/chat/message로 보낼 때
public void message(ChatMessageDto message) {
// DB에 채팅 내역 저장하는 로직 추가 가능
// 구독 중인 사람들에게 메시지 전달
messagingTemplate.convertAndSend("/sub/chat/room/" + message.getRoomId(), message);
}
}
① 보안 및 인증 (가장 많이 막히는 부분)
connect)에 쿼리 파라미터로 토큰을 보내거나, STOMP의 connectCallback 헤더에 JWT 토큰을 실어 보내야 함. 백엔드에서 ChannelInterceptor를 구현해서 토큰 검증 로직 따로 짰음② CORS 에러
.addEndpoint("/ws"))에도 .setAllowedOrigins 설정을 정확히 해줘야 함. 안 그러면 프론트에서 접속 못 함......③ 도메인 연결 (HTTPS/WSS)
ws://가 아니라 wss://로 접속해야 함.Member ID 리스트를 받아서 ChatMember(중간 테이블)에 저장해야 했음. 그래야 나중에 "내 채팅방 목록" 조회할 때 내가 속한 방만 가져올 수 있었음유저마다 last_read_at(마지막 읽은 시간)을 필드로 둠. 채팅방 목록 불러올 때 이 시간 이후로 쌓인 메시지 개수를 쿼리로 날려서 "안 읽은 메시지 N개" 기능을 구현했음..