실제 프로젝트에 사용됐던 코드 중 간소화를 많이 시켰기 때문에 오류가 발생할 수도 있으며,참고용으로만 사용하길 바람
npm
을 통해 STOMP 모듈을 설치한다.
npm i @stomp/stompjs
웹 소켓을 사용해서 서버와 통신하려면 http
가 아니라 ws
프로토콜을 사용해야 하므로 소켓 프록시를 따로 설정한다.
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = (app) => {
app.use(
"/ws",
createProxyMiddleware({ target: "http://localhost:8787", ws: true })
);
};
ws: true
를 줘서 웹 소켓을 사용한다.function CreateReadChat() {
const client = useRef({});
const connect = () => { // 연결할 때
client.current = new StompJs.Client({
brokerURL: 'ws://localhost:8787/ws',
onConnect: () => {
subscribe(); // 연결 성공 시 구독하는 로직 실행
},
);
client.current.activate(); // 클라이언트 활성화
};
const disconnect = () => { // 연결이 끊겼을 때
client.current.deactivate();
};
useEffect(() => {
connect();
return () => disconnect();
}, []);
useRef()
훅을 사용해 속성 값이 변경돼도 재렌더링하지 않고, 다시 렌더링하더라도 값이 유실되지 않도록 클라이언트를 current
속성에 만든다.onConnect()
옵션에서 연결에 성공했을 시, 채널을 구독하고 메시지를 받는 함수를 실행한다.activate()
함수를 사용해 클라이언트를 활성화 시킨다.useEffect()
훅을 사용해서 최초 렌더링 시, 웹소켓에 연결되도록 한다.connect()
가 한 번만 실행되도록 한다. const [chatList, setChatList] = useState([]); // 화면에 표시될 채팅 기록
const { apply_id } = useParams(); // 채널을 구분하는 식별자를 URL 파라미터로 받는다.
const subscribe = () => {
client.current.subscribe('/sub/chat/' + apply_id, (body) => {
const json_body = JSON.parse(body.body);
setChatList((_chat_list) => [
..._chat_list, json_body
]);
});
};
body.body
에 실려온다.string
으로 오기 때문에 JSON으로 사용하려면 파싱이 한 번 필요하다.setChatList
로 화면에 받은 메시지를 표시한다. const [chat, setChat] = useState(''); // 입력되는 채팅
const publish = (chat) => {
if (!client.current.connected) return; // 연결되지 않았으면 메시지를 보내지 않는다.
client.current.publish({
destination: '/pub/chat',
body: JSON.stringify({
applyId: apply_id,
chat: chat,
}), // 형식에 맞게 수정해서 보내야 함.
});
setChat('');
};
destination
은 STOMP 서버에서 메시지를 받기 위해 @MessageMapping
으로 연결해둔 주소로 설정한다.body
는 STOMP 서버에서 정의하고 있는 형식에 맞게 가공해서 보낸다.setChat('');
을 통해 메시지 입력란을 초기화한다.4.전체적인 코드는 다음과 같아진다.
import { useRef, useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import * as StompJs from '@stomp/stompjs';
import instance from '../../utils/axiosConfig';
function CreateReadChat() {
const [chatList, setChatList] = useState([]);
const [chat, setChat] = useState('');
const { apply_id } = useParams();
const client = useRef({});
const connect = () => {
client.current = new StompJs.Client({
brokerURL: 'ws://localhost:8787/ws',
onConnect: () => {
console.log('success');
subscribe();
},
});
client.current.activate();
};
const publish = (chat) => {
if (!client.current.connected) return;
client.current.publish({
destination: '/pub/chat',
body: JSON.stringify({
applyId: apply_id,
chat: chat,
}),
});
setChat('');
};
const subscribe = () => {
client.current.subscribe('/sub/chat/' + apply_id, (body) => {
const json_body = JSON.parse(body.body);
setChatList((_chat_list) => [
..._chat_list, json_body
]);
});
};
const disconnect = () => {
client.current.deactivate();
};
const handleChange = (event) => { // 채팅 입력 시 state에 값 설정
setChat(event.target.value);
};
const handleSubmit = (event, chat) => { // 보내기 버튼 눌렀을 때 publish
event.preventDefault();
publish(chat);
};
useEffect(() => {
connect();
return () => disconnect();
}, []);
return (
<div>
<div className={'chat-list'}>{chatList}</div>
<form onSubmit={(event) => handleSubmit(event, chat)}>
<div>
<input type={'text'} name={'chatInput'} onChange={handleChange} value={chat} />
</div>
<input type={'submit'} value={'의견 보내기'} />
</form>
</div>
);
}