04. WiFi ๋„คํŠธ์›Œํ‚น

04. WiFi ๋„คํŠธ์›Œํ‚น

ํ•™์Šต ๋ชฉํ‘œ

  • ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด WiFi ์„ค์ • ๋ฐฉ๋ฒ• ์Šต๋“
  • Python ์†Œ์ผ“ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ์ดˆ ์ดํ•ด
  • ESP32 WiFi ๊ฐœ์š” ํŒŒ์•…
  • ๋„คํŠธ์›Œํฌ ์Šค์บ” ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง
  • HTTP ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก

1. ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด WiFi ์„ค์ •

1.1 ๋ช…๋ น์ค„ WiFi ์„ค์ •

# ํ˜„์žฌ WiFi ์ƒํƒœ ํ™•์ธ
iwconfig wlan0

# ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„คํŠธ์›Œํฌ ์Šค์บ”
sudo iwlist wlan0 scan | grep -E "ESSID|Quality"

# WiFi ์—ฐ๊ฒฐ (nmcli ์‚ฌ์šฉ)
sudo nmcli dev wifi connect "SSID์ด๋ฆ„" password "๋น„๋ฐ€๋ฒˆํ˜ธ"

# ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ
nmcli connection show

# IP ์ฃผ์†Œ ํ™•์ธ
ip addr show wlan0

1.2 wpa_supplicant ์„ค์ •

# /etc/wpa_supplicant/wpa_supplicant.conf ํŽธ์ง‘
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
# /etc/wpa_supplicant/wpa_supplicant.conf
country=KR
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

# ๊ธฐ๋ณธ WPA2 ๋„คํŠธ์›Œํฌ
network={
    ssid="MyNetwork"
    psk="MyPassword"
    key_mgmt=WPA-PSK
}

# ์ˆจ๊ฒจ์ง„ ๋„คํŠธ์›Œํฌ
network={
    ssid="HiddenNetwork"
    scan_ssid=1
    psk="Password"
}

# ์šฐ์„ ์ˆœ์œ„ ์„ค์ • (๋†’์€ ๊ฐ’ = ์šฐ์„ )
network={
    ssid="PreferredNetwork"
    psk="Password"
    priority=10
}

1.3 Python์œผ๋กœ WiFi ์ •๋ณด ์กฐํšŒ

#!/usr/bin/env python3
"""WiFi ์—ฐ๊ฒฐ ์ •๋ณด ์กฐํšŒ"""

import subprocess
import re

def get_wifi_info() -> dict:
    """ํ˜„์žฌ WiFi ์—ฐ๊ฒฐ ์ •๋ณด ๋ฐ˜ํ™˜"""
    info = {}

    try:
        # SSID ์กฐํšŒ
        result = subprocess.run(
            ['iwgetid', '-r'],
            capture_output=True,
            text=True
        )
        info['ssid'] = result.stdout.strip()

        # IP ์ฃผ์†Œ ์กฐํšŒ
        result = subprocess.run(
            ['hostname', '-I'],
            capture_output=True,
            text=True
        )
        ips = result.stdout.strip().split()
        info['ip_addresses'] = ips

        # ์‹ ํ˜ธ ๊ฐ•๋„ ์กฐํšŒ
        result = subprocess.run(
            ['iwconfig', 'wlan0'],
            capture_output=True,
            text=True
        )
        match = re.search(r'Signal level=(-?\d+)', result.stdout)
        if match:
            info['signal_dbm'] = int(match.group(1))

        # MAC ์ฃผ์†Œ
        result = subprocess.run(
            ['cat', '/sys/class/net/wlan0/address'],
            capture_output=True,
            text=True
        )
        info['mac_address'] = result.stdout.strip()

    except Exception as e:
        info['error'] = str(e)

    return info

def get_wifi_networks() -> list:
    """์ฃผ๋ณ€ WiFi ๋„คํŠธ์›Œํฌ ์Šค์บ”"""
    networks = []

    try:
        result = subprocess.run(
            ['sudo', 'iwlist', 'wlan0', 'scan'],
            capture_output=True,
            text=True
        )

        current_network = {}
        for line in result.stdout.split('\n'):
            if 'ESSID:' in line:
                ssid = re.search(r'ESSID:"(.+)"', line)
                if ssid and current_network:
                    networks.append(current_network)
                current_network = {'ssid': ssid.group(1) if ssid else ''}

            elif 'Quality=' in line:
                quality = re.search(r'Quality=(\d+)/(\d+)', line)
                if quality:
                    current_network['quality'] = f"{quality.group(1)}/{quality.group(2)}"

                signal = re.search(r'Signal level=(-?\d+)', line)
                if signal:
                    current_network['signal_dbm'] = int(signal.group(1))

        if current_network:
            networks.append(current_network)

    except Exception as e:
        print(f"์Šค์บ” ์‹คํŒจ: {e}")

    return networks

if __name__ == "__main__":
    print("=== WiFi ์—ฐ๊ฒฐ ์ •๋ณด ===")
    info = get_wifi_info()
    for key, value in info.items():
        print(f"  {key}: {value}")

    print("\n=== ์ฃผ๋ณ€ WiFi ๋„คํŠธ์›Œํฌ ===")
    networks = get_wifi_networks()
    for net in networks[:10]:  # ์ƒ์œ„ 10๊ฐœ๋งŒ
        print(f"  {net.get('ssid', 'Unknown')}: {net.get('signal_dbm', 'N/A')} dBm")

2. Python ์†Œ์ผ“ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

2.1 ์†Œ์ผ“ ๊ธฐ์ดˆ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    ์†Œ์ผ“ ํ†ต์‹  ํ๋ฆ„                            โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚   ํด๋ผ์ด์–ธํŠธ                              ์„œ๋ฒ„                โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚   โ”‚ socket()โ”‚                        โ”‚ socket()โ”‚            โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜                        โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚        โ”‚                                  โ”‚                 โ”‚
โ”‚        โ”‚                             โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚        โ”‚                             โ”‚  bind() โ”‚            โ”‚
โ”‚        โ”‚                             โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚        โ”‚                             โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚        โ”‚                             โ”‚ listen()โ”‚            โ”‚
โ”‚        โ”‚                             โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”      ์—ฐ๊ฒฐ ์š”์ฒญ         โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚   โ”‚connect()โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚ accept()โ”‚            โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜                        โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚        โ”‚                                  โ”‚                 โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”      ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹      โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚   โ”‚  send() โ”‚ โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  recv() โ”‚            โ”‚
โ”‚   โ”‚  recv() โ”‚                        โ”‚  send() โ”‚            โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜                        โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚        โ”‚                                  โ”‚                 โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”                        โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”            โ”‚
โ”‚   โ”‚ close() โ”‚                        โ”‚ close() โ”‚            โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜            โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

2.2 TCP ์„œ๋ฒ„

#!/usr/bin/env python3
"""TCP ์„œ๋ฒ„ - ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ """

import socket
import json
from datetime import datetime

HOST = '0.0.0.0'  # ๋ชจ๋“  ์ธํ„ฐํŽ˜์ด์Šค
PORT = 9999

def start_tcp_server():
    """TCP ์„œ๋ฒ„ ์‹œ์ž‘"""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
        # ์ฃผ์†Œ ์žฌ์‚ฌ์šฉ ํ—ˆ์šฉ
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        server.bind((HOST, PORT))
        server.listen(5)

        print(f"TCP ์„œ๋ฒ„ ์‹œ์ž‘: {HOST}:{PORT}")

        while True:
            client, address = server.accept()
            print(f"ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ: {address}")

            with client:
                while True:
                    data = client.recv(1024)
                    if not data:
                        break

                    try:
                        # JSON ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ
                        message = json.loads(data.decode('utf-8'))
                        timestamp = datetime.now().strftime("%H:%M:%S")
                        print(f"[{timestamp}] ์ˆ˜์‹ : {message}")

                        # ์‘๋‹ต ์ „์†ก
                        response = {
                            "status": "ok",
                            "received": message.get("sensor_id")
                        }
                        client.sendall(json.dumps(response).encode('utf-8'))

                    except json.JSONDecodeError:
                        print(f"์ž˜๋ชป๋œ JSON: {data}")

            print(f"ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ข…๋ฃŒ: {address}")

if __name__ == "__main__":
    start_tcp_server()

2.3 TCP ํด๋ผ์ด์–ธํŠธ

#!/usr/bin/env python3
"""TCP ํด๋ผ์ด์–ธํŠธ - ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ „์†ก"""

import socket
import json
import time
import random

SERVER_HOST = '192.168.1.100'  # ์„œ๋ฒ„ IP
SERVER_PORT = 9999

def send_sensor_data():
    """์„ผ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„๋กœ ์ „์†ก"""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
        client.connect((SERVER_HOST, SERVER_PORT))
        print(f"์„œ๋ฒ„ ์—ฐ๊ฒฐ: {SERVER_HOST}:{SERVER_PORT}")

        sensor_id = "temp_sensor_01"

        try:
            while True:
                # ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
                data = {
                    "sensor_id": sensor_id,
                    "temperature": round(random.uniform(20, 30), 1),
                    "humidity": round(random.uniform(40, 70), 1),
                    "timestamp": time.time()
                }

                # ์ „์†ก
                message = json.dumps(data).encode('utf-8')
                client.sendall(message)
                print(f"์ „์†ก: {data}")

                # ์‘๋‹ต ์ˆ˜์‹ 
                response = client.recv(1024)
                if response:
                    print(f"์‘๋‹ต: {response.decode('utf-8')}")

                time.sleep(5)

        except KeyboardInterrupt:
            print("\n์—ฐ๊ฒฐ ์ข…๋ฃŒ")

if __name__ == "__main__":
    send_sensor_data()

2.4 UDP ์†Œ์ผ“

#!/usr/bin/env python3
"""UDP ์†Œ์ผ“ ํ†ต์‹  (๋น ๋ฅธ ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ „์†ก)"""

import socket
import json
import time

# === UDP ์„œ๋ฒ„ ===
def udp_server(port: int = 9998):
    """UDP ์„œ๋ฒ„"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(('0.0.0.0', port))

    print(f"UDP ์„œ๋ฒ„ ์‹œ์ž‘: ํฌํŠธ {port}")

    while True:
        data, addr = sock.recvfrom(1024)
        message = json.loads(data.decode('utf-8'))
        print(f"[{addr}] {message}")

# === UDP ํด๋ผ์ด์–ธํŠธ ===
def udp_client(server_ip: str, port: int = 9998):
    """UDP ํด๋ผ์ด์–ธํŠธ"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    sensor_data = {
        "sensor_id": "motion_01",
        "motion_detected": True,
        "timestamp": time.time()
    }

    message = json.dumps(sensor_data).encode('utf-8')
    sock.sendto(message, (server_ip, port))
    print(f"์ „์†ก ์™„๋ฃŒ: {sensor_data}")
    sock.close()

if __name__ == "__main__":
    import sys

    if len(sys.argv) > 1 and sys.argv[1] == 'server':
        udp_server()
    else:
        udp_client('192.168.1.100')

3. ESP32 WiFi ๊ฐœ์š”

3.1 ESP32์™€ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ๋น„๊ต

ํŠน์„ฑ ESP32 Raspberry Pi
ํ”„๋กœ์„ธ์„œ Xtensa 240MHz ARM 1.5GHz
RAM 520KB 1-8GB
OS FreeRTOS/์—†์Œ Linux
์–ธ์–ด C/C++, MicroPython Python, ๋ชจ๋“  ์–ธ์–ด
WiFi ๋‚ด์žฅ ๋‚ด์žฅ (Pi 3+)
์ „๋ ฅ ๋‚ฎ์Œ (80mA) ๋†’์Œ (700mA+)
์šฉ๋„ ์„ผ์„œ ๋…ธ๋“œ ๊ฒŒ์ดํŠธ์›จ์ด, ์—ฃ์ง€

3.2 ESP32 MicroPython WiFi ์˜ˆ์ œ

# ESP32์šฉ MicroPython ์ฝ”๋“œ
# (์ฐธ๊ณ ์šฉ - ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์—์„œ๋Š” ์‹คํ–‰ ๋ถˆ๊ฐ€)

import network
import time

def connect_wifi(ssid: str, password: str) -> str:
    """ESP32 WiFi ์—ฐ๊ฒฐ"""
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)

    if not wlan.isconnected():
        print(f'WiFi ์—ฐ๊ฒฐ ์ค‘: {ssid}')
        wlan.connect(ssid, password)

        # ์—ฐ๊ฒฐ ๋Œ€๊ธฐ
        timeout = 10
        while not wlan.isconnected() and timeout > 0:
            time.sleep(1)
            timeout -= 1

    if wlan.isconnected():
        ip = wlan.ifconfig()[0]
        print(f'์—ฐ๊ฒฐ๋จ! IP: {ip}')
        return ip
    else:
        print('์—ฐ๊ฒฐ ์‹คํŒจ')
        return None

# ์‚ฌ์šฉ
# ip = connect_wifi("MySSID", "MyPassword")

3.3 ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด - ESP32 ํ†ต์‹  ์•„ํ‚คํ…์ฒ˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด - ESP32 ํ†ต์‹                        โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                              โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         WiFi         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚   โ”‚              โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚              โ”‚    โ”‚
โ”‚   โ”‚  Raspberry   โ”‚                      โ”‚    ESP32     โ”‚    โ”‚
โ”‚   โ”‚     Pi       โ”‚                      โ”‚   ์„ผ์„œ ๋…ธ๋“œ  โ”‚    โ”‚
โ”‚   โ”‚              โ”‚                      โ”‚              โ”‚    โ”‚
โ”‚   โ”‚  - MQTT      โ”‚       TCP/UDP        โ”‚  - ์˜จ๋„ ์„ผ์„œ โ”‚    โ”‚
โ”‚   โ”‚    Broker    โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  - ์Šต๋„ ์„ผ์„œ โ”‚    โ”‚
โ”‚   โ”‚  - ๋ฐ์ดํ„ฐ    โ”‚                      โ”‚  - ๋ชจ์…˜ ์„ผ์„œ โ”‚    โ”‚
โ”‚   โ”‚    ์ˆ˜์ง‘      โ”‚       HTTP           โ”‚              โ”‚    โ”‚
โ”‚   โ”‚  - ๋ถ„์„      โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  ์ €์ „๋ ฅ      โ”‚    โ”‚
โ”‚   โ”‚              โ”‚                      โ”‚  ๋™์ž‘        โ”‚    โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚        โ”‚                                      โ”‚             โ”‚
โ”‚        โ”‚                                      โ”‚             โ”‚
โ”‚        โ–ผ                                      โ–ผ             โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚   โ”‚   ํด๋ผ์šฐ๋“œ   โ”‚                      โ”‚   ๋ฐฐํ„ฐ๋ฆฌ     โ”‚    โ”‚
โ”‚   โ”‚   AWS/GCP    โ”‚                      โ”‚   ๋™์ž‘ ๊ฐ€๋Šฅ  โ”‚    โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

4. ๋„คํŠธ์›Œํฌ ์Šค์บ” ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง

4.1 ๋„คํŠธ์›Œํฌ ์žฅ์น˜ ์Šค์บ”

#!/usr/bin/env python3
"""๋„คํŠธ์›Œํฌ ์žฅ์น˜ ์Šค์บ”"""

import subprocess
import re
from concurrent.futures import ThreadPoolExecutor
import socket

def get_local_network() -> str:
    """๋กœ์ปฌ ๋„คํŠธ์›Œํฌ ์ฃผ์†Œ ๋ฐ˜ํ™˜"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()

    # ๋„คํŠธ์›Œํฌ ์ฃผ์†Œ ์ถ”์ถœ (์˜ˆ: 192.168.1.0/24)
    parts = ip.split('.')
    return f"{parts[0]}.{parts[1]}.{parts[2]}.0/24"

def ping_host(ip: str) -> dict | None:
    """๋‹จ์ผ ํ˜ธ์ŠคํŠธ ํ•‘"""
    try:
        result = subprocess.run(
            ['ping', '-c', '1', '-W', '1', ip],
            capture_output=True,
            text=True,
            timeout=2
        )
        if result.returncode == 0:
            return {'ip': ip, 'status': 'up'}
    except:
        pass
    return None

def scan_network(network: str = None) -> list:
    """๋„คํŠธ์›Œํฌ ์ „์ฒด ์Šค์บ”"""
    if network is None:
        network = get_local_network()

    # IP ๋ฒ”์œ„ ์ƒ์„ฑ
    base = '.'.join(network.split('.')[:-1])
    ips = [f"{base}.{i}" for i in range(1, 255)]

    print(f"์Šค์บ” ์ค‘: {network}")

    results = []
    with ThreadPoolExecutor(max_workers=50) as executor:
        for result in executor.map(ping_host, ips):
            if result:
                results.append(result)
                print(f"  ๋ฐœ๊ฒฌ: {result['ip']}")

    return results

def get_hostname(ip: str) -> str:
    """IP ์ฃผ์†Œ์—์„œ ํ˜ธ์ŠคํŠธ๋ช… ์กฐํšŒ"""
    try:
        return socket.gethostbyaddr(ip)[0]
    except:
        return "Unknown"

if __name__ == "__main__":
    devices = scan_network()
    print(f"\n=== ๋ฐœ๊ฒฌ๋œ ์žฅ์น˜: {len(devices)}๊ฐœ ===")

    for device in devices:
        hostname = get_hostname(device['ip'])
        print(f"  {device['ip']:15} - {hostname}")

4.2 ํฌํŠธ ์Šค์บ”

#!/usr/bin/env python3
"""๊ฐ„๋‹จํ•œ ํฌํŠธ ์Šค์บ”"""

import socket
from concurrent.futures import ThreadPoolExecutor

COMMON_PORTS = {
    22: 'SSH',
    80: 'HTTP',
    443: 'HTTPS',
    1883: 'MQTT',
    3306: 'MySQL',
    5432: 'PostgreSQL',
    8080: 'HTTP-Alt',
    8883: 'MQTT-TLS'
}

def check_port(target: str, port: int) -> dict | None:
    """ํฌํŠธ ์—ด๋ฆผ ํ™•์ธ"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)

    try:
        result = sock.connect_ex((target, port))
        if result == 0:
            return {
                'port': port,
                'status': 'open',
                'service': COMMON_PORTS.get(port, 'unknown')
            }
    except:
        pass
    finally:
        sock.close()

    return None

def scan_ports(target: str, ports: list = None) -> list:
    """์—ฌ๋Ÿฌ ํฌํŠธ ์Šค์บ”"""
    if ports is None:
        ports = list(COMMON_PORTS.keys())

    print(f"ํฌํŠธ ์Šค์บ”: {target}")

    results = []
    with ThreadPoolExecutor(max_workers=20) as executor:
        futures = {executor.submit(check_port, target, port): port for port in ports}
        for future in futures:
            result = future.result()
            if result:
                results.append(result)
                print(f"  ํฌํŠธ {result['port']} ({result['service']}): OPEN")

    return results

if __name__ == "__main__":
    target = input("์Šค์บ”ํ•  IP ์ฃผ์†Œ: ")
    scan_ports(target)

5. HTTP ํด๋ผ์ด์–ธํŠธ

5.1 requests ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

#!/usr/bin/env python3
"""HTTP ํด๋ผ์ด์–ธํŠธ - ์„ผ์„œ ๋ฐ์ดํ„ฐ ์ „์†ก"""

import requests
import time
import json

API_BASE = "http://192.168.1.100:5000/api"

def send_sensor_data(sensor_id: str, data: dict) -> bool:
    """์„ผ์„œ ๋ฐ์ดํ„ฐ POST ์ „์†ก"""
    url = f"{API_BASE}/sensors/{sensor_id}/data"

    try:
        response = requests.post(
            url,
            json=data,
            headers={"Content-Type": "application/json"},
            timeout=5
        )

        if response.status_code == 201:
            print(f"๋ฐ์ดํ„ฐ ์ „์†ก ์„ฑ๊ณต: {data}")
            return True
        else:
            print(f"์ „์†ก ์‹คํŒจ: {response.status_code} - {response.text}")
            return False

    except requests.exceptions.RequestException as e:
        print(f"๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜: {e}")
        return False

def get_sensor_config(sensor_id: str) -> dict | None:
    """์„ผ์„œ ์„ค์ • ์กฐํšŒ"""
    url = f"{API_BASE}/sensors/{sensor_id}/config"

    try:
        response = requests.get(url, timeout=5)
        if response.status_code == 200:
            return response.json()
    except requests.exceptions.RequestException as e:
        print(f"์กฐํšŒ ์‹คํŒจ: {e}")

    return None

def periodic_reporting(sensor_id: str, interval: int = 10):
    """์ฃผ๊ธฐ์  ๋ฐ์ดํ„ฐ ๋ฆฌํฌํŒ…"""
    import random

    print(f"์„ผ์„œ {sensor_id} ๋ฆฌํฌํŒ… ์‹œ์ž‘ (๊ฐ„๊ฒฉ: {interval}์ดˆ)")

    while True:
        data = {
            "temperature": round(random.uniform(20, 30), 1),
            "humidity": round(random.uniform(40, 70), 1),
            "timestamp": int(time.time())
        }

        send_sensor_data(sensor_id, data)
        time.sleep(interval)

if __name__ == "__main__":
    periodic_reporting("sensor_001", 10)

5.2 ๋น„๋™๊ธฐ HTTP ํด๋ผ์ด์–ธํŠธ

#!/usr/bin/env python3
"""๋น„๋™๊ธฐ HTTP ํด๋ผ์ด์–ธํŠธ (aiohttp)"""

import asyncio
import aiohttp
import time

API_BASE = "http://192.168.1.100:5000/api"

async def send_data_async(session: aiohttp.ClientSession,
                          sensor_id: str,
                          data: dict) -> bool:
    """๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์ „์†ก"""
    url = f"{API_BASE}/sensors/{sensor_id}/data"

    try:
        async with session.post(url, json=data) as response:
            if response.status == 201:
                return True
    except aiohttp.ClientError as e:
        print(f"์˜ค๋ฅ˜: {e}")

    return False

async def batch_send(sensors: list, data_list: list):
    """์—ฌ๋Ÿฌ ์„ผ์„œ ๋ฐ์ดํ„ฐ ๋™์‹œ ์ „์†ก"""
    async with aiohttp.ClientSession() as session:
        tasks = [
            send_data_async(session, sensor, data)
            for sensor, data in zip(sensors, data_list)
        ]

        results = await asyncio.gather(*tasks)
        success = sum(results)
        print(f"์ „์†ก ์™„๋ฃŒ: {success}/{len(results)}")

if __name__ == "__main__":
    sensors = ["sensor_001", "sensor_002", "sensor_003"]
    data_list = [
        {"temperature": 25.5, "timestamp": time.time()},
        {"temperature": 26.0, "timestamp": time.time()},
        {"temperature": 24.8, "timestamp": time.time()}
    ]

    asyncio.run(batch_send(sensors, data_list))

5.3 HTTP ํด๋ผ์ด์–ธํŠธ with ์žฌ์‹œ๋„

#!/usr/bin/env python3
"""์žฌ์‹œ๋„ ๋กœ์ง์ด ์žˆ๋Š” HTTP ํด๋ผ์ด์–ธํŠธ"""

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import time

def create_session_with_retry(
    retries: int = 3,
    backoff_factor: float = 0.5,
    status_forcelist: tuple = (500, 502, 503, 504)
) -> requests.Session:
    """์žฌ์‹œ๋„ ์„ค์ •์ด ๋œ ์„ธ์…˜ ์ƒ์„ฑ"""
    session = requests.Session()

    retry = Retry(
        total=retries,
        read=retries,
        connect=retries,
        backoff_factor=backoff_factor,
        status_forcelist=status_forcelist
    )

    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)

    return session

class IoTHttpClient:
    """IoT์šฉ HTTP ํด๋ผ์ด์–ธํŠธ"""

    def __init__(self, base_url: str):
        self.base_url = base_url.rstrip('/')
        self.session = create_session_with_retry()
        self.session.headers.update({
            'Content-Type': 'application/json',
            'User-Agent': 'IoT-Sensor/1.0'
        })

    def send_data(self, endpoint: str, data: dict) -> dict:
        """๋ฐ์ดํ„ฐ ์ „์†ก"""
        url = f"{self.base_url}/{endpoint}"

        try:
            response = self.session.post(url, json=data, timeout=10)
            response.raise_for_status()
            return {"success": True, "data": response.json()}

        except requests.exceptions.RequestException as e:
            return {"success": False, "error": str(e)}

    def get_config(self, endpoint: str) -> dict:
        """์„ค์ • ์กฐํšŒ"""
        url = f"{self.base_url}/{endpoint}"

        try:
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
            return {"success": True, "data": response.json()}

        except requests.exceptions.RequestException as e:
            return {"success": False, "error": str(e)}

    def close(self):
        """์„ธ์…˜ ์ข…๋ฃŒ"""
        self.session.close()

# ์‚ฌ์šฉ ์˜ˆ
if __name__ == "__main__":
    client = IoTHttpClient("http://192.168.1.100:5000/api")

    result = client.send_data("sensors/001/data", {
        "temperature": 25.5,
        "timestamp": time.time()
    })

    print(result)
    client.close()

์—ฐ์Šต ๋ฌธ์ œ

๋ฌธ์ œ 1: WiFi ๋ชจ๋‹ˆํ„ฐ๋ง

  1. ํ˜„์žฌ WiFi ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
  2. ์‹ ํ˜ธ ๊ฐ•๋„๊ฐ€ -70dBm ์ดํ•˜๋กœ ๋–จ์–ด์ง€๋ฉด ๊ฒฝ๊ณ ๋ฅผ ์ถœ๋ ฅํ•˜์„ธ์š”.

๋ฌธ์ œ 2: ๋กœ์ปฌ ์„œ๋ฒ„

  1. TCP ์„œ๋ฒ„๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์„ผ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜์„ธ์š”.
  2. ์ˆ˜์‹ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ผ์— ์ €์žฅํ•˜์„ธ์š”.

๋ฌธ์ œ 3: HTTP ๋ฆฌํฌํ„ฐ

  1. ์˜จ๋„ ์„ผ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ HTTP POSTํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
  2. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ์‹œ ์žฌ์‹œ๋„ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.

๋‹ค์Œ ๋‹จ๊ณ„

  • 05_BLE_Connectivity.md: BLE ํ†ต์‹ ์œผ๋กœ ์ €์ „๋ ฅ ์„ผ์„œ ์—ฐ๊ฒฐ
  • 06_MQTT_Protocol.md: MQTT๋กœ ํšจ์œจ์ ์ธ IoT ๋ฉ”์‹œ์ง•

์ตœ์ข… ์—…๋ฐ์ดํŠธ: 2026-02-01

to navigate between lessons