실전 프로젝트에서 채팅 기능을 넣기로 했다. 채팅 기능과 관련해서 알아보았을 때, 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
을 달리 해보면서 백엔드 팀원과 확인을 해보았다.
와이어 프레임이 나오면 채팅 기능을 완전히 완성할 것인데, 그때는 오류가 나지 않길,, 😂