MQTT와 HTTP의 차이점
이 글에서는 MQTT와 HTTP의 아키텍처 및 성능 차이를 비교하고, 저전력·저대역폭의 IoT 환경에서 MQTT가 선호되는 이유와 각 프로토콜에 적합한 사용 사례를 설명합니다.
개요
서빙 로봇 회사에서 인턴으로 근무하면서, 로봇의 여러 시스템이 제가 웹 개발에서 익숙하게 사용하던 HTTP 대신 MQTT를 사용하고 있다는 것을 알게 되었습니다. 이를 계기로 왜 로봇과 IoT 기기들이 HTTP보다 MQTT를 선호하는 경향이 있는지 궁금해졌습니다. 이 글에서는 MQTT와 HTTP의 주요 차이점, 각각의 장단점을 살펴보고, 어떤 상황에 어떤 프로토콜을 사용하는 것이 적합한지 알아보고자 합니다.
MQTT란 무엇인가?
MQTT는 Message Queuing Telemetry Transport의 약자입니다. 신뢰성 있는 통신을 위해 TCP 연결을 사용하여 메시지 전달 보장, 패킷 오류 검사, 분할 및 순서 보장 등을 수행합니다. 이는 IoT(사물 인터넷) 환경을 위해 설계되었으며, 기본적으로 발행-구독(Publish-Subscribe) 모델을 사용합니다.
- 발행-구독 모델
- 발행자(Publisher)는 메시지를 수신할 대상(구독자)을 알지 못한 채 특정 ‘토픽(Topic)’으로 메시지를 보냅니다.
- 구독자(Subscriber)는 메시지를 발행하는 주체(발행자)를 알지 못한 채 관심 있는 ‘토픽’을 구독합니다.
- 브로커(Broker)는 발행자로부터 모든 메시지를 수신하여 해당 토픽을 구독하는 구독자들에게 메시지를 전달하는 중앙 허브 역할을 합니다.
각 클라이언트는 브로커에 연결을 설정한 후, 특정 토픽에 메시지를 발행하거나 구독할 수 있습니다.
MQTT의 가장 중요한 특징 중 하나는 한 번 연결이 설정되면 세션 내내 연결이 유지된다는 점입니다. 따라서 연결이 수립된 후에는 양방향으로 원하는 만큼 메시지를 주고받을 수 있습니다.
MQTT 메시지는 특정 명령어(제어 패킷 또는 메시지 타입)를 가집니다. 주요 MQTT 명령어는 다음과 같습니다.
- CONNECT: 클라이언트가 브로커와의 연결을 수립하기 위해 보냅니다.
- PUBLISH: 클라이언트 또는 브로커가 애플리케이션 메시지를 전달하기 위해 보냅니다.
- SUBSCRIBE: 클라이언트가 특정 토픽을 구독하기 위해 보냅니다.
- DISCONNECT: 클라이언트가 정상적으로 연결을 종료했음을 알리기 위해 보냅니다.
또한, MQTT는 신뢰할 수 없는 네트워크 환경에서 메시지를 안정적으로 전달하기 위해 서비스 품질(Quality of Service, QoS) 개념을 도입했습니다.
- 서비스 품질 (QoS)
- QoS 0 (최대 한 번 전송, At most once): 메시지를 한 번만 보내고 전달 여부를 확인하지 않습니다. (Fire and forget)
- QoS 1 (최소 한 번 전송, At least once): 메시지가 최소 한 번은 전달되도록 보장하지만, 중복 수신이 발생할 수 있습니다.
- QoS 2 (정확히 한 번 전송, Exactly once): 메시지가 중복 없이 정확히 한 번만 전달되도록 보장합니다. 가장 신뢰성이 높지만 그만큼 리소스 소모가 큽니다.
HTTP란 무엇인가?
HTTP는 Hypertext Transfer Protocol의 약자입니다. 이는 월드 와이드 웹(WWW)에서 데이터를 주고받기 위한 기반 프로토콜이며, 기본적으로 요청-응답(Request-Response) 모델을 사용합니다.
- 요청-응답 모델
- 클라이언트(주로 웹 브라우저)가 서버에 요청을 보냅니다.
- 서버는 그 요청을 처리하고 적절한 응답을 반환합니다.
이 모델에서는 기본적으로 각 요청마다 새로운 연결 설정이 필요합니다. (HTTP/1.1 이후부터는 keep-alive 기능이 도입되어 일정 시간 동안 연결을 유지할 수 있지만, MQTT처럼 세션 내내 유지되는 것은 아닙니다.) 통신은 리소스 중심으로 이루어지며, URL을 사용하여 특정 리소스를 식별하고 접근합니다.
HTTP 클라이언트는 주로 다음과 같은 요청 메서드를 사용합니다.
- GET: 서버로부터 데이터를 조회합니다.
- POST: 서버에 데이터를 제출하여 처리를 요청합니다.
- PUT: 지정된 리소스를 생성하거나 전체를 교체합니다.
- DELETE: 지정된 리소스를 삭제합니다.
MQTT와 HTTP의 차이점
| MQTT | HTTP | |
|---|---|---|
| 아키텍처 | 발행-구독 (Publish-Subscribe) | 요청-응답 (Request-Response) |
| 명령어 대상 | 토픽 (Topics) | URL |
| 기반 프로토콜 | TCP/IP | TCP/IP |
| 메시징 모드 | 비동기, 이벤트 기반 (Asynchronous, event-based) | 동기 (Synchronous) |
| 메시지 큐잉 | 브로커가 연결 끊긴 구독자를 위해 메시지 큐잉 가능 | 애플리케이션 레벨에서 직접 구현 필요 |
| 메시지 오버헤드 | 최소 2바이트, 헤더 데이터는 바이너리 형식 | 최소 8바이트, 헤더 데이터는 텍스트 형식 (압축 가능) |
| 메시지 크기 | 최대 256MB | 제한은 없으나, 일반적인 사용 사례에서 256MB는 매우 큼 |
| 콘텐츠 타입 | 모든 종류 (바이너리) | 텍스트 (바이너리는 Base64 인코딩 필요) |
| 메시지 분배 | 일대다 (One-to-Many) | 일대일 (One-to-One) |
| 신뢰성 | 서비스 품질(QoS) 0, 1, 2 제공 | 애플리케이션 레벨에서 직접 구현 필요 |
더 자세한 차이점을 깊이 있게 살펴보겠습니다.
아키텍처
- MQTT: 발행-구독 모델로, 발행자와 구독자는 서로를 알 필요가 없습니다. 브로커가 메시지 라우팅을 모두 처리합니다. 이 덕분에 구성 요소 간의 결합도가 낮아집니다.
- HTTP: 요청-응답 모델로, 클라이언트와 서버 간의 직접적인 통신이 필요합니다. 클라이언트가 항상 요청을 시작해야 합니다.
주소 지정 (Addressing)
- MQTT: 계층적인 구조의 토픽(예:
device/sensor/temperature)을 사용하며, 와일드카드(+,#)를 지원하여 유연한 구독이 가능합니다. - HTTP: URL을 사용하여 서버에 있는 특정 리소스를 식별합니다.
메시징 모드
- MQTT: 비동기 및 이벤트 기반입니다. 클라이언트는 이벤트가 발생했을 때 자동으로 업데이트를 수신합니다. 데이터를 실시간으로 푸시(push)하는 데 유리합니다.
- HTTP: 동기 방식입니다. 클라이언트는 데이터를 요청하고 응답을 받을 때까지 기다려야 합니다. 데이터를 폴링(polling)하는 구조입니다.
메시지 큐잉
- MQTT: 브로커에 메시지 큐잉 기능이 내장되어 있습니다. 클라이언트가 오프라인 상태일 때 메시지를 저장했다가 다시 연결되면 전달할 수 있습니다. (QoS 레벨에 따라 동작이 다름)
- HTTP: 기본적으로 큐잉 기능이 없으며, 애플리케이션 레벨에서 별도로 구현해야 합니다.
효율성
- MQTT: 최소 2바이트의 작은 헤더와 바이너리 형식을 사용하여 대역폭이 제한된 환경에 최적화되어 있습니다. 배터리 소모가 적고 네트워크 부하가 적습니다.
- HTTP: 상대적으로 큰 헤더(최소 8바이트)와 텍스트 기반 형식으로 더 많은 대역폭을 소모합니다.
메시지 분배
- MQTT: 일대다(One-to-Many) 통신이 효율적입니다. 하나의 메시지로 여러 구독자에게 동시에 도달할 수 있습니다.
- HTTP: 일대일(One-to-One) 통신 방식입니다. 서버는 각 클라이언트의 요청을 개별적으로 처리해야 합니다.
신뢰성
- MQTT: 세 가지 내장 QoS 레벨을 통해 다양한 수준의 메시지 전달 보장을 제공합니다. 네트워크가 불안정해도 메시지 유실을 방지할 수 있습니다.
- HTTP: 신뢰성 보장은 TCP 레벨에 의존하며, 애플리케이션 레벨에서 재시도 로직 등을 직접 구현해야 합니다.
IoT 환경에서 MQTT와 HTTP의 성능 비교
Google IoT Core에서 MQTT와 HTTP의 성능을 비교한 예시가 있습니다.
TCP 메시지 오버헤드
| 동작 | MQTT (Bytes) | HTTP (Bytes) |
|---|---|---|
| 연결 수립 | 5,572 | 2,261 |
| 연결 종료 | 376 (선택 사항) | 0 |
| 메시지 1개 발행 시마다 | 388 | 3,285 |
| 메시지 1개 전송 시 총합 | 6,336 | 5,546 |
| 메시지 10개 전송 시 총합 | 9,829 | 55,460 |
| 메시지 100개 전송 시 총합 | 44,748 | 554,600 |
연결을 수립할 때는 MQTT가 더 많은 바이트를 필요로 합니다. 이는 TCP 연결이 완료된 후 MQTT CONNECT 패킷 교환 과정이 추가로 있기 때문입니다. 하지만 일단 MQTT 연결이 수립되면, 여러 메시지를 처리할 때 HTTP보다 훨씬 적은 바이트를 사용합니다. HTTP는 각 요청마다 새로운 연결을 설정(하거나 최소한의 핸드셰이크를 반복)해야 하기 때문입니다. 즉, 초기 연결 비용은 MQTT가 높지만, 지속적인 통신에서는 MQTT가 훨씬 효율적임을 알 수 있습니다.
응답 시간
| MQTT의 연결 주기 당 메시지 수 | MQTT 메시지 당 평균 응답 시간 (ms) (QoS 1) | HTTP 메시지 당 평균 응답 시간 (ms) |
|---|---|---|
| 1 | 113 | 289 |
| 10 | 47 | 289 |
| 100 | 43 | 289 |
MQTT는 전송하는 메시지 수가 늘어날수록 메시지 당 평균 응답 시간이 감소하는 것을 볼 수 있습니다. 이는 동일한 TCP 연결을 여러 메시지에 재사용하기 때문에 예상된 결과입니다.
하지만 한 가지 의문이 들었습니다. 메시지가 1개일 때, 연결 패킷 크기가 더 큰 MQTT가 어떻게 HTTP보다 더 빠른 응답 시간을 가질 수 있었을까요? 몇 가지 자료를 찾아본 후 다음과 같은 결론에 도달했습니다.
- HTTP의 프로토콜 오버헤드: HTTP는 헤더 파싱, 상태 코드 처리 등 더 많은 처리 과정이 필요합니다.
- HTTP의 핸드셰이크: HTTP는 TCP 연결뿐만 아니라, 요청 시마다 HTTP 레벨의 핸드셰이크(메서드, 경로, 헤더 전송 및 파싱)가 필요합니다. 이 과정이 응답 시간을 길게 만듭니다.
즉, 단순히 TCP 연결 패킷 크기만으로 성능을 판단할 수 없으며, 애플리케이션 계층에서 발생하는 프로토콜 처리 오버헤드가 실제 응답 시간에 큰 영향을 미친다는 것을 알 수 있습니다.
결론
MQTT가 적합한 사용 사례
- IoT 및 로보틱스: 전력 및 대역폭이 제한된 환경
- 실시간 통신: 즉각적인 업데이트가 필요한 채팅 앱, 실시간 모니터링 시스템
- 지속적인 데이터 교환: 하나의 연결을 통해 다수의 메시지를 주고받는 경우
- 불안정한 네트워크: 네트워크 연결이 자주 끊기는 환경
- 양방향 통신: 클라이언트와 서버(브로커)가 서로 메시지를 주고받아야 하는 경우
HTTP가 적합한 사용 사례
- 웹 기반 애플리케이션: 브라우저와의 연동이 필수적인 경우
- RESTful API 설계: 리소스 중심의 명확한 구조가 필요한 경우
- 일회성 트랜잭션: 연결 오버헤드가 크게 문제되지 않는 간단한 데이터 요청/전송
MQTT와 HTTP의 차이점을 학습한 후, 제가 근무했던 회사의 로봇 서비스들이 왜 MQTT를 사용했는지 명확히 이해하게 되었습니다. 성능과 신뢰성 측면에서 MQTT는 원격 측정(Telemetry) 데이터를 주고받는 데 HTTP보다 확실한 이점을 가지고 있었습니다.
또한, 성능 테스트 결과를 보면서 각 사용 사례에 맞는 적절한 프로토콜을 선택하는 것이 얼마나 중요한지 다시 한번 깨닫게 되었습니다.
