실전 프로젝트에서 채팅 기능을 넣기로 했다. 채팅 기능과 관련해서 알아보았을 때, soket.io를 많이 사용한다고 해서 soket.io로 하려고 했으나, 추가적으로 알아보니 백엔드가 Spring일 때에는 SockJS와 Stompjs를 많이 사용한다고 한다. 이전의 soket.io는 Node.js와 많이 사용한다고 한다.
우리 프로젝트의 백엔드 팀은 Spring을 사용 중이라서 SockJS와 Stompjs를 사용해서 기능을 구현하려고 한다.
웹 소켓은 http에서 실시간 통신을 할 수 없는 문제를 해결하기 위해 나온 기술이다.
http로는 실시간 통신을 할 수 없는데, 다음과 같은 특징을 가지기 때문이다.
요정을 보매면 단방향적 구조로 통신을 하기 때문에 TCP/IP 프로토콜을 사용하는 소켓처럼 계속 connection이 유지되는 실시간 통신을 할 수 없기 때문에 웹소켓 프로토콜이 나오게 되었다.
그래서 자주 사용하는 상황은 다음과 같다.
Socker.io, SockerJS가 html5이전의 기술로 구현된 서비스에서 웹 소켓처럼 사용할 수 있도록 도와주는 기술이다. 이걸로 실시간 통신을 도와준다.
우선 와이어 프레임이 나오지 않아서 HTML, CSS 작업은 하지 못했다. 완전히 처음 배우는 내용이라 프로젝트에서 꽤 오래 걸릴 것 같은(?) 기능이라서 우선 기능부터 완성해보기로 하였다. 서버 쪽 코드는 없고, 프론트 쪽에서 콘솔로만 찍어서 확인을 해보았다.
yarn add stompjs sockjs 명령어를 사용해서 설치"dependencies"에 "net": "^1.0.2"를 추가해주고 yarn install을 해준다. 2번은
Uncaught Error: Cannot find module 'net'. stomp-node.js:14오류로 Node.js의 내장 모듈 중 하나인 'net' 모듈을 찾을 수 없다고 해서 추가로 설치해주었다.
// Get Chat Room
...
export const __getChatRoom = createAsyncThunk(
"getChatRoom",
async (payload, thunkAPI) => {
try {
const response = await instance.post(`/chat/room/${payload}`);
console.log("chat response", response.data);
return thunkAPI.fulfillWithValue(response.data);
} catch (error) {
console.log(error.response.data.message);
throw new Error(error.response.data.message);
}
}
);
...
채팅방이 필요해서 우선 Redux thunk를 사용하여 RoomId를 서버 쪽에서 받아오고 state에 저장해 사용한다.
...
const ChatRoom = () => {
const roomId = useSelector((state) => state.chat.roomId);
...
useEffect(() => {
dispatch(__getChatRoom(postId));
wsConnectSubscribe();
return () => {
onbeforeunloda();
};
}, [roomId]);
const socket = new SockJS(`${process.env.REACT_APP_CHAT_TEST}/ws/chat`);
// SockJS를 내부에 들고 있는 stomp를 내어줌
const ws = Stomp.over(socket);
// 서버에 보내줄 값
const content = {
type: "TALK",
sender: "test1",
message: message,
};
// 연결 시 실행
const waitForConnection = (ws, callback) => {
setTimeout(
() => {
// 연결되었을 때 콜백함수 실행
if (ws.ws.readyState === 1) {
callback();
// 연결이 안 되었으면 재호출
} else {
waitForConnection(ws, callback);
}
},
1 // 밀리초 간격으로 실행
);
}; //stomp 메시지 에러 waitForConnection함수로 해결
const wsConnectSubscribe = () => {
try {
// headers에는 jwt token을 넣었음
ws.connect(headers, (frame) => {
// connect 성공시 연결시 실행
ws.subscribe(`/sub/${roomId}`, (response) => {
// 상대 채팅을 받아옴
let data = JSON.parse(response.body);
});
});
} catch (error) {
console.log("error message ->", error);
}
};
const onbeforeunloda = () => {
try {
//연결 끊기
ws.disconnect(
() => {
ws.unsubscribe("sub-0");
clearTimeout(waitForConnection);
},
{ Access_Token: localStorage.getItem("Access_Token") }
);
} catch (e) {}
};
const handleSubmit = () => {
waitForConnection(ws, () => {
// message 보내기
ws.send(`/pub/${roomId}`, {}, JSON.stringify(content));
});
};
...
처음 만져보는 기능이라 공부하는데 시간도 걸렸고, 서버 쪽에서 CORS에러도 발생하고 연결이 잘 되지 않았던 것 같다.
위의 코드들은 우선 간단하게 콘솔로만 확인을 해보았다. 메세지가 잘 오고 감을 확인 할 수 있었고,content의type을 달리 해보면서 백엔드 팀원과 확인을 해보았다.
와이어 프레임이 나오면 채팅 기능을 완전히 완성할 것인데, 그때는 오류가 나지 않길,, 😂