[Spring AI] MQTT로 실시간 객체탐지 구현하기 — Mosquitto, paho-mqtt, YOLO, WebSocket
by 우와한개발자
MQTT는 발행/구독 모델 기반의 경량 메시지 프로토콜로, IoT 환경에서 실시간 통신에 주로 사용된다.
1. MQTT(Message Queuing Telemetry Transport)란?
- 머신 대 머신 통신에 사용되는 표준 기반 경량 메시지 프로토콜
- 주로 IoT(사물인터넷) 환경에서 기기들이 서로 메시지를 주고받을때 사용된다.
- 낮은 대역폭(느린 인터넷)과 제한된 환경(IoT 센서, 임베디드 기기 등)에서도 신뢰성 있게 통신할 수 있도록 설계되었다.
1) HTTP vs MQTT
| 구분 | HTTP | MQTT |
| 통신 방식 | 요청-응답 (클라이언트가 먼저 요청) | 발행-구독 (브로커가 밀어줌) |
| 연결 방식 | 요청마다 새로운 연결 | 한 번 연결 후 계속 유지 |
| 서버 → 클라이언트 | 불가 (클라이언트가 요청해야만 응답) | 가능 (구독자에게 자동 전달) |
| 수신 대상 | 요청한 1명만 응답 받음 | 구독 중인 전원이 동시에 받음 |
| 실시간성 | 낮음 (폴링 필요) | 높음 |
| 메시지 크기 | 헤더가 크고 무거움 | 헤더 최소 2byte로 매우 가벼움 |
| 사용 상황 | 일반 웹 서비스, REST API | IoT, 실시간 모니터링 |
| 비유 | 편지 (요청해야 답장) | 라디오 방송 (틀면 바로 들림) |
2) MQTT 브로커 종류
| 브로커 | 특징 |
| Mosquitto | 가볍고 널리 사용됨. MQTT 5.0 지원 |
| HiveMQ | 상용 중 가장 강력. 확장성, 고가용성, 웹 UI |
| EMQX | 클라우드 기반, 대규모 연결 지원 |
| VerneMQ | Erlang 기반 고성능 브로커 |
| RabbitMQ | 기본은 AMQP 기반. MQTT는 플러그인으로 부분 지원 |
| AWS IoT Core | 보안·인증에 강점. MQTT 3.1.1 / 5.0 지원 |
| Azure IoT Hub | MQTT 3.1.1만 지원 (5.0 미지원) |
| Google Cloud IoT Core | 2023년 서비스 종료 |
| NanoMQ | 경량 엣지 디바이스용. 빠르고 가벼움 |
| Solace PubSub+ | MQTT 외 SMF, REST 등 여러 프로토콜 지원 |
3) MQTT 주요 특징
- 경량성 : 작은 데이터 패킷을 사용하여 네트워크 부하를 최소화한다.
- 발행/구독 모델 : 클라이언트는 특정 토픽에 메시지를 발행하거나 구독할 수 있다.
- 신뢰성 : QoS(Quality of Service) 레벨을 제공하여 메시지 전송을 보장한다.
- 유연성 : 제한된 리소스를 가진 IoT 기기 등 다양한 환경에서 사용 가능하다.
4) MQTT 구성 요소

- 브로커 (Broker) : 발행자와 구독자 사이에서 메시지를 중계하는 서버이다. 모든 메시지는 브로커를 통해 전달된다.
- 발행자 (Publisher) : 특정 토픽에 메시지를 발행하는 클라이언트이다. 예) 온도 센서가 home/temperature 토픽에 데이터를 전송한다.
- 구독자 (Subscriber) : 특정 토픽을 구독하여 메시지를 수신하는 클라이언트이다. 예) 스마트폰 앱이 home/temperature 토픽을 구독해 실시간으로 온도를 표시한다.
2. 실시간 객체탐지 MQTT 구현 – Mosquitto 브로커
1) 브로커 설치 및 설정
(1) Windows
- https://mosquitto.org/download 에서 다운로드한다.
- 설치 후 C:\\Program Files\\mosquitto\\mosquitto.conf 를 아래와 같이 수정한다.
- 이때, 해당 파일은 읽기전용 파일로 되어 있으므로 읽기 전용을 해제해야하고, 메모장을 관리자 권한으로 열어 읽어와서 수정
(2) Mac
- Homebrew로 설치한다.
brew install mosquitto
- 설정 파일 위치는 /opt/homebrew/etc/mosquitto/mosquitto.conf 이다.
# 파일 열기
nano /opt/homebrew/etc/mosquitto/mosquitto.conf
(3) 설정 내용 (Windows / Mac 공통)
# MQTT 포트
listener 1883
protocol mqtt
# WebSocket 포트 (브라우저 연결용)
listener 9001
protocol websockets
# 인증 없이 접속 허용
allow_anonymous true
(4) 실행
| 구분 | Windows | Mac |
| 실행 | mosquitto -c "C:\Program Files\mosquitto\mosquitto.conf" | mosquitto -c /opt/homebrew/etc/mosquitto/mosquitto.conf |
| 백그라운드 실행 | 서비스 등록 후 자동 실행 | brew services start mosquitto |
| 중지 | 서비스 중지 | brew services stop mosquitto |
2) Python 구현
(1) 라이브러리 설치
- paho-mqtt : Python에서 MQTT 프로토콜을 사용할 수 있게 해주는 라이브러리로 MQTT 3.1, 3.1.1, 5.0 버전을 지원한다.
pip install paho-mqtt ultralytics opencv-python
(2) 코드
import base64
import cv2
import numpy as np
import paho.mqtt.client as mqtt
import json
from ultralytics import YOLO
model = YOLO("yolov8n.pt") # YOLOv8 경량 모델 로드
# MQTT 브로커 설정
broker = 'localhost'
port = 1883
topic = "/camera/objects"
client = mqtt.Client(callback_api_version=mqtt.CallbackAPIVersion.VERSION2)
def on_connect(client, userdata, flags, rc, properties=None):
print(f"Connected with result code {rc}")
client.on_connect = on_connect
client.connect(broker, port) # 브로커 연결
def get_colors(num_colors):
np.random.seed(0) # seed 고정 → 항상 같은 색상
colors = [tuple(np.random.randint(0, 255, 3).tolist()) for _ in range(num_colors)]
return colors
colors = get_colors(len(model.names)) # 클래스별 색상 생성
def detect_objects(image: np.array):
results = model(image, verbose=False) # 객체 탐지
for result in results:
boxes = result.boxes.xyxy # 바운딩 박스 좌표
confidences = result.boxes.conf # 신뢰도
class_ids = result.boxes.cls # 클래스 ID
for box, confidence, class_id in zip(boxes, confidences, class_ids):
x1, y1, x2, y2 = map(int, box)
label = model.names[int(class_id)]
cv2.rectangle(image, (x1, y1), (x2, y2), colors[int(class_id)], 2) # 박스 그리기
cv2.putText(image, f'{label} {confidence:.2f}', (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, colors[int(class_id)], 2) # 라벨 표시
return image
cap = cv2.VideoCapture(1) # 카메라 열기 (0: 기본 카메라)
running = True
def on_message(client, userdata, msg):
if msg.topic == 'camera/control' and msg.payload.decode() == 'stop':
global running
running = False # stop 메시지 수신 시 루프 종료
client.subscribe('camera/control') # 종료 신호 토픽 구독
client.on_message = on_message
while cap.isOpened() and running:
ret, frame = cap.read()
if not ret:
print("Error: Failed to read frame")
break
result_image = detect_objects(frame)
encode_param = [cv2.IMWRITE_JPEG_QUALITY, 90]
_, buffer = cv2.imencode('.jpg', result_image, encode_param) # jpg 인코딩 (품질 90, 낮출수록 전송 빠름)
jpg_as_text = base64.b64encode(buffer).decode('utf-8') # base64 문자열 변환
payload = json.dumps({"image": jpg_as_text})
client.publish(topic, payload) # MQTT로 발행
cap.release()
client.disconnect()
3) js 구현 (camera.html)
<!DOCTYPE html>
<html>
<head>
<title>실시간 객체탐지</title>
<meta charset="utf-8">
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
<h1>실시간 객체탐지</h1>
<img id="cameraView"/>
<script>
const imgEl = document.getElementById("cameraView");
const broker = "ws://localhost:9001"; // WebSocket 포트
const topic = "/camera/objects";
const client = mqtt.connect(broker, { protocolVersion: 5 }); // 브로커 연결
client.on('connect', () => {
console.log('Connected to broker');
client.subscribe(topic, (err) => { // 토픽 구독
if (!err) console.log(`Subscribed to topic: ${topic}`);
});
});
client.on('message', (topic, message) => {
try {
const payload = JSON.parse(message.toString());
imgEl.src = `data:image/jpeg;base64,${payload.image}`; // 이미지 표시
} catch (e) {
console.log('Failed to parse message:', e);
}
});
client.on('error', (error) => console.log('Connection error:', error));
client.on('close', () => console.log('Disconnected from broker'));
// Q 키로 종료
document.addEventListener('keydown', (e) => {
if (e.key === 'q' || e.key === 'Q') {
client.publish('camera/control', 'stop'); // Python에 종료 신호 발행
client.disconnect();
}
});
</script>
</body>
</html>
4) 실행
(1) 브로커 실행
| 구분 | Windows | Mac |
| 실행 | mosquitto -c "C:\Program Files\mosquitto\mosquitto.conf" | mosquitto -c /opt/homebrew/etc/mosquitto/mosquitto.conf |
| 백그라운드 | 서비스 등록 후 자동 실행 | brew services start mosquitto |
(2) Python 실행
python camera.py
(3) 브라우저 실행
- 브라우저에서 camera.html 을 직접 열거나 Spring Boot 실행 후 접속한다.
| 방법 | 주소 |
| 파일 직접 열기 | camera.html 파일을 브라우저로 드래그 |
| Spring Boot 사용 시 | http://localhost:8080/camera.html |
(4) 종료
- 브라우저에서 Q 키를 누르면 Python 프로세스까지 종료된다.
'AI > Spring AI' 카테고리의 다른 글
| [Spring AI] Spring AI로 OpenAI API 연동하기 — ChatClient (0) | 2026.06.01 |
|---|---|
| [Spring] WebClient로 OpenAI API 연동하기 (0) | 2026.06.01 |
| [Spring] WebFlux로 FastAPI 프록시 구현하기 — MultipartBodyBuilder, FilePart, WebClient (0) | 2026.06.01 |
| [Spring] Spring WebFlux란? — 동기/비동기, 블로킹/논블로킹, Mono/Flux, WebClient (0) | 2026.06.01 |
| [Spring AI] Spring Boot와 FastAPI로 AI 객체탐지 서비스 구현하기 (0) | 2026.05.30 |
블로그의 정보
우와한개발자 님의 블로그
우와한개발자