이 전 포스팅에서 웹소켓이 무엇이고 웹소켓에 jwt를 추가하는 글을 작성했었는데, 이 번 글에서는 Spring Boot를 사용해 WebSocket 기반의 실시간 통신을 구현하는 과정을 공유합니다. 이 글에서는 WebSocket 설정, 클라이언트와의 메시징 경로, 그리고 JWT 기반의 인증 처리까지 함께 다룰 예정입니다.
■ Gradle 설정
Spring Boot에서 WebSocket을 사용하려면 의존성을 추가해야 합니다. Gradle의 build.gradle 파일에 아래 내용을 추가하세요:
implementation 'org.springframework.boot:spring-boot-starter-websocket'
■ 메시지 전송 코드
아래는 WebSocket을 통해 메시지를 전송하는 서비스 클래스입니다.
SimpMessagingTemplate을 사용해 특정 사용자 또는 모든 사용자에게 메시지를 전송합니다.
## MessageSender.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
@Service
public class MessageSender {
@Autowired
private SimpMessagingTemplate messagingTemplate;
// 특정 사용자에게 메시지 전송
public void sendMessageToUser(String userId, String message) {
messagingTemplate.convertAndSendToUser(userId, "/queue/match", message);
}
// 모든 클라이언트에게 메시지 전송 (브로드캐스트)
public void sendBroadcastMessage(String topic, String message) {
messagingTemplate.convertAndSend("/topic/" + topic, message);
}
}
핵심 내용:
- 특정 사용자에게 메시지 전송:
- convertAndSendToUser 메서드는 /user/{userId}/queue/match 경로로 메시지를 보냅니다.
- 내부적으로 /user 경로가 기본적으로 붙습니다. 이는 Spring의 SimpMessagingTemplate의 기본 동작입니다.
- 실제 코드는 다음과 같이 동작(org.springframework:spring-messaging:6.1.10:SimpleMessagingTemplate.java: convertAndSendToUser )
- 브로드캐스트 메시지 전송:
- /topic/{topic} 경로로 메시지를 전송하여, 해당 경로를 구독 중인 모든 사용자에게 메시지를 보냅니다.
■ WebSocket 설정
WebSocket 설정은 클라이언트와 서버 간의 경로를 정의하고, 메시징과 인증 처리 방식도 설정합니다. 아래는 WebSocket 설정 코드입니다.
## WebSocketConfig.java
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import taxi.share.back.util.JwtUtil;
@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private final JwtUtil jwtUtil;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 클라이언트로 메시지를 보낼 브로커 경로 설정
config.enableSimpleBroker("/user", "/topic"); // 클라이언트가 구독할 경로
config.setApplicationDestinationPrefixes("/app"); // 클라이언트 → 서버 메시지 경로
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// WebSocket 엔드포인트 설정
registry.addEndpoint("/ws") // 클라이언트가 WebSocket 연결 요청할 경로
.addInterceptors(new HttpSessionHandshakeInterceptor(), new CustomHandshakeInterceptor()) // HTTP 헤더 전달
.setAllowedOriginPatterns("*") // 모든 도메인 허용 (CORS 설정)
.withSockJS(); // SockJS 지원
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
// JWT를 쿠키에서 추출
String jwt = (String) accessor.getSessionAttributes().get("jwt-token");
// JWT를 검증하고 사용자 정보를 추출
if (jwt != null) {
String userId = jwtUtil.getUserIdByToken(jwt);
// 사용자 정보를 Principal로 설정
accessor.setUser(new UsernamePasswordAuthenticationToken(userId, null, null));
/**
* accessor.setUser를 통해 /user/{userId} 경로를 구독할 수 있도록 설정합니다.
* 이후 클라이언트가 해당 경로를 구독하면 특정 사용자에게만 메시지를 보낼 수 있습니다.
*/
}
System.out.println("User Info: " + accessor.getUser()); // User가 제대로 설정되었는지 확인
return message;
}
});
}
}
코드 설명:
- 메시지 브로커 설정:
- 클라이언트가 구독할 경로:
- /user: 특정 사용자와의 개인 메시징에 사용.
- /topic: 모든 사용자와의 브로드캐스트 메시징에 사용.
- 클라이언트 → 서버로 전송되는 경로는 **/app**으로 시작.
- 클라이언트가 구독할 경로:
- STOMP 엔드포인트 등록:
- /ws: 클라이언트가 WebSocket 연결을 요청하는 엔드포인트.
- SockJS: WebSocket을 지원하지 않는 환경에서도 사용 가능하도록 백업 옵션 제공.
- JWT 인증 처리:
- WebSocket 연결 시 클라이언트가 전송한 JWT를 쿠키에서 읽어 사용자 ID를 추출.
- 사용자 ID를 **StompHeaderAccessor**의 Principal로 설정.
- accessor.setUser를 사용하면 /user/{userId} 경로로 메시지를 구독할 수 있습니다.
■ 요약
이 글에서는 WebSocket 설정과 메시지 전송 방식을 설명했습니다. 특히, convertAndSendToUser 메서드를 통해 /user/{userId} 경로로 메시지를 보내는 방법을 다뤘습니다. accessor.setUser 메서드를 사용하여 특정 사용자 경로를 등록하고, 클라이언트가 해당 경로를 구독할 수 있도록 설정했습니다.
'Java' 카테고리의 다른 글
[Redis] 캐싱 구현(콜론 두 개 :: 등장 이유) (0) | 2025.01.30 |
---|---|
[S3] Spring에서 AWS S3 설정을 활용한 파일 업로드 (1) | 2024.11.08 |
[Gradle] 의존성 설정과 컴파일/런타임 시점의 이해(feat.Lombok) (0) | 2024.09.30 |
[Gradle] 의존성 설정 알아보기 (1) | 2024.09.27 |
[JUnit]Spring Boot에서 JUnit으로 테스트하는 방법 (1) | 2024.09.09 |