์๋ฐ ์ธ์ด๊ฐ IoT ๋๋ฐ์ด์ค๋ฅผ ์ํด ๋ง๋ค์ด์ง ๊ฒ์ผ๋ก ์๋ ค์ ธ ์์ง๋ง ์ค์ ๋ก๋ ๋ผ์ฆ๋ฒ ๋ฆฌํ์ด์ ๊ฐ์ ์ํ ๋๋ฐ์ด์ค์ ํ์ด์ฌ ๋๋ C๋ก ์์ฑ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ์์ฑ๋์ด ํฌํจ๋๋ ๊ฒ ๊ฐ๋ค. ์๋ฅผ ๋ค์ด, ํ์ด์ฌ ์ธ์ด๋ก AWS IoT ์ ํต์ ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๋๋ AWS IoT Device SDK v2 for Python๋ฅผ ์ฌ์ฉํ ์ ์๋ค. basic-connect.py ๋๋ pubsub.py ์ํ ์ฝ๋๋ฅผ ์ฐธ๊ณ ํ์ฌ ํด๋ผ์ด์ธํธ ๋๋ฐ์ด์ค๋ก ์์ง๋ ๋ฐ์ดํฐ๋ฅผ AWS IoT๋ฅผ ํตํด ๋ชจ๋ํฐ๋ง ์์คํ ์ผ๋ก ์ ๋ฌํ ์ ์์์ ํ ์คํธ ํด๋ณผ ์ ์๋ค. ํ์ด์ฌ ์ธ์ด๋ ํ ์คํธ ์์ง๋์ด๋ ์ฌ์ฉํ๋ ๋ฒ์ฉ์ ์ธ ์ธ์ด์ด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์์ QA ๋ชจ๋ ์ฌ์ฉํ ์ ์๋ ํ ์คํธ ์ฝ๋๊ฐ ๋๋ค.
ํ์ด์ฌ ํ๊ฒฝ ์ค๋นํ๊ธฐ โ
ํ์ด์ฌ ์ฝ๋๋ฅผ ์คํํ๊ธฐ ์ํด์๋ ํ์ด์ฌ ๊ฐ์ ํ๊ฒฝ
์ ์ค๋นํด์ผํ๋ค. ํ์ด์ฌ์ ๊ฐ์ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ๊ฑด ์๊ฐ๋ณด๋ค ์ฝ์ง ์์ ๋ถ๋ถ์ผ ์ ์๊ธฐ ๋๋ฌธ์ ํ์ด์ฌ์ ์ฃผ๋ก ๋ค๋ฃจ์ง ์๋ ๊ฐ๋ฐ์๋ผ๋ฉด ๋ฌ์คํธ๋ก ์์ฑ๋ UV๋ฅผ ํ์ด์ฌ ํจํค์ง ๊ด๋ฆฌ์๋ก ์ค์นํ์ฌ ํ์ด์ฌ ๊ฐ์ ํ๊ฒฝ(.venv)๋ฅผ ์ฝ๊ฒ ๊ตฌ์ฑํ๋๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค. ์ฐธ๊ณ ๋ก, ์ฌ๋ฌ๊ฐ์ง ํ์ด์ฌ์ผ๋ก ์์ฑ๋ MCP๋ฅผ ์ค์นํ ๋์๋ UV ๋ช
๋ น์ด๊ฐ ์ฌ์ฉ๋๊ณ ์๋ค.
PS C:\Users\Mambo> uv python install 3.12
Installed Python 3.12.9 in 7.79s
+ cpython-3.12.9-windows-x86_64-none
PS C:\Users\Mambo> uv python pin 3.12
Pinned `.python-version` to `3.12`
PS C:\Users\Mambo> uv python dir
C:\Users\Mambo\AppData\Roaming\uv\python
PS C:\Users\Mambo> uv init --app awsiot
Initialized project `awsiot` at `C:\Users\Mambo\awsiot`
PS C:\Users\Mambo\awsiot> uv run main.py
Using CPython 3.12.9
Creating virtual environment at: .venv
Hello from awsiot!
์ผ๋ฐ์ ์ผ๋ก PyCharm์ ์ฌ์ฉํด์ ํ์ด์ฌ ํ๋ก์ ํธ๋ฅผ ์ด๊ฒ ๋๋ฉด Python ์ธํฐํ๋ฆฌํฐ๋ฅผ ์ค์ ํด์ผ ๋ฉ๋๋ค. UV ๋ฅผ ์ฌ์ฉํ๋ ํ๋ก์ ํธ๋ ํ์ด์ฌ ๊ฐ์ ํ๊ฒฝ์ด ์๋์ผ๋ก ๊ตฌ์ฑ๋์๊ธฐ ๋๋ฌธ์ Python ์ธํฐํ๋ฆฌํฐ๊ฐ ๊ธฐ๋ณธ ์ค์ ๋จ์ ํ์ธํ ์ ์์ต๋๋ค.
ํ์ด์ฌ ํจํค์ง ์ค์นํ๊ธฐ โ
ํ์ด์ฌ ์ฝ๋์์ ํ์์ ์ผ๋ก ์ฌ์ฉํด์ผํ AWS IoT Device SDK v2 for Python (awsiotsdk)์ ํจ๊ป ํ๊ฒฝ ๋ณ์๋ฅผ ํ์ผ๋ก ์ ์ฉํ ์ ์๋ python-dotenv ๋ฅผ ์ค์นํ์. python-dotenv ๋ ๋ฐ๋์ ํ์ํ์ง ์์ง๋ง AWS IoT Endpoint ์ ClientID ์ ๊ฐ์ ์ผ๋ถ ์ ๋ณด๋ค์ ํ๋์ฝ๋ฉํ๋ ๊ฒ๋ณด๋ค๋ ๋์ ๊ฒ์ด๋ค.
PS C:\Users\Mambo\awsiot> uv add awsiotsdk python-dotenv
Resolved 4 packages in 57ms
Installed 3 packages in 16ms
+ awscrt==0.27.4
+ awsiotsdk==1.24.0
+ python-dotenv==1.1.1
mqtt_connection_builder ๋ก AWS IoT์ ์ฐ๊ฒฐํ๊ธฐ โ
์์ ์ฝ๋ ์ค basic_connect.py ๋ฅผ ๋ณด๋ฉด awsiotsdk ์ mqtt_connection_builder๋ฅผ ์ฌ์ฉํ๋ฉฐ MQTT ์ฐ๊ฒฐ์ ์์ฑํ ์ ์์์ ์ ์ ์๋ค. ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ๊ณ ํ์ด์ฌ ์ฝ๋๋ฅผ ์คํํด๋ณด๋ฉด AWS IoT ์๋ํฌ์ธํธ์ ์ฐ๊ฒฐ๋๊ณ ๋์ ์ข ๋ฃ๋จ์ ์ ์ ์๋ค.
import os
from awscrt import mqtt
from awsiot import mqtt_connection_builder
from dotenv import load_dotenv
load_dotenv()
ENDPOINT = os.getenv("ENDPOINT") # iot:Data-ATS endpoint
CLIENT_ID = os.getenv("CLIENT_ID")
TOPIC = os.getenv("TOPIC")
CERTIFICATE_PATH = os.getenv("CERTIFICATE_PATH")
PRIVATE_KEY_PATH = os.getenv("PRIVATE_KEY_PATH")
ROOT_CA_PATH = os.getenv("ROOT_CA_PATH")
if __name__ == '__main__':
print("\nStarting MQTT PubSub sample...")
# Create a MQTT connection
mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=ENDPOINT,
ca_filepath=ROOT_CA_PATH,
cert_filepath=CERTIFICATE_PATH,
pri_key_filepath=PRIVATE_KEY_PATH,
client_id=CLIENT_ID,
)
# Connect MQTT Broker...
print(f"Connecting to {ENDPOINT} with client ID '{CLIENT_ID}'...")
connect_future = mqtt_connection.connect()
connect_future.result() # Future.result() waits until a result is available
print("Connected!")
(awsiot) PS C:\Users\Mambo\awsiot> uv run main.py
Starting MQTT PubSub sample...
Connecting to $prefix$-ats.iot.ap-northeast-2.amazonaws.com with client ID '$client_id$'...
Connected!
Topic ๊ตฌ๋ ํ๊ณ MQTT ๋ฉ์์ง ๊ฒ์ํ๊ธฐ โ
์์ ์ฝ๋ ์ค pubsub.py ๋ฅผ ์ฐธ๊ณ ํ๋ฉด mqtt_connection ์ subscribe ํจ์๋ก ๋ฉ์์ง๋ฅผ ๊ตฌ๋ ํ๊ณ publish ํจ์๋ก ๋ฉ์์ง๋ฅผ ๊ฒ์ํ๋ ๊ฒ์ ์ ์ ์๋ค.
import os
import time
from awscrt import mqtt
from awsiot import mqtt_connection_builder
from dotenv import load_dotenv
load_dotenv()
# AWS IoT Core
ENDPOINT = os.getenv("ENDPOINT") # iot:Data-ATS endpoint
CLIENT_ID = os.getenv("CLIENT_ID") # Recommended Thing ID
TOPIC = os.getenv("TOPIC")
# AWS IoT Core - Thing Certificate
CERTIFICATE_PATH = os.getenv("CERTIFICATE_PATH")
PRIVATE_KEY_PATH = os.getenv("PRIVATE_KEY_PATH")
ROOT_CA_PATH = os.getenv("ROOT_CA_PATH") # Optional
def on_message_received(topic, payload):
print(f"Received message from {topic}: \n<<< {payload.decode()}")
if __name__ == '__main__':
print("\nStarting MQTT PubSub sample...")
# Create a MQTT connection
mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=ENDPOINT,
ca_filepath=ROOT_CA_PATH,
cert_filepath=CERTIFICATE_PATH,
pri_key_filepath=PRIVATE_KEY_PATH,
client_id=CLIENT_ID,
)
# Connect MQTT Broker...
print(f"Connecting to {ENDPOINT} with client ID '{CLIENT_ID}'...")
connect_future = mqtt_connection.connect()
connect_future.result() # Future.result() waits until a result is available
print("Connected!")
# Subscribe
print(f"Subscribing to topic '{TOPIC}'...")
subscribe_future, packet_id = mqtt_connection.subscribe(
topic=TOPIC,
qos=mqtt.QoS.AT_LEAST_ONCE,
callback=on_message_received)
subscribe_result = subscribe_future.result()
print(f"Subscribed with {subscribe_result}")
# Publish
print(f"Publishing message to topic '{TOPIC}'...")
payload = "PING"
mqtt_connection.publish(
topic=TOPIC,
payload=payload.encode('ascii'),
qos=mqtt.QoS.AT_LEAST_ONCE)
print(f"Published message to {TOPIC}: \n>>> {payload}")
time.sleep(2)
# Disconnect
print("Disconnecting...")
disconnect_future = mqtt_connection.disconnect()
disconnect_future.result()
print("Disconnected!")
(awsiot) PS C:\Users\Mambo\awsiot> uv run main.py
Starting MQTT PubSub sample...
Connecting to $prefix$-ats.iot.ap-northeast-2.amazonaws.com with client ID '$client_id$'...
Connected!
Subscribing to topic '$topic$'...
Subscribed with {'packet_id': 1, 'topic': '$topic$', 'qos': <QoS.AT_LEAST_ONCE: 1>}
Publishing message to topic '$topic$'...
Published message to $topic$:
>>> PING
Received message from $topic$:
<<< PING
Disconnecting...
Disconnected!
๋ฉ์์ง ๊ฒ์์ ๋์ผํ ํ ํฝ์ ๊ตฌ๋
ํ ๊ฒ์ผ๋ก MQTT ๋ฉ์์ง ๋ธ๋ก์ปค์ธ AWS IoT ์์ ๋ฉ์์ง๋ฅผ ์ ๋ฌ๋จ์ ํ์ธํ๋ค. ๋ค๋ง, AWS IoT ๋ก ๊ฒ์๋ ๋ฉ์์ง๊ฐ ๋ชจ๋ํฐ๋ง ์์คํ
์์ ์๊ตฌํ๋ ํ์ด๋ก๋ ํ์์ ๋ง๋์ง๋ ์ ์ ์๋ค. AWS IoT ์ ๋ฉ์์ง ๋ผ์ฐํ
๊ท์น
์ ๋ง๋ ์ฃผ์ ์ ๋ฉ์์ง๋ฅผ ๊ฒ์ํ๊ณ (๋ฉ์์ง๋ฅผ SQS ๋๊ธฐ์ด๋ก ์ ์กํ๋๋ก ์ค์ ํ๋ค๋ฉด) Amazon SQS ์ ๊ฒ์๋ ๋ฉ์์ง๊ฐ ์ ๋ฌ๋๋์ง๊น์ง ํ์ธํด์ผํ๋ค. ๋ํ, ๊ฒ์ํ ๋ฉ์์ง์ ํฌํจ๋ ๋ฐ์ดํฐ๊ฐ ๋ชจ๋ํฐ๋ง ์์คํ
์์ ํ์ธํ ์ ์๋ ๊ฐ๋จํ ์นํ์ด์ง๋ฅผ ๊ฐ๋ฐํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ค.
AWS IoT ๋ฃจํธ CA ์ธ์ฆ์ โ
ํ
์คํธ ์ฝ๋์์ ๋ฃจํธ CA ์ธ์ฆ์๊ฐ ๋ฐ๋์ ํ์ํ ๊ฒ์ ์๋์ง๋ง ์ปดํจํฐ ํ๊ฒฝ๋ง๋ค ์ ๋ขฐ ๊ฐ๋ฅํ ์ธ์ฆ์ ๋ชฉ๋ก์ ํฌํจ๋์ด์์ง ์์ ์ ์๊ธฐ ๋๋ฌธ์ ์ํธ ์ธ์ฆ(mTLS)
์ ์ํํ๊ธฐ ์ํด์ ๋ช
์์ ์ผ๋ก ์ง์ ํ ํ์๊ฐ ์๋ค. AWS IoT ์์ ์ธ์ฆ์๋ฅผ ๋ค์ด๋ก๋ ๋ฐ๊ฒ ๋๋ฉด Amazon Trust Services Repository ์์ ์ ๊ณตํ๋ AmazonRootCA1.pem ์ AmazonRootCA3.pem๊ฐ ํฌํจ๋๋๋ฐ RSA 2048๋นํธ ํค์ ํด๋น๋๋ AmazonRootCA1 ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ค. ํ์ฌ ๋ด ๊ฐ๋ฐ ํ๊ฒฝ์ผ๋ก ๊ตฌ์ฑ๋ AWS IoT ์ ์ฐ๊ฒฐํด๋ณธ ๊ฒฐ๊ณผ๋ก๋ ECC 256๋นํธ ํค์ ํด๋น๋๋ AmazonRootCA3๋ฅผ ์ฌ์ฉํ๋ awscrt.exceptions.AwsCrtError: AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE: TLS (SSL) negotiation failed
๋ผ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.