[Spring + React] 웹소켓 구현하기

토리·2024년 2월 27일
0
post-thumbnail

웹소켓이 필요한 이유

유저 페이지에서 나의 이벤트 참여가 완료 여부를 실시간으로 받아야 한다.
하지만 기존 HTTP 통신으로는 클라이언트에서의 요청 없이 서버에서 클라이언트로 메세지를 전달할 수 없다.

웹소켓으로는 양방향 통신이 가능하기 때문에 웹소켓을 이용해 통신하기로 한다.

웹소켓을 사용하려면

클라이언트에서도, 서버에서도 소켓 통신을 한다고 허용해줘야 한다

스프링에서의 소켓 설정

pom.xml에 의존성 주입 (레거시 프로젝트)

<dependency>
	<groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>${org.springframework-version}</version>
</dependency>

WebSocketConfig으로 설정

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket-demo")
                .setAllowedOrigins("*")
                .withSockJS();
    }
}
  • 추가적으로 WebConfig에서 이 class를 인식하도록 해줘야 한다

메세지를 보내고 받을 Controller

@Log4j
@Controller
public class GreetingController {
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public String greeting(String message) throws Exception {
        Thread.sleep(1000);
        log.debug("서버에서 요청 받음");
        return "Hello from server!" + message;
    }
}

동작 흐름

  • 클라이언트에서 /websocket-demo로 연결 요청
  • 클라이언트가 /topic/greetings를 구독
  • 클라이언트가 /app/hello에 메세지 보냄

리액트에서 소켓 설정


import SockJS from 'sockjs-client';
import { Client } from '@stomp/stompjs';
  
  useEffect(() => {
    const socket = new SockJS(`${process.env.REACT_APP_API_BASE_URL}/websocket-demo`);
    const stompClient = new Client({
      webSocketFactory: () => socket,
      onConnect: function (frame) {
        console.log('Connected: ' + frame);

        // 소켓 구독
        stompClient.subscribe('/topic/greetings', (greeting) => {
          const message = new TextDecoder().decode(new Uint8Array(greeting.binaryBody));
          console.log('서버에서 받은 메세지: ', message);
        });

        // 소켓 발행
        stompClient.publish({
          destination: '/app/hello',
          body: 'Hello WebSocket',
        });
      }
    });
    stompClient.activate();
  }, []);

위 코드에서는 페이지 로딩 시 (useEffect) 소켓 구독과 발행이 함께 일어나지만 구독만 필요하거나 (서버에서 메세지를 받음) 발행만 필요하다면 (서버에 메세지를 보냄) 둘 중 하나만 하면 되겠다

해결한 문제들

Module not found: Error: Can't resolve 'net' in ...

→ stompJs 패키지였나 쓰고 있었는데 다른 패키지를 사용해 해결했다. 다른 해결법으로는 webpack.config.js 정의해서 net을 빼주는 방법이 있었는데 그냥 다른 모듈을 사용함

서버로 요청은 가는데 연결이 안 됨

  • Spring Security에서 해당 경로 제외시켜줘야 했는데 안 해서 문제 발생
  • .setAllowedOrigins("*") 안 붙여줘서 CORS 에러 발생 (리액트 3000번 포트, 스프링 8080번 포트 이용하기에 처리 필요)

이외에도 더 처리한 부분이 있었는데 잘.. 기억이 안 난다

구현 화면

추가적으로 커스텀할 부분도 많고, 작업이 많이 남아서 모두 완료하면 정리해서 포스팅을 남기고 싶다

참고자료

해당 PR

0개의 댓글