실습 — 실시간 센서 데이터 수집기
이 PART에서 배운 UDP를 실전에 가까운 예제로 마무리합니다. 온도 센서가 주기적으로 측정값을 쏘아 보내고, 수집기가 그것을 받아 모으는 프로그램입니다. UDP가 왜 이런 일에 어울리는지를 직접 확인하게 됩니다.
왜 여기에 UDP인가
센서 데이터 수집은 UDP의 성격과 잘 맞습니다. 센서는 1초에도 여러 번 값을 보냅니다. 그중 하나가 사라져도 곧 다음 값이 오니 큰일이 아닙니다. 연결을 맺고 끊는 부담 없이 가볍게 측정값만 던지면 되고, 수집기는 여러 센서가 보내는 데이터를 한 포트에서 모두 받을 수 있습니다. 도착 보장보다 가벼움과 실시간성이 중요한 전형적인 상황입니다.
측정값을 어떻게 담을까
센서가 보낼 데이터는 두 가지입니다. 어느 센서인지를 나타내는 번호와, 측정한 온도 값입니다. 온도는 22.5처럼 소수점이 있는 실수입니다.
PART 02의 파일 전송에서 길이를 바이트로 포장했듯, 여기서도 측정값을 정해진 형식으로 포장합니다. 센서 번호는 2바이트 정수로, 온도는 4바이트 실수로 담겠습니다. UDP는 한 데이터그램이 통째로 도착하므로, 이 6바이트가 쪼개질 걱정 없이 한 번에 옵니다. PART 02에서 TCP 때문에 recv_exactly로 고생했던 것과 대조적입니다.
센서 노드
먼저 측정값을 보내는 센서 쪽입니다.
# 새 파일: sensor_node.pyimport socketimport structimport timeclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)addr = ("127.0.0.1", 9700)temperatures = [21.5, 22.1, 22.8, 23.0, 22.4]for temp in temperatures: client.sendto(struct.pack(">Hf", 1, temp), addr) time.sleep(0.1)client.close()
핵심은 struct.pack(">Hf", 1, temp) 한 줄입니다. >는 네트워크 순서, H는 2바이트 정수, f는 4바이트 실수를 뜻합니다. 그래서 이 한 줄이 센서 번호 1과 온도 값을 6바이트로 포장합니다. 실제 센서라면 측정할 때마다 보내겠지만, 여기서는 미리 정한 다섯 개의 값을 0.1초 간격으로 보내 흉내 냅니다.
수집기
받아서 모으는 쪽입니다.
# 새 파일: sensor_collector.pyimport socketimport structserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)server.bind(("127.0.0.1", 9700))print("센서 수집기 대기 중")readings = []for _ in range(5): data, addr = server.recvfrom(1024) sensor_id, value = struct.unpack(">Hf", data) readings.append(value) print(f"센서 {sensor_id}: {value:.1f}도")average = sum(readings) / len(readings)print(f"평균 온도: {average:.2f}도")server.close()
수집기는 recvfrom으로 데이터그램을 받아 struct.unpack(">Hf", data)로 풀어냅니다. 보낼 때 pack으로 쌌던 것을 받을 때 unpack으로 푸는 것은 파일 전송 때와 같은 짝의 원리입니다. 풀어낸 센서 번호와 온도를 출력하고, 받은 값들을 모아 두었다가 마지막에 평균을 냅니다.
실행해 보기
수집기를 먼저 띄우고 센서 노드를 실행합니다.
# 파일: 터미널 1 (수집기)python3 sensor_collector.py
# 파일: 터미널 2 (센서)python3 sensor_node.py
수집기 쪽에 측정값이 실시간으로 찍히고, 마지막에 평균이 나옵니다.
센서 수집기 대기 중
센서 1: 21.5도
센서 1: 22.1도
센서 1: 22.8도
센서 1: 23.0도
센서 1: 22.4도
평균 온도: 22.36도
다섯 개의 측정값이 차례로 도착했고, 평균 22.36도가 계산되었습니다.
더 나아가기
이 작은 프로그램에는 확장의 여지가 많습니다. 센서 노드를 여러 개 띄우고 각각 다른 번호를 붙이면, 수집기 하나가 여러 센서의 데이터를 한꺼번에 받습니다. recvfrom이 보낸 사람의 주소를 함께 주므로, 어느 센서에서 왔는지도 구별할 수 있습니다. 앞 장에서 배운 멀티캐스트를 더하면, 한 수집기가 모은 데이터를 여러 모니터링 화면에 동시에 뿌릴 수도 있습니다.
이로써 UDP의 세계를 한 바퀴 돌았습니다. 연결 없는 가벼움, 보장 없는 자유로움, 그리고 필요할 때 직접 더하는 신뢰성까지. UDP를 통해 TCP의 가치도 더 깊이 이해하게 되었습니다.
지금까지 우리가 만든 서버에는 한 가지 공통된 약점이 있습니다. 한 번에 한 클라이언트만 상대할 수 있다는 점입니다. 다음 PART에서 이 한계를 정면으로 돌파해, 여러 클라이언트를 동시에 다루는 법을 배웁니다.