221106 TIL [Spring boot + React]실시간 채팅 기능 구현하기 - 4

오늘 드디어 Spring + React를 이용해서 실시간 채팅 기능을 구현했다.

WebSocket으로 채팅방 하나인 1:1 채팅 기능은 구글링 조금만 하면 금방 구현할 수 있었는데 채팅방이 여러 개를 구현하기 위해 STOMP를 사용해야 할 때부터 막히기 시작했었다.

구글링 하면 정보는 많았지만 STOMP를 전혀 알지 못한 채 알아보려니까 이게 맞는 정보인지 틀린 정보인지 판별이 되지 않아 하나씩 전부 읽어보면서 확인하는 작업을 하는데 너무 오랜 시간이 걸렸다... ㅠㅠ

솔직히 실시간 채팅 하나 구현하는데 4일이나 걸릴 줄은 몰랐지만 포기하지 않고 어떻게든 구현했다는 사실과 채팅을 구현하는데 최적의 코드는 아닐지라도 이것저것 해보면서 실시간 채팅을 구현했다는 사실만으로도 너무 기뻤다.  

 

 

아무튼 어제는 Spring으로 Stomp 설정하는 방법과 동작 원리에 대해 배웠고 오늘은 백엔드에서 설정한 Stomp를 React와 연동하는 작업을 했다.

 

코드를 보면서 살펴보자

스프링과 연동하기 위해 리액트에서 sockjs와 stompjs를 사용했다.

useEffect를 이용해 컴포넌트가 렌더링 될때 new SockJs()를 이용해서 백엔드에서 addEndpoint로 설정해준 /stomp/chat과 연결을 해준다. 

stompClient는 useRef를 사용해서 선언해주었는데 그 이유는 값이 변경돼도 재 랜더링 되지 않게 하고, 랜더링이 돼도 값을 변화시키지 않고 유지하기 위해 useRef hook을 사용했다.

useRef를 사용하지 않고 그냥 구현하려고 하니까 처음 웹 페이지 렌더링 할 때 Stomp Connection을 여러 번 하고 useState를 사용해서 입력창의 메시지를 변화시키게 했는데 이렇게 하니까 글씨를 칠 때마다 렌더링이 되어버려 웹소켓에 계속 연결하는 문제가 있었기 때문에 useRef를 사용했다.

 

이후에 채팅방에 연결을 하는데 구독하고 있을 채팅방을 주소를 써주면 해당 주소로 메시지가 들어오면 chat으로 메시지를 받아올 수 있다.

  • stomp는 텍스트 처리만 가능하기 때문에 보낼 때는 JSON.stringify()); 받을 때 JSON.parse() 처리를 해줘야 한다.

그리고 누가 입장했는지도 알리기 위해 connect할시에 send 메서드로 보낼 채팅방 id와 채팅에 들어간 유저 아이디를 전달해준다.

이렇게 연결을 해주면 콘솔 창에 아래와 같은 메시지를 확인할 수 있을 것이다.

 

connect 되었다는 콘솔 메시지와 내가 구독한 주소의 url을 알려주는 destination 그리고 채팅창에 들어갔을 때 보내지는 메시지인 채팅방 번호와 유저 닉네임까지 SEND command로 확인할 수 있다.

 

그리고 채팅방을 벗어났을 시 연결을 끊어주는 코드도 작성해줌으로써 다른 페이지로 이동했을 때 disconnect 되었다는 메시지도 확인할 수 있었다.

 

입력한 메시지를 보낼 때는 위에서 사용했던 send 메서드에 message까지 같이 보내주면 MESSAGE command로 메시지가 돌아온 것을 확인할 수 있다.

 

위 코드에서 내가 생각하는 핵심적인 부분들을 설명하자면

1. 웹소켓 세션을 연결 : SockJs로 백엔드에서 설정해준 endpoint를 호출

2. 채팅방 구독 설정 : subscribe 메서드로 백엔드에서 설정해준 SimpleBorker를 호출

3. 메시지 보내는 것 : send 메서드로 백엔드에서 @MessageMapping에 써준 url과 메시지를 보낼 채팅방 번호와 메시지를 같이 전송

 

정리하고 보니 별거 아닌 내용 같지만 나는 위 3가지를 동작 과정을 이해하는데 너무 오랜 시간이 걸렸다. 

이게 왜 이렇게 되는 거지? 이 블로그에서는 이렇게 쓰네? 아까 본 블로그에서는 다른 방식으로 사용했던 것 같았는데..?? 뭐야 저기는 또 다른 방법으로 구현했네???라는 생각을 정말 많이 했을 정도로 채팅을 구현하는 데는 여러 방법이 있었던 것 같다.

그중에서 어떤 방법이 옳고 틀린지는 아직도 알지는 못하지만 그만큼 정보를 가려내기가 쉽지 않았다.

이 블로그 저 블로그 보면서 내 상황에서는 어떻게 코드를 작성하고 적용시킬 수 있을지 고민을 많이 했고 안될 때는 정말 괴롭기도 했지만... 어쨌든 구현했다는 거 ㅎㅎㅎ

 

참조 

https://itprogramming119.tistory.com/entry/React-useRef-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B0%8F-%EC%98%88%EC%A0%9C

https://velog.io/@hellocdpa/220310-WebSocket-STOMP%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%B1%84%ED%8C%85%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B01-DB%EC%84%A4%EA%B3%84

https://velog.io/@dldmswjd322/Spring-boot-React-STOMP%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%B1%84%ED%8C%85-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-1-Spring-boot-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

https://sg-choi.tistory.com/414