Skip to content

์ž๋ฐ” ์–ธ์–ด๊ฐ€ IoT ๋””๋ฐ”์ด์Šค๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ์œผ๋กœ ์•Œ๋ ค์ ธ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์™€ ๊ฐ™์€ ์†Œํ˜• ๋””๋ฐ”์ด์Šค์— ํŒŒ์ด์ฌ ๋˜๋Š” C๋กœ ์ž‘์„ฑ๋œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ž‘์„ฑ๋˜์–ด ํฌํ•จ๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŒŒ์ด์ฌ ์–ธ์–ด๋กœ AWS IoT ์™€ ํ†ต์‹ ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค๋•Œ๋Š” AWS IoT Device SDK v2 for Python๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. basic-connect.py ๋˜๋Š” pubsub.py ์ƒ˜ํ”Œ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ๋””๋ฐ”์ด์Šค๋กœ ์ˆ˜์ง‘๋œ ๋ฐ์ดํ„ฐ๋ฅผ AWS IoT๋ฅผ ํ†ตํ•ด ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Œ์„ ํ…Œ์ŠคํŠธ ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํŒŒ์ด์ฌ ์–ธ์–ด๋Š” ํ…Œ์ŠคํŠธ ์—”์ง€๋‹ˆ์–ด๋„ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ”์šฉ์ ์ธ ์–ธ์–ด์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ์ž์™€ QA ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค.

ํŒŒ์ด์ฌ ํ™˜๊ฒฝ ์ค€๋น„ํ•˜๊ธฐ โ€‹

ํŒŒ์ด์ฌ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํŒŒ์ด์ฌ ๊ฐ€์ƒ ํ™˜๊ฒฝ์„ ์ค€๋น„ํ•ด์•ผํ•œ๋‹ค. ํŒŒ์ด์ฌ์˜ ๊ฐ€์ƒ ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฑด ์ƒ๊ฐ๋ณด๋‹ค ์‰ฝ์ง€ ์•Š์€ ๋ถ€๋ถ„์ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŒŒ์ด์ฌ์„ ์ฃผ๋กœ ๋‹ค๋ฃจ์ง€ ์•Š๋Š” ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋Ÿฌ์ŠคํŠธ๋กœ ์ž‘์„ฑ๋œ UV๋ฅผ ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž๋กœ ์„ค์น˜ํ•˜์—ฌ ํŒŒ์ด์ฌ ๊ฐ€์ƒ ํ™˜๊ฒฝ(.venv)๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•˜๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. ์ฐธ๊ณ ๋กœ, ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํŒŒ์ด์ฌ์œผ๋กœ ์ž‘์„ฑ๋œ MCP๋ฅผ ์„ค์น˜ํ•  ๋•Œ์—๋„ UV ๋ช…๋ น์–ด๊ฐ€ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค.

powershell
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 ์™€ ๊ฐ™์€ ์ผ๋ถ€ ์ •๋ณด๋“ค์„ ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ๋‚˜์„ ๊ฒƒ์ด๋‹ค.

powershell
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 ์—”๋“œํฌ์ธํŠธ์— ์—ฐ๊ฒฐ๋˜๊ณ ๋‚˜์„œ ์ข…๋ฃŒ๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

python
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!")
powershell
(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 ํ•จ์ˆ˜๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฒŒ์‹œํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

python
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!")
powershell
(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 ๋ผ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

Released under the MIT License.