미니 프로젝트 - WebSocket 초기 설정 및 연결

Zyoon·2025년 7월 18일

미니프로젝트

목록 보기
24/35
post-thumbnail

📘Spring Boot 에서 WebSocket 생성하여 Postman 으로 테스트하기

알림기능 중에 메세지 보내기

  • 서버에서 특정 userId에게 WebSocket 메시지를 보내는 메서드 호출
  • 세션 방식 테스트. JWT 없이 /ws?userId=1 형식으로 WebSocket 연결, 세션에 userId 매핑
  • Postman WebSocket 요청으로 연결하고, 메시지 수신 여부 확인

구현

1. Config

/**
 * WebSocket 설정 클래스입니다.
 * Spring 에서 WebSocket 서버를 활성화하고, 특정 엔드포인트(`/ws`)에 WebSocket 핸들러를 등록합니다.
 */
@Configuration
@EnableWebSocket
@RequiredArgsConstructor
public class WebSocketConfig implements WebSocketConfigurer {
	
	private final WebSocketHandler handler;

	/**
	 * WebSocket 핸들러를 특정 엔드포인트에 등록합니다.
	 * 클라이언트는 /ws 경로로 WebSocket 연결을 시도할 수 있습니다.
	 *
	 * @param registry WebSocket 핸들러를 등록할 수 있는 레지스트리 객체
	 */
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(handler, "/ws")
			.setAllowedOriginPatterns("*");
	}
}
  • @EnableWebSocket 없으면 spring 에서 webSocket 으로 판단 불가
  • 클라이언트는 /ws 경로로 WebSocket 연결을 시도
  • CORS 우회를 위해 모든 오리진 허용(*) 설정이 적용되어 있음

2. Handler

@Component
public class WebSocketHandler extends TextWebSocketHandler {
	// userId → session 매핑 저장소
	private final Map<Long, WebSocketSession> userSessions = new ConcurrentHashMap<>();

	//Json 타입 변경
	private final ObjectMapper objectMapper = new ObjectMapper();

	/**
	 * 클라이언트와 WebSocket 연결이 성공적으로 수립되었을 때 호출됩니다.
	 * 사용자 ID를 쿼리 파라미터에서 추출하고 세션을 매핑에 저장합니다.
	 *
	 * @param session 연결된 클라이언트의 세션
	 */
	@Override
	public void afterConnectionEstablished(@NonNull WebSocketSession session) {
		Long userId = extractUserIdFromQuery(session);
		userSessions.put(userId, session);
	}

	/**
	 * 클라이언트와의 WebSocket 연결이 종료되었을 때 호출됩니다.
	 * 연결이 종료된 세션을 userSessions 맵에서 제거합니다.
	 *
	 * @param session 종료된 세션
	 * @param status  종료 상태 정보
	 */
	@Override
	public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) {
		userSessions.values().removeIf(s -> s.getId().equals(session.getId()));
	}

	/**
	 * 서버에서 특정 사용자에게 알림 메시지를 전송합니다.
	 * 세션이 유효하고 열려 있는 경우에만 메시지를 전송합니다.
	 *
	 * @param message 전송할 알림 메시지 (userId, content, timestamp 포함)
	 * @throws IOException 메시지 전송 실패 시 예외 발생
	 */
	public void sendToUser(NotificationMessage message) throws IOException  {
		WebSocketSession session = userSessions.get(message.userId());
		if (session != null && session.isOpen()) {
			String json = objectMapper.writeValueAsString(message);
			session.sendMessage(new TextMessage(json));
		}
	}

	/**
	 * WebSocket 연결 URI 의 쿼리 파라미터에서 userId를 추출합니다.
	 * 예: /ws?userId=33 → 33 추출
	 *
	 * @param session 연결된 세션
	 * @return 추출된 사용자 ID
	 */
	private Long extractUserIdFromQuery(WebSocketSession session) {
		String query = Objects.requireNonNull(session.getUri()).getQuery();
		return Long.parseLong(query.split("=")[1]);
	}
}
  • /ws?userId=1 형식으로 접속하며, 쿼리에서 userId 추출해 세션에 매핑함
  • 한 userId당 하나의 세션만 저장되어, 중복 접속 시 이전 세션 덮어씀
  • 서버에서 특정 userId로 직접 메시지 전송 가능함

3. Service

@Component
@RequiredArgsConstructor
public class NotificationSender {

	private final WebSocketHandler webSocketHandler;

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void send(NotificationMessage message) {
		webSocketHandler.sendToUser(message);
	}
}
  • 전달 받은 DTOWebSocketHandler 로 전달

4. DTO

//WebSocket 으로 보내는 DTO
public record NotificationMessage(
	Long userId, 
	String content,
  String timestamp) {

포스트맨에서 WebSocket 설정

  • 우측 상단 New 누른 후 WebSocket 선택

  • 주소는 기본적으로 특별히 건드리지 않았다면 해당 주소로 생성
  • userId로 식별
  • 오른쪽 Connect (Spring Boot 실행 상태에서 실행)

  • Connect 완료 시 하단 구문에 Connected 생성

  • 이제 준비된 URI 를 통해 구문을 날려주면

  • WebSocket 하단에 날린 메세지가 생성되면 성공
profile
기어 올라가는 개발

0개의 댓글