IoT 프로젝트를 진행하며 MQTT 통신을 사용하려고 하였으나, 버전 문제로 어려움을 겪어 socket.io 통신으로 MQTT와 유사한 Publish/Subscribe 메커니즘을 구현하였다.
프로젝트의 주요 기능별로 topic을 정하고 연결된 message를 주고 받음으로써 기능을 구현하였는데, 사용했던 코드를 소개해보고자 한다.
먼저 Socket.io와 MQTT에 대해 알아보자.
Socket.io와 MQTT는 모두 실시간 통신을 위한 프로토콜이지만, 몇 가지 차이점이 있다.
socket.io를 사용하기 위해서는 서버와 클라이언트 모두 패키지를 설치해야한다. (socket.io 공식문서)
리액트에서는 socket.io-client를 설치한다.
npm install socket.io-client
socket 통신은 기본적으로 socket.emit('eventName', Func) 으로 데이터를 보내고, socket.on('eventName', Listener)로 데이터를 받는 형식으로, 이러한 구조를 통해 데이터의 교환이 이루어진다.
io 함수 호출하여 클라이언트 측에서 서버와 소켓 연결을 생성const [socket, setSocket] = useState(null); // 소켓 상태 변수와 상태 변경 함수
useEffect(() => {
// 서버와 소켓 연결 생성
const newSocket = io('http://localhost:3001'); // 서버 주소로 변경
setSocket(newSocket); // 생성한 소켓 인스턴스를 상태 변수에 저장
// 컴포넌트가 언마운트될 때 소켓 연결 해제
return () => {
newSocket.disconnect();
};
}, []); // 초기 렌더링 후에만 실행됨, 의존성 배열이 비어 있음
socket.emit('sendMessage', message) : 소켓 객체를 통해 sendMessage 이벤트를 메시지와 함께 서버로 보냄const [message, setMessage] = useState('');
const sendMessage = () => {
if (socket && message) {
socket.emit('sendMessage', message); // 서버로 메시지 전송
setMessage(''); // 메시지 입력 초기화
}
};
return (
<div>
<h1>Socket.io Example</h1>
<input
type="text"
placeholder="Enter a message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button onClick={sendMessage}>Send</button>
</div>
);
import React, { useState, useEffect } from 'react';
import io from 'socket.io-client'; // socket.io-client 모듈 가져오기
const App = () => {
const [message, setMessage] = useState(''); // 메시지 상태 변수와 상태 변경 함수
const [socket, setSocket] = useState(null); // 소켓 상태 변수와 상태 변경 함수
useEffect(() => {
// 서버와 소켓 연결 생성
const newSocket = io('http://localhost:3001'); // 서버 주소로 변경
setSocket(newSocket); // 생성한 소켓 인스턴스를 상태 변수에 저장
// 컴포넌트가 언마운트될 때 소켓 연결 해제
return () => {
newSocket.disconnect();
};
}, []); // 초기 렌더링 후에만 실행됨, 의존성 배열이 비어 있음
// 메시지 전송 함수
const sendMessage = () => {
if (socket && message) {
socket.emit('sendMessage', message); // 서버로 메시지 전송
setMessage(''); // 메시지 입력 초기화
}
};
// JSX 렌더링
return (
<div>
<h1>Socket.io Example</h1>
<input
type="text"
placeholder="Enter a message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button onClick={sendMessage}>Send</button>
</div>
);
};
export default App;
이제 위 코드를 활용하여 MQTT의 Publish/Subscribe 메커니즘을 구현해보겠다.
io 함수 호출하여 클라이언트 측에서 서버와 소켓 연결을 생성const [socket, setSocket] = useState(null); // 소켓 상태 변수와 상태 변경 함수
useEffect(() => {
const BrokerAddress = 'http://localhost:3001'; // 브로커 주소
// 새로운 소켓 인스턴스 생성
const newSocket = io(BrokerAddress, {
cors: {origin: '*'} // 모든 도메인에서의 교차 출처 요청 허용
});
// 소켓이 브로커에 연결되었을 때 실행되는 이벤트 핸들러
newSocket.on('connect', () => {
console.log('Connected to the broker.');
});
// 새로운 메시지가 도착했을 때 실행되는 이벤트 핸들러
newSocket.on('message', (receivedMessage) => {
console.log(`Received message: ${receivedMessage}`);
});
setSocket(newSocket); // 생성한 소켓 인스턴스를 상태 변수에 저장
// 컴포넌트가 해제될 때 소켓 연결 해제
return () => {
newSocket.disconnect();
};
}, []); // 초기 렌더링 후에만 실행됨, 의존성 배열이 비어 있음
const [topic, setTopic] = useState(''); // 주제(topic) 상태 변수와 상태 변경 함수
// 주제를 구독하는 함수
const subscribeToTopic = () => {
if (socket && topic) { // 소켓과 주제가 비어있지 않을 경우
socket.emit('subscribe', topic); // 소켓을 통해 주제 구독 요청 전송
console.log(`Subscribed to topic: ${topic}`);
}
};
return (
<div className="App">
{/* 주제 입력 필드 */}
<input
type="text"
placeholder="Topic"
value={topic}
onChange={(e) => setTopic(e.target.value)}
/>
{/* 주제 구독 버튼 */}
<button onClick={subscribeToTopic}>Subscribe</button>
);
const [topic, setTopic] = useState(''); // 주제(topic) 상태 변수와 상태 변경 함수
const [message, setMessage] = useState(''); // 메시지(message) 상태 변수와 상태 변경 함수
// 메시지를 발행하는 함수
const publishMessage = () => {
if (socket && topic && message) { // 소켓, 주제, 메시지가 비어있지 않을 경우
socket.emit('publish', { topic, message }); // 주제와 메시지를 함께 서버로 전송
console.log(`Published message "${message}" to topic: ${topic}`);
setMessage(''); // 메시지 입력 상태 초기화
}
};
return (
{/* 메시지 입력 필드 */}
<input
type="text"
placeholder="Message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
{/* 메시지 발행 버튼 */}
<button onClick={publishMessage}>Publish</button>
</div>
);
import { useState, useEffect } from 'react';
import io from 'socket.io-client'; // socket.io-client 모듈 가져오기
const BrokerAddress = 'http://localhost:3001'; // 브로커 주소
function App() {
const [topic, setTopic] = useState(''); // 주제(topic) 상태 변수와 상태 변경 함수
const [message, setMessage] = useState(''); // 메시지(message) 상태 변수와 상태 변경 함수
const [socket, setSocket] = useState(null); // 소켓 상태 변수와 상태 변경 함수
useEffect(() => {
// 새로운 소켓 인스턴스 생성
const newSocket = io(BrokerAddress, {
cors: {origin: '*'} // CORS 설정
});
// 소켓이 브로커에 연결되었을 때 실행되는 이벤트 핸들러
newSocket.on('connect', () => {
console.log('Connected to the broker.');
});
// 새로운 메시지가 도착했을 때 실행되는 이벤트 핸들러
newSocket.on('message', (receivedMessage) => {
console.log(`Received message: ${receivedMessage}`);
});
setSocket(newSocket); // 생성한 소켓 인스턴스를 상태 변수에 저장
// 컴포넌트가 해제될 때 소켓 연결 해제
return () => {
newSocket.disconnect();
};
}, []); // 초기 렌더링 후에만 실행됨, 의존성 배열이 비어 있음
// 주제를 구독하는 함수
const subscribeToTopic = () => {
if (socket && topic) { // 소켓과 주제가 비어있지 않을 경우
socket.emit('subscribe', topic); // 소켓을 통해 주제 구독 요청 전송
console.log(`Subscribed to topic: ${topic}`);
}
};
// 메시지를 발행하는 함수
const publishMessage = () => {
if (socket && topic && message) { // 소켓, 주제, 메시지가 비어있지 않을 경우
socket.emit('publish', { topic, message }); // 주제와 메시지를 함께 서버로 전송
console.log(`Published message "${message}" to topic: ${topic}`);
setMessage(''); // 메시지 입력 상태 초기화
}
};
return (
<div className="App">
{/* 주제 입력 필드 */}
<input
type="text"
placeholder="Topic"
value={topic}
onChange={(e) => setTopic(e.target.value)}
/>
{/* 주제 구독 버튼 */}
<button onClick={subscribeToTopic}>Subscribe</button>
{/* 메시지 입력 필드 */}
<input
type="text"
placeholder="Message"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
{/* 메시지 발행 버튼 */}
<button onClick={publishMessage}>Publish</button>
</div>
);
}
export default App;