반응형

 

MQTT Keep-Alive의 모든 것: 연결이 끊어져도 당황하지 않는 법

IoT 개발을 하다 보면 한 번쯤은 겪게 되는 상황이 있습니다. 분명히 잘 작동하던 MQTT 클라이언트가 갑자기 메시지를 받지 못하거나, 브로커와의 연결이 끊어져서 데이터가 손실되는 일 말이죠. 특히 실시간성이 중요한 시스템에서는 이런 연결 문제가 치명적일 수 있습니다.

하지만 걱정하지 마세요. MQTT는 이런 문제를 해결하기 위해 정교한 연결 관리 시스템을 내장하고 있습니다. 오늘은 MQTT의 keep-alive 메커니즘을 속속들이 파헤쳐보면서, 안정적인 IoT 시스템을 구축하는 비법을 알아보겠습니다.

 

Keep-Alive, 넌 누구냐?

MQTT의 keep-alive는 마치 "나 여기 있어요!"라고 주기적으로 손을 흔드는 것과 같습니다. 클라이언트가 브로커에게 "내가 아직 살아있고 연결도 정상이야"라는 신호를 보내는 생명선 역할을 합니다.

여기서 중요한 점은 keep-alive가 클라이언트 주도라는 것입니다. 브로커는 수동적으로 기다리기만 하고, 모든 keep-alive 활동은 클라이언트가 담당합니다. 마치 학생이 선생님에게 "저 깨어있어요!"라고 손을 들어 보이는 것과 비슷하죠.

import paho.mqtt.client as mqtt

# 60초 keep-alive로 연결 설정
# "60초마다 내가 살아있다고 알려줄게요"라는 약속
client = mqtt.Client()
client.connect("localhost", 1883, 60)  # 마지막 숫자가 keep-alive 값

 

Keep-Alive의 정교한 동작 원리

Keep-alive의 동작 방식을 단계별로 살펴보면 정말 잘 설계되었다는 것을 알 수 있습니다.

먼저 클라이언트는 자신이 브로커에게 마지막으로 보낸 메시지 시점을 계속 추적합니다. PUBLISH나 SUBSCRIBE 같은 일반적인 메시지를 보냈다면 별도로 할 일이 없습니다. 하지만 keep-alive 시간의 절반이 지나도록 아무것도 보내지 않았다면, 이때 PINGREQ라는 특별한 "안부 인사" 패킷을 전송합니다.

브로커는 이 PINGREQ를 받으면 "응, 너 살아있구나" 하면서 PINGRESP로 대답해줍니다. 이 과정이 마치 전화로 "여보세요? 거기 계세요?" "네, 여기 있어요" 하는 것과 똑같습니다.

만약 클라이언트가 PINGRESP를 받지 못하거나, 브로커가 keep-alive 시간의 1.5배 동안 클라이언트로부터 아무 소식을 듣지 못하면, 양쪽 모두 "아, 연결에 문제가 생겼구나" 하고 연결을 정리합니다.

 

실전 시나리오: 15초 Keep-Alive의 마법

실무에서 자주 사용하는 패턴 중 하나가 "15초 keepalive → 끊기면 즉시 reconnect" 전략입니다. 이것이 어떻게 작동하는지 살펴볼까요?

import paho.mqtt.client as mqtt
import time

def on_disconnect(client, userdata, rc):
    """연결이 끊어졌을 때 자동으로 실행되는 함수"""
    print("연결 끊김 감지! 재연결 시도 중...")
    
    # 재연결 성공할 때까지 계속 시도
    while True:
        try:
            client.reconnect()
            print("재연결 성공!")
            break
        except:
            print("재연결 실패, 1초 후 다시 시도...")
            time.sleep(1)

# 클라이언트 설정
client = mqtt.Client()
client.on_disconnect = on_disconnect

# 15초로 짧은 keep-alive 설정
# 문제 발생 시 최대 15초 내에 감지 가능
client.connect("broker.example.com", 1883, 15)

15초라는 짧은 간격이 핵심입니다. 네트워크에 문제가 생기면 최대 15초 내에 감지할 수 있고, 감지되는 순간 자동으로 재연결을 시도합니다. 사용자는 아무것도 모르는 사이에 시스템이 스스로 복구되는 것이죠.

 

브로커의 숨겨진 일들

클라이언트 연결이 끊어지면 브로커는 단순히 "아, 연결 끊어졌네" 하고 끝나는 것이 아닙니다. 뒤에서 정말 많은 일들이 벌어집니다.

먼저 해당 클라이언트의 모든 구독 정보를 정리합니다. Clean Session으로 설정된 클라이언트라면 모든 정보를 깔끔하게 삭제하고, Persistent Session이라면 나중에 재연결할 때를 대비해서 정보를 보관해둡니다.

그리고 Will Message가 설정되어 있다면 다른 구독자들에게 "이 클라이언트가 예상치 못하게 사라졌어요"라는 알림을 보냅니다. 마지막으로 TCP 소켓을 정리하면서 모든 네트워크 연결을 끊어버립니다.

# Will Message 설정 예시
client.will_set("device/status", "offline", retain=True)
# 연결이 비정상적으로 끊어지면 "offline" 메시지가 자동 발송됨

 

TCP Keep-Alive vs MQTT Keep-Alive

여기서 조금 복잡해지는 부분이 있습니다. 사실 TCP 레벨에서도 자체적인 keep-alive 메커니즘이 있거든요. 이것이 MQTT keep-alive와 어떻게 다른지 이해하는 것이 중요합니다.

TCP keep-alive는 운영체제 레벨에서 동작합니다. 기본적으로 2시간 동안 아무런 통신이 없으면 "혹시 상대방 살아있나?" 하고 빈 패킷을 보내기 시작합니다. 하지만 2시간은 너무 길죠. IoT 시스템에서 2시간 동안 연결 문제를 모르고 있으면 큰일납니다.

그래서 MQTT keep-alive가 필요한 것입니다. 애플리케이션 레벨에서 훨씬 빠르게 연결 상태를 확인할 수 있거든요. 마치 이중 안전장치처럼 두 메커니즘이 함께 작동해서 시스템의 안정성을 보장합니다.

 

네트워크 세상의 현실적인 문제들

이론은 완벽하지만 현실은 복잡합니다. 실제 네트워크 환경에서는 예상치 못한 일들이 많이 벌어집니다.

예를 들어 브로커가 TCP 소켓을 닫았는데, 그 신호가 클라이언트까지 도달하지 못할 수 있습니다. 중간에 있는 공유기나 방화벽이 패킷을 드롭시키거나, 네트워크 분할이 일어날 수 있거든요. 이런 상황에서는 클라이언트가 "연결이 살아있다"고 착각하면서 계속 메시지를 보내려고 시도합니다.

하지만 결국 MQTT keep-alive가 이 문제를 해결해줍니다. PINGREQ를 보내도 응답이 없으면 "아, 뭔가 문제가 있구나" 하고 연결 끊김을 감지할 수 있으니까요.

물리적 네트워크 장비에 문제가 생기는 경우도 흥미롭습니다. 클라이언트와 브로커 양쪽 모두 "상대방과 연결되어 있다"고 생각하지만, 실제로는 중간 경로가 막혀서 통신이 불가능한 상태가 되는 것이죠. 이때도 keep-alive가 실제 통신을 시도해보고 나서야 문제를 발견할 수 있습니다.

 

실무에서의 황금 법칙들

이 모든 내용을 바탕으로 실무에서 적용할 수 있는 팁들을 정리해보겠습니다.

Keep-alive 값 설정은 시스템의 성격에 따라 달라집니다. 실시간성이 중요한 시스템이라면 15-30초로 짧게 설정하고, 배터리 효율이 중요한 IoT 기기라면 5-10분으로 길게 설정할 수 있습니다. 너무 짧으면 네트워크 부담이 되고, 너무 길면 장애 감지가 늦어진다는 점을 기억하세요.

자동 재연결 로직은 필수입니다. 연결이 끊어졌을 때 수동으로 복구하는 것은 현실적이지 않거든요. 재연결 시도 간격도 점진적으로 늘려가는 것이 좋습니다. 1초, 2초, 4초, 8초 이런 식으로 말이죠.

def exponential_backoff_reconnect(client, max_delay=60):
    """지수적 백오프를 적용한 재연결 로직"""
    delay = 1
    
    while True:
        try:
            client.reconnect()
            print("재연결 성공!")
            break
        except:
            print(f"재연결 실패, {delay}초 후 다시 시도...")
            time.sleep(delay)
            delay = min(delay * 2, max_delay)  # 최대 60초까지만

Will Message도 적극 활용하세요. 다른 클라이언트들이 "이 기기가 갑자기 사라졌구나" 하고 알 수 있게 해주면 시스템 전체의 안정성이 향상됩니다.

 

마무리: 연결 관리의 달인 되기

MQTT의 keep-alive 메커니즘을 이해하고 나면, 이것이 얼마나 정교하고 실용적인 시스템인지 감탄하게 됩니다. 애플리케이션 레벨과 네트워크 레벨의 이중 보호 장치, 클라이언트와 브로커의 양방향 감지, 그리고 다양한 장애 상황에 대한 대응책까지 모든 것이 체계적으로 설계되어 있습니다.

이제 여러분도 네트워크가 불안정해도, 서버가 재시작되어도, 중간 장비에 문제가 생겨도 당황하지 않을 수 있습니다. MQTT keep-alive가 여러분의 시스템을 든든하게 지켜줄 테니까요.

다음에 IoT 프로젝트를 시작할 때는 처음부터 keep-alive 설정과 재연결 로직을 신중하게 계획해보세요. 그러면 사용자들이 "이 시스템 정말 안정적이네!" 하고 감탄하는 날이 올 것입니다.

반응형

+ Recent posts