sm 기술 블로그

[스프링부트 + 리액트] websocket 구현 본문

스프링부트

[스프링부트 + 리액트] websocket 구현

sm_hope 2022. 9. 15. 22:21

저번에는 스프링자체에서 템플릿으로 웹소캣 된것을 출력해 보았다.

이번에는 리액트를 이용해서 출력해보자.  

 

스프링부분의 자세한 설명은 아래를 참고하자.

https://smhope.tistory.com/527

 

[스프링부트] Websocket(stomp) 구현

웹소캣은 양방향 통신을 제공하여 예를들어 유저 1이 메세지를 보내면 유저 2에게 실시간으로 도착하고 반대로 유저 2가 유저 1에게 메시지를 보내면 실시간으로 도착하도록 하는 프로토콜이다.

smhope.tistory.com


BackEnd

1. Config

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
@EnableScheduling
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // enableSimpleBroker()
        // 스프링에서 제공하는 내장프로커로 /sub로 들어오는 메시지를 브로커가 처리한다. (구독)
        registry.enableSimpleBroker("/queue","/topic");

        // setApplicationDestinationPrefixes()
        // 바로 메시지를 뿌리는 것이 아닌 처리를 하고 보내기 위해 메시지 핸들러로 라우팅됨.
        registry.setApplicationDestinationPrefixes("/app");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 채팅 클라이언트가 서버와 연결하웹소켓 셋팅 부분 ->웹소켓 연결 주소 -> /url/chatting
        registry.addEndpoint("/chat/chatting").setAllowedOriginPatterns("*").withSockJS();
    }
}

2. Model(Message)

import lombok.Data;

import java.util.Date;

@Data
public class Message {
    private String roomId;
    private String writerId;
    private String content;
    private Date createDate;

    public Message() {

    }

    public Message(String roomId, String writerId, String content, Date createDate) {
        this.roomId = roomId;
        this.writerId = writerId;
        this.content = content;
        this.createDate = createDate;
    }
}

3. ChatController

import com.sbb.sm_chatting.DTO.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashSet;
import java.util.Set;

@RestController
public class ChatController {
    private static Set<String> userList = new HashSet<>();

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/chat")
    public void sendMessage(@Payload Message message){
        this.simpMessagingTemplate.convertAndSend("/queue/addChatToClient",message);
    }

}

FrontEnd
import SockJS from "sockjs-client";
import Stomp from "stompjs";
import React, { useState, useEffect } from "react";

const Talk = () => {
  const [msg, setMsg] = useState([]);
  const [content, setContent] = useState("");
  let socket = new SockJS("http:/localhost:8031/chat/chatting");
  let client = Stomp.over(socket);

  useEffect(() => {
    client.connect({}, () => {
      // 연결시 jwt를 보냄
      // client.send("/app/join", {} ,JSON.stringify(localStorage.getItem("Token")))

      // (초기 셋팅)처음 들어오면 DB에 있는 메시지를 추출함
      client.send(`/app/first/3과4`, {}, JSON.stringify("success"));

      //
      client.subscribe("/queue/firstChat/3과4", function (Message) {
        const newMsg = JSON.parse(Message.body).map((a) => a.content);
        setMsg(newMsg);
      });

      client.subscribe("/queue/addChatToClient/3과4", function (Message) {
        const newMsg = JSON.parse(Message.body).content;
        setMsg((prev) => prev.concat(newMsg));
      });
    });
  }, []);

  const handleSubmit = (e, content) => {
    e.preventDefault();
    client.send(`/app/chat/3과4`, {}, JSON.stringify({ content }));
    setContent("");
  };

  return (
    <div>
      <div>채팅</div>
      <form onSubmit={(e) => handleSubmit(e, content)}>
        <div>
          <input
            type="text"
            placeholder="내용을 입력하세요."
            value={content}
            onChange={(e) => {
              setContent(e.target.value);
            }}
          />
        </div>
        <button type="submit">제출</button>
        <div>내용</div>
        <div>
          <ul>
            {msg.map((_msg, index) => (
              <li key={index}>{_msg}</li>
            ))}
          </ul>
        </div>
      </form>
    </div>
  );
};

export default Talk;

App.js에서 출력을 하고 Talk부분은 컴포넌트로 작성하였다.

템플릿으로 작성한것과 내용이 크게 다른부분이 없기 때문에 위에 링크를 참고하면 크게 무리 없이 이해가 가능할 것이다.

 

구현

Comments