# ๐Ÿ”’ MAM ๊ฐœ์ธ ์ „์šฉ ๋ธŒ๋กœ์ปค(Private Broker) ๊ตฌ์ถ• ๋ฐ ์—ฐ๋™ ๊ฐ€์ด๋“œ (`PRIVATE_SERVER.md`) - **์ž‘์„ฑ์ผ**: 2026-08-20 (Rev.2) - **๋ฌธ์„œ ๋ชฉ์ **: MAM(Multi-Agent Mux)์˜ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ์˜์กด์„ฑ ๋ฐ ๋ณด์•ˆ ๊ฒฐํ•จ(A-2)์„ ํ•ด์†Œํ•˜๊ธฐ ์œ„ํ•œ ๊ฐœ์ธ ์ „์šฉ ๋ธŒ๋กœ์ปค(NATS / Mosquitto) ๊ตฌ์ถ•, ์šด์˜, ๋‹ค๋Šฅ์„ฑ ํ™œ์šฉ ๋ฐ MAM ํด๋ผ์ด์–ธํŠธ ์—ฐ๋™ ๊ฐ€์ด๋“œ. - **์—ฐ๊ณ„ ๋ฌธ์„œ**: [`NATS_REPORT.md`](NATS_REPORT.md), [`implementation_plan.md`](implementation_plan.md), [`IMPROVEMENTS.md`](IMPROVEMENTS.md) --- ## 1. ๊ฐœ์š” ๋ฐ ๋„์ž… ๋ฐฐ๊ฒฝ ํ˜„์žฌ MAM ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ธฐ๋ณธ ๋ฉ”์‹œ์ง• ๋ธŒ๋กœ์ปค๋Š” ๊ณต๊ฐœ ์„œ๋ฒ„(`broker.hivemq.com:1883`)๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ์ธ ์ „์šฉ ๋ธŒ๋กœ์ปค(Private Broker)๋ฅผ ๊ตฌ์ถ•ํ•˜์—ฌ ์—ฐ๊ฒฐํ•˜๋ฉด **ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์—†์ด(0์ค„ ๋ณ€๊ฒฝ)** ๋ณด์•ˆ ์œ„ํ—˜์„ ์›์ฒœ ์ฐจ๋‹จํ•˜๊ณ  ๋„คํŠธ์›Œํฌ ์•ˆ์ •์„ฑ์„ ๋Œ€ํญ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ``` [MAM Orchestrator / Agents] โ”‚ โ–ผ (MQTT 3.1.1 / TLS) [Private Dedicated Broker] โ”€โ”€โ”€> ์‚ฌ์„ค๋ง/๊ฐœ์ธ ์„œ๋ฒ„ (NATS Server / Mosquitto) โ€ข ์™ธ๋ถ€ ๋ถˆ๋ฒ• ํŠธ๋ž˜ํ”ฝ 100% ์ฐจ๋‹จ (A-2 ๋ณด์•ˆ ํ•ด์†Œ) โ€ข JetStream ์˜์†์„ฑ ๋ฐ NKey/JWT ACL ์ง€์› โ€ข ์ดˆ์ €์ง€์—ฐ (<1ms) ๋ฐ ๋ฌด์ œํ•œ ๋Œ€์—ญํญ ``` MAM์˜ ์ œ์–ด ํ‰๋ฉด(`run_loop.sh`์˜ `wait_for_job` ํŒŒ์ผ์‹œ์Šคํ…œ ํด๋ง)์€ ๋ธŒ๋กœ์ปค์™€ 100% ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋ฏ€๋กœ, ๋ธŒ๋กœ์ปค๋Š” **๋น„๋™๊ธฐ ๊ด€์ธก(observability) ์‚ฌ์ด๋“œ์นด** ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. --- ## 2. ํ•ด๊ฒฐ ์˜์—ญ ๋งคํŠธ๋ฆญ์Šค (๋ธŒ๋กœ์ปค ์ „ํ™˜ vs ์ฝ”๋“œ ํŒจ์น˜) ์ „์šฉ ๋ธŒ๋กœ์ปค ๊ตฌ์ถ•์œผ๋กœ ์ฆ‰์‹œ ํ•ด๊ฒฐ๋˜๋Š” ์˜์—ญ๊ณผ, ๋กœ์ปฌ ์ฝ”๋“œ ํŒจ์น˜(Track 0)๊ฐ€ ๋ณ‘ํ–‰๋˜์–ด์•ผ ํ•˜๋Š” ์˜์—ญ์˜ ๋ช…ํ™•ํ•œ ๊ตฌ๋ถ„์ž…๋‹ˆ๋‹ค. | ๊ตฌ๋ถ„ | ๋‹น๋ฉด ๊ณผ์ œ | ๊ฐœ์ธ ๋ธŒ๋กœ์ปค ๊ตฌ์ถ• ์‹œ | ๋กœ์ปฌ ์ฝ”๋“œ ํŒจ์น˜ ํ•„์š” ์—ฌ๋ถ€ (Track 0) | |---|---|:---:|:---:| | **๋ณด์•ˆ (A-2)** | ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ๋…ธ์ถœ ๋ฐ ์™ธ๋ถ€ ์•…์˜์  ์ด๋ฒคํŠธ ์ˆ˜์‹  ์œ„ํ˜‘ | ๐ŸŸข **100% ์ฆ‰์‹œ ํ•ด์†Œ** (์‚ฌ์„ค๋ง/ACL ๊ฒฉ๋ฆฌ) | Track 2์—์„œ ํ† ํฐ ๋ฐœ๊ธ‰ ๊ฐ•์ œ | | **์•ˆ์ •์„ฑ** | ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค์˜ ์˜ˆ๊ณ  ์—†๋Š” ์ˆœ๋‹จ ๋ฐ ์†๋„ ์ œํ•œ(Rate-limit) | ๐ŸŸข **100% ์ฆ‰์‹œ ํ•ด์†Œ** (์ „์šฉ ๋ฆฌ์†Œ์Šค) | โ€” | | **๋‚ด๊ฒฐํ•จ์„ฑ (B-14)** | ๋ธŒ๋กœ์ปค ์ผ์‹œ ์žฅ์•  ์‹œ 65๋ถ„ ๋ฃจํ”„ ์ •์ง€(Hang) ๊ฒฐํ•จ | โš ๏ธ ๋ธŒ๋กœ์ปค ์ ๊ฒ€/์ˆœ๋‹จ ์‹œ ์—ฌ์ „ํžˆ ์œ„ํ—˜ | ๐Ÿ”ด **ํ•„์ˆ˜ (Track 0 Step 1 ์„ ํ–‰ ํŒจ์น˜)** | | **์ง€์—ฐ/์˜คํŒ (B-15)** | ๋ธŒ๋กœ์ปค ๋‹ค์šด ์‹œ 120์ดˆ ์ง€์—ฐ ๋ฐ ์ •์ƒ ์ž‘์—…์˜ ์—๋Ÿฌ ์˜คํŒ์ • | โš ๏ธ ๋ธŒ๋กœ์ปค ์ ๊ฒ€/์ˆœ๋‹จ ์‹œ ์—ฌ์ „ํžˆ ์œ„ํ—˜ | ๐Ÿ”ด **ํ•„์ˆ˜ (Track 0 Step 2 & 3 ์„ ํ–‰ ํŒจ์น˜)** | > [!IMPORTANT] > **๋ฐฉํƒ„ ์•„ํ‚คํ…์ฒ˜ ์›์น™:** > "Track 0(`B-14`, `B-15`) ํŒจ์น˜๋ฅผ ํ†ตํ•ด ๋ธŒ๋กœ์ปค๊ฐ€ ๋‹ค์šด๋˜์–ด๋„ ๋ฃจํ”„๊ฐ€ 100% ์ •์ƒ ์™„์ฃผํ•˜๋„๋ก ๋กœ์ปฌ ๋””์Šคํฌ ๋‚ด๊ฒฐํ•จ์„ฑ์„ ๋จผ์ € ํ™•๋ณดํ•˜๊ณ , ๊ฐœ์ธ ๋ธŒ๋กœ์ปค๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ A-2 ๋ณด์•ˆ๊ณผ ์„ฑ๋Šฅ์„ ์™„๊ฒฐํ•ฉ๋‹ˆ๋‹ค." --- ## 3. ์ „์šฉ ๋ธŒ๋กœ์ปค ์ถ”์ฒœ ๋ฐ ๋น„๊ต MAM ํด๋ผ์ด์–ธํŠธ๋Š” ํ‘œ์ค€ `paho-mqtt`๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, MQTT 3.1.1์„ ์ง€์›ํ•˜๋Š” ๋ชจ๋“  ๋ธŒ๋กœ์ปค์™€ 100% ํ˜ธํ™˜๋ฉ๋‹ˆ๋‹ค. | ๋น„๊ต ํ•ญ๋ชฉ | ๐Ÿ† `nats-server` (๊ฐ•๋ ฅ ๊ถŒ์žฅ) | `eclipse-mosquitto` (๋Œ€์•ˆ) | |---|---|---| | **์•„ํ‚คํ…์ฒ˜** | Go ๋‹จ์ผ ์ •์  ๋ฐ”์ด๋„ˆ๋ฆฌ (Zero Dependency) | C ๊ธฐ๋ฐ˜ ๊ฒฝ๋Ÿ‰ ์˜คํ”ˆ์†Œ์Šค ๋ธŒ๋กœ์ปค | | **์ฃผ์š” ํŠน์ง•** | โ€ข ๋‚ด์žฅ MQTT 3.1.1 ๋ฆฌ์Šค๋„ˆ (`mqtt { port: 1883 }`)
โ€ข JetStream ์—”์ง„ ๋‚ด์žฅ (์ด๋ฒคํŠธ ์˜์†ํ™” ๋ฐ ๋ณต๊ตฌ)
โ€ข NKey/JWT ๊ธฐ๋ฐ˜ ๊ณ„์ • ๋ฐ Subject๋ณ„ ์„ธ๋ถ„ํ™”๋œ ACL
โ€ข WebSocket ๋ฐ NATS ๋„ค์ดํ‹ฐ๋ธŒ ํ”„๋กœํ† ์ฝœ ๋™์‹œ ์„œ๋น™ | โ€ข ๊ฐ€์žฅ ๋„๋ฆฌ ์“ฐ์ด๋Š” ํ‘œ์ค€ ๊ฒฝ๋Ÿ‰ MQTT ๋ธŒ๋กœ์ปค
โ€ข ๋‚ฎ์€ ๋ฉ”๋ชจ๋ฆฌ ์ ์œ ์œจ (~10MB) | | **์ถ”์ฒœ ์šฉ๋„** | ๋ชจ๋˜ ์ธํ”„๋ผ, ํ™•์žฅ์„ฑ, ๊ฐ์‚ฌ ๋กœ๊ทธ ์˜์†ํ™”, ํ™ˆ๋žฉ ํ†ตํ•ฉ | ์ •ํ†ต ์ดˆ๊ฒฝ๋Ÿ‰ ์ž„๋ฒ ๋””๋“œ/๋‹จ์ผ ๋ชฉ์  ํ™˜๊ฒฝ | | **๋ฐฐํฌ ๋‚œ์ด๋„** | ๐ŸŸข ๋ฐ”์ด๋„ˆ๋ฆฌ 1๊ฐœ ์‹คํ–‰ ๋˜๋Š” Docker 1์ค„ | ๐ŸŸข ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € (`apt`, `brew`) ๋˜๋Š” Docker | --- ## 4. ๊ฐœ์ธ ์„œ๋ฒ„ ๋ธŒ๋กœ์ปค ๋ฐฐํฌ ๊ฐ€์ด๋“œ ### 4.1 `nats-server` ๋ฐฐํฌ (๊ถŒ์žฅ) `nats-server`์—์„œ MQTT๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด ์„ค์ • ํŒŒ์ผ(`nats.conf`)์— `mqtt { port: 1883 }` ๋ธ”๋ก๊ณผ `jetstream { }` ๋ธ”๋ก์ด ๋ฐ˜๋“œ์‹œ ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. > [!NOTE] > `nats-server`์˜ `-m` ํ”Œ๋ž˜๊ทธ๋Š” HTTP ๋ชจ๋‹ˆํ„ฐ๋ง ํฌํŠธ(`--http_port`)๋ฅผ ์ง€์ •ํ•˜๋Š” ์˜ต์…˜์ด๋ฉฐ, MQTT๋ฅผ ์ผœ๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. MQTT ํ™œ์„ฑํ™”๋Š” ๋ฐ˜๋“œ์‹œ `-c nats.conf` ์„ค์ • ํŒŒ์ผ์„ ํ†ตํ•ด ๊ตฌ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. #### 1) ๊ณตํ†ต ์„ค์ • ํŒŒ์ผ (`nats.conf`) ```conf # nats.conf server_name: mam-hub # JetStream ์˜์† ์Šคํ† ๋ฆฌ์ง€ (MQTT QoS 1 ๋ฐ Retained ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ์— ํ•„์ˆ˜) jetstream { store_dir: "~/.local/share/nats/data" # Docker ํ™˜๊ฒฝ์—์„œ๋Š” "/data"๋กœ ๋งคํ•‘ max_file: 10G # ํ™ˆ๋žฉ ๋””์Šคํฌ ์ƒํ•œ ์„ค์ • } # HTTP ๋ชจ๋‹ˆํ„ฐ๋ง ์—”๋“œํฌ์ธํŠธ (/varz, /jsz ๋Œ€์‹œ๋ณด๋“œ) http_port: 8222 # ํ‰๋ฉด A: MAM MQTT 3.1.1 ํ”„๋กœํ† ์ฝœ ๋ฆฌ์Šค๋„ˆ mqtt { port: 1883 } # ํ‰๋ฉด B: ํ™ˆ๋žฉ/์›น ๋ธŒ๋ผ์šฐ์ € ๋Œ€์‹œ๋ณด๋“œ์šฉ WebSocket ๋ฆฌ์Šค๋„ˆ (์„ ํƒ ์‚ฌํ•ญ) websocket { port: 8080 no_tls: true # ๋‚ด๋ถ€ ์‚ฌ์„ค๋ง ํ•œ์ • } ``` #### 2) ๋ฐฐํฌ ๋ฐฉ๋ฒ• A. Docker / Docker Compose (๊ถŒ์žฅ) **๋‹จ์ผ Docker ์‹คํ–‰:** ```bash # ํ˜ธ์ŠคํŠธ์— nats.conf ์ƒ์„ฑ ํ›„ ์‹คํ–‰ docker run -d \ --name mam-nats \ --restart unless-stopped \ -p 1883:1883 \ -p 4222:4222 \ -p 8222:8222 \ -p 8080:8080 \ -v ./nats.conf:/etc/nats/nats.conf:ro \ -v nats-data:/data \ nats:latest \ -c /etc/nats/nats.conf ``` **Docker Compose (`docker-compose.yml`):** ```yaml version: '3.8' services: nats: image: nats:latest container_name: mam-nats restart: unless-stopped command: ["-c", "/etc/nats/nats.conf"] ports: - "1883:1883" # MQTT 3.1.1 ํฌํŠธ (ํ‰๋ฉด A: MAM) - "4222:4222" # NATS ๊ธฐ๋ณธ ํฌํŠธ (ํ‰๋ฉด B) - "8222:8222" # HTTP ๋ชจ๋‹ˆํ„ฐ๋ง (/varz, /jsz) - "8080:8080" # WebSocket (ํ‰๋ฉด B) volumes: - ./nats.conf:/etc/nats/nats.conf:ro - nats-data:/data volumes: nats-data: ``` #### 3) ๋ฐฐํฌ ๋ฐฉ๋ฒ• B. ๋„ค์ดํ‹ฐ๋ธŒ ๋ฐ”์ด๋„ˆ๋ฆฌ ์„ค์น˜ (macOS / Linux โ€” ๋น„๋ฃจํŠธ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„) macOS์˜ sealed APFS ๋ฃจํŠธ ๋ณผ๋ฅจ(`/data`) ๊ถŒํ•œ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ(`~/.config/nats/`, `~/.local/share/nats/data`)๋ฅผ ๊ธฐ๋ณธ ์Šคํ† ๋ฆฌ์ง€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ```bash # ์„ค์ • ๋ฐ ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ (sudo ๋ถˆํ•„์š”) mkdir -p ~/.config/nats ~/.local/share/nats/data # ์„ค์ • ํŒŒ์ผ ์ž‘์„ฑ cat <<'EOF' > ~/.config/nats/nats.conf server_name: mam-hub jetstream { store_dir: "~/.local/share/nats/data" max_file: 10G } http_port: 8222 mqtt { port: 1883 } websocket { port: 8080 no_tls: true } EOF # macOS (Homebrew ์„ค์น˜ ๋ฐ ์‹คํ–‰) brew install nats-server nats-server -c ~/.config/nats/nats.conf & # Linux (x86_64 ๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ ์„ค์น˜ ๋ฐ ์‹คํ–‰) curl -L https://github.com/nats-io/nats-server/releases/download/v2.10.20/nats-server-v2.10.20-linux-amd64.tar.gz | tar xz sudo mv nats-server-v2.10.20-linux-amd64/nats-server /usr/local/bin/ nats-server -c ~/.config/nats/nats.conf & ``` --- ### 4.2 `mosquitto` ๋ฐฐํฌ (๋Œ€์•ˆ) #### Docker ์‹คํ–‰: ```bash docker run -d \ --name mam-mosquitto \ --restart unless-stopped \ -p 1883:1883 \ -v ./mosquitto.conf:/mosquitto/config/mosquitto.conf \ eclipse-mosquitto:latest ``` **๊ธฐ๋ณธ `mosquitto.conf` ์„ค์ • ํŒŒ์ผ ์˜ˆ์‹œ:** ```conf listener 1883 allow_anonymous true persistence true persistence_location /mosquitto/data/ ``` --- ## 5. ํ•˜๋‚˜์˜ ์„œ๋ฒ„๋กœ ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ โ€” `nats-server` ๋‹ค๋Šฅ์„ฑ (Versatility) `nats-server`์˜ ๋‹ค๋Šฅ์„ฑ์€ **MAM์„ ๋„ค์ดํ‹ฐ๋ธŒ NATS๋กœ ์ด๊ด€ํ•  ์ด์œ ๊ฐ€ ์•„๋‹ˆ๋ผ, MAM ์ฝ”๋“œ๋ฅผ ํ•œ ์ค„๋„ ๋ฐ”๊พธ์ง€ ์•Š๊ณ ๋„ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ๋ถ€๊ฐ€์  ์ด๋“**์ž…๋‹ˆ๋‹ค. ### 5.1 ๋‘ ๊ฐœ์˜ ์†Œ๋น„ ํ‰๋ฉด (Two Consumption Planes) `nats-server`๋Š” ๋‹จ์ผ ํ”„๋กœ์„ธ์Šค ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ํ”„๋กœํ† ์ฝœ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋™์‹œ์— ๊ตฌ๋™ํ•˜๋ฏ€๋กœ, MAM์˜ ๋‹จ์ˆœ์„ฑ๊ณผ ๊ฐœ์ธ ํ™ˆ๋žฉ์˜ ํ™•์žฅ์„ฑ์„ ์™„๋ฒฝํžˆ ์–‘๋ฆฝ์‹œํ‚ต๋‹ˆ๋‹ค. ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ nats-server (๋‹จ์ผ ์ธ์Šคํ„ด์Šค) โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ ํ‰๋ฉด A: MAM ์›Œํฌ๋กœ๋“œ โ”‚ ํ‰๋ฉด B: ํ™ˆ๋žฉ/๊ฐœ์ธ ํ”„๋กœ์ ํŠธ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค ํ”„๋กœํ† ์ฝœ โ”‚ MQTT 3.1.1 (ํฌํŠธ 1883) โ”‚ NATS(4222), WebSocket(8080) โ”‚ ํด๋ผ์ด์–ธํŠธ โ”‚ paho-mqtt (์ฝ”๋“œ ๋ณ€๊ฒฝ 0์ค„) โ”‚ nats-py, nats.js, CLI ๋“ฑ ์ž์œ  โ”‚ ์‚ฌ์šฉ ๊ธฐ๋Šฅ โ”‚ QoS 1, Retain, ์™€์ผ๋“œ์นด๋“œ, TLSโ”‚ JetStream ๋ฆฌํ”Œ๋ ˆ์ด, KV, Objectโ”‚ ์„ค๊ณ„ ์›์น™ โ”‚ ์ดˆ๊ฒฝ๋Ÿ‰ ๋™๊ธฐ CLI ํ•ซํŒจ์Šค ๋ณด์กด โ”‚ ๊ณ ๊ธ‰ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆฌ๋ฐ โ”‚ ๊ณต์œ  ์ž์› โ”‚ โ””โ”€โ”€โ”€โ”€โ”€ ๋‹จ์ผ ์ •์  ๋ฐ”์ด๋„ˆ๋ฆฌ / JetStream ์Šคํ† ๋ฆฌ์ง€ / ACL โ”€โ”€โ”€โ”€โ”€โ”˜โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### 5.2 ๊ต์ฐจ ํ”„๋กœํ† ์ฝœ ๋ธŒ๋ฆฌ์ง• (Cross-Protocol Bridging) - `nats-server`๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ MQTT ํ† ํ”ฝ(`/`)์„ NATS Subject(`.`)๋กœ ์‹ค์‹œ๊ฐ„ ์ž๋™ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. - MAM ์—์ด์ „ํŠธ๊ฐ€ MQTT ํ† ํ”ฝ `python/mqtt/jobs//events`๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋ฉด, ์›น ๋ธŒ๋ผ์šฐ์ €๋‚˜ ํƒ€ ํ”„๋กœ์ ํŠธ์˜ NATS ๊ตฌ๋…์ž๋Š” NATS Subject `python.mqtt.jobs..events` ๋˜๋Š” `python.mqtt.jobs.*.events`๋กœ ์ฆ‰์‹œ ์‹ค์‹œ๊ฐ„ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **์‹ค์šฉ์  ์ด์ **: MAM ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋‹จ 1์ค„๋„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ React/Vue ์›น ๋Œ€์‹œ๋ณด๋“œ๋‚˜ ํ„ฐ๋ฏธ๋„ ๋ชจ๋‹ˆํ„ฐ๋ง ํˆด์„ ์ฆ‰์‹œ ๋ถ€์ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **์ฃผ์˜ ์‚ฌํ•ญ**: ํ† ํ”ฝ ๋ ˆ๋ฒจ ๋‚ด์— ๋งˆ์นจํ‘œ(`.`)๊ฐ€ ํฌํ•จ๋˜๋ฉด NATS ๊ณ„์ธต์—์„œ ํ† ํฐ์ด ๋ถ„๋ฆฌ๋  ์ˆ˜ ์žˆ์œผ๋‚˜, MAM์˜ `job_id`๋Š” 8์ž๋ฆฌ hex, ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ง€๋ฌธ์€ 12์ž๋ฆฌ hex์ด๋ฏ€๋กœ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค. ### 5.3 JetStream ์ด๋ฒคํŠธ ๋ฆฌํ”Œ๋ ˆ์ด (Event Replay) - `python.mqtt.jobs.>` Subject๋ฅผ ๊ตฌ๋…ํ•˜๋Š” JetStream ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•˜๋ฉด, ์ง€๋‚œ ์ž‘์—…์˜ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ ์ „์ฒด๋ฅผ ์‹œ์  ์ง€์ •(Time-based) ๋˜๋Š” ์‹œํ€€์Šค ์ง€์ •(Sequence-based)์œผ๋กœ ์‚ฌํ›„ ๋ฆฌํ”Œ๋ ˆ์ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **์ฃผ์˜ ์‚ฌํ•ญ**: ์ด ๊ธฐ๋Šฅ์€ ์˜ตํŠธ์ธ(Opt-in)์ด๋ฉฐ, MQTT QoS 1 ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋‚ด๋ถ€ ์‹œ์Šคํ…œ ์ŠคํŠธ๋ฆผ(`$MQTT_*`)๊ณผ ๋ณ„๊ฐœ๋กœ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ๋””์Šคํฌ ์šฉ๋Ÿ‰ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด `max_age`๋‚˜ `max_bytes` ์ƒํ•œ์„ ๋ฐ˜๋“œ์‹œ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ### 5.4 ๋‚ด์žฅ Key-Value (KV) ๋ฐ Object Store - ํ™ˆ๋žฉ ๋ฐ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์—์„œ Redis๋‚˜ MinIO ๊ฐ™์€ ๋ณ„๋„ ์ธํ”„๋ผ๋ฅผ ๋„์šฐ์ง€ ์•Š๊ณ ๋„ `nats-server` ๋‚ด์žฅ KV ๋ฐ Object Store๋ฅผ ์ฆ‰์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **๊ธˆ์ง€ ์‚ฌํ•ญ (Non-Goal)**: MAM์˜ ๋กœ์ปฌ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ(`.mam/jobs/*.json`)๋ฅผ JetStream KV๋กœ ๋Œ€์ฒดํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค (`wait_for_job`์˜ fcntl ๋ฐ ํŒŒ์ผ์‹œ์Šคํ…œ ํด๋ง ๊ณ„์•ฝ ์œ ์ง€). ### 5.5 ๋ฉ€ํ‹ฐํ…Œ๋„ŒํŠธ ๊ณ„์ • ๋ถ„๋ฆฌ ๋ฐ ๋ณด์•ˆ - ๋‹จ์ผ ์„œ๋ฒ„ ๋‚ด์—์„œ `MAM` ์ „์šฉ ๊ณ„์ •๊ณผ `HOME` ๊ฐœ์ธ ๊ณ„์ •์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ฆฌ์†Œ์Šค ์ฟผํ„ฐ์™€ ๊ถŒํ•œ์„ ์™„๋ฒฝํžˆ ๊ฒฉ๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **๊ถŒ๊ณ  ๋ฐฐ์น˜**: MAM๊ณผ ์ด๋ฅผ ๊ด€์ธกํ•˜๋Š” ๋Œ€์‹œ๋ณด๋“œ๋Š” ๋™์ผํ•œ ๊ณ„์ •(`MAM`)์— ๋ฐฐ์น˜ํ•˜๊ณ , ๋ฌด๊ด€ํ•œ ํ™ˆ๋žฉ ์„œ๋น„์Šค๋Š” ๋ณ„๋„ ๊ณ„์ •(`HOME`)์— ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. --- ## 6. MAM ํด๋ผ์ด์–ธํŠธ ์—ฐ๋™ ์„ค์ • (`.mam.env`) ๊ฐœ์ธ ์„œ๋ฒ„ ๋ธŒ๋กœ์ปค๊ฐ€ ๊ตฌ๋™๋˜๋ฉด, MAM ์ €์žฅ์†Œ ๋ฃจํŠธ์˜ [`.mam.env`](file:///.mam.env) ํŒŒ์ผ์— ๊ฐœ์ธ ์„œ๋ฒ„ ์ฃผ์†Œ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. > [!NOTE] > MAM ์ฝ”๋“œ(`mqtt_common.py`)๋Š” `MQTT_*` ์ ‘๋‘์‚ฌ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋น„๊ณต์‹ ๋ฌธ์„œ์˜ `MAM_MQTT_*` ๋ณ€์ˆ˜๋Š” ๋ฌดํšจํ•˜๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ ์•„๋ž˜์˜ ํ‘œ์ค€ ๋ณ€์ˆ˜๋ช…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ```bash # ============================================================================== # MAM Private MQTT Broker Configuration (.mam.env) # ============================================================================== # ๊ฐœ์ธ ์„œ๋ฒ„ IP ๋˜๋Š” ๋„๋ฉ”์ธ MQTT_BROKER="192.168.1.100" # ์˜ˆ: 10.0.0.5, mqtt.my-domain.com ๋“ฑ # MQTT ๊ธฐ๋ณธ ํฌํŠธ (ํ‰๋ฌธ TCP: 1883, TLS ์•”ํ˜ธํ™”: 8883) MQTT_PORT=1883 # TLS ์•”ํ˜ธํ™” ํ™œ์„ฑํ™” ์—ฌ๋ถ€ (0: ํ‰๋ฌธ TCP, 1: TLS ์•”ํ˜ธํ™”) MQTT_TLS=0 # ์ธ์ฆ ์„ค์ • (์ต๋ช… ๋ธŒ๋กœ์ปค๋Š” ์ฃผ์„ ์ฒ˜๋ฆฌ ๋˜๋Š” ๋นˆ ๋ฌธ์ž์—ด ์œ ์ง€) # MQTT_USERNAME=my_agent_user # MQTT_PASSWORD=my_secure_password # TLS ์ธ์ฆ์„œ ๊ฒฝ๋กœ (MQTT_TLS=1 ์„ค์ • ์‹œ ์‚ฌ์šฉ) # MQTT_CA_CERTS=/path/to/ca.crt # MQTT_CERTFILE=/path/to/client.crt # MQTT_KEYFILE=/path/to/client.key ``` *์ฐธ๊ณ : OS ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ๋™์ผํ•œ ์ด๋ฆ„์ด ์ด๋ฏธ `export`๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ OS ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ `.mam.env` ํŒŒ์ผ ์„ค์ •๋ณด๋‹ค ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.* --- ## 7. ์—ฐ๋™ ๋ฐ ๋™์ž‘ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ (4-Step Verification) ๊ฐœ์ธ ์„œ๋ฒ„ ๋ธŒ๋กœ์ปค์™€์˜ ์—ฐ๋™ ์ƒํƒœ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๊ฒ€์ฆํ•˜๋Š” 4๋‹จ๊ณ„ ์ ˆ์ฐจ์ž…๋‹ˆ๋‹ค. ### Step 1. ๋ธŒ๋กœ์ปค ๋ฆฌ์Šค๋„ˆ ๋ฐ JetStream ์ƒํƒœ ํ™•์ธ ```bash # MQTT ๋ฆฌ์Šค๋„ˆ ํ™œ์„ฑํ™” ํ™•์ธ curl -s http://192.168.1.100:8222/varz | grep -i mqtt # JetStream ์—”์ง„ ์ •์ƒ ๊ตฌ๋™ ํ™•์ธ curl -s http://192.168.1.100:8222/jsz ``` ### Step 2. ์ž„์‹œ ์žก ๋“ฑ๋ก ๋ฐ ์—ฐ๊ฒฐ ๊ฒ€์ฆ ์ด๋ฒคํŠธ ๋ฐœํ–‰ `publish_event.py`๋Š” ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋ก๋œ ์žก์— ๋Œ€ํ•ด์„œ๋งŒ ๋ฐœํ–‰์„ ์ˆ˜ํ–‰ํ•˜๋ฏ€๋กœ, ์ž„์‹œ ์žก์„ ๋“ฑ๋กํ•˜๊ณ  ๋ฐœํ–‰ํ•œ ํ›„ ์™„๋ฃŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ```bash # 1) ์ž„์‹œ ์žก ๋“ฑ๋ก (์ž๋™ ์ฑ„๋ฒˆ๋œ JID ์บก์ฒ˜) JID=$(.venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/registry.py \ --registry-dir .mam/jobs \ register \ --prompt "Private broker connectivity test" \ --agent-session "herdr:test") echo "registered test job: $JID" # 2) ์ด๋ฒคํŠธ ๋ฐœํ–‰ (์ƒ์„ธ ๋กœ๊ทธ ์ถœ๋ ฅ ๋ฐ rc=0 ๋‹จ์–ธ) .venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/publish_event.py \ --registry-dir .mam/jobs \ --job "$JID" \ --event progress \ --detail "Private broker connection verified" -v # 3) ํ…Œ์ŠคํŠธ ์žก ์ข…๊ฒฐ ์ฒ˜๋ฆฌ (๋ฏธ์ข…๊ฒฐ ์‹œ --wait-any ์œ ๋ น ์žก ์ž”์กด ๋ฐฉ์ง€) .venv/bin/python .agents/skills/multi-agent-mux-delegate-job/scripts/registry.py \ --registry-dir .mam/jobs status --job "$JID" --set completed ``` ### Step 3. ์ ‘์† ๋Œ€์ƒ ๋ธŒ๋กœ์ปค IP ๋‹จ์–ธ Step 2์˜ `-v` ์ถœ๋ ฅ ๋กœ๊ทธ ๋˜๋Š” `.mam/delegate_job_logs/$JID/events.ndjson` ํŒŒ์ผ์—์„œ ์‹ค์ œ ์ ‘์† ํ˜ธ์ŠคํŠธ๊ฐ€ ๊ฐœ์ธ ๋ธŒ๋กœ์ปค IP๋กœ ๋‚˜ํƒ€๋‚˜๊ณ  `broker.hivemq.com`์ด ํฌํ•จ๋˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ### Step 4. ๋‹จ์œ„ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ๊ฒ€์ฆ ```bash .venv/bin/python -m pytest tests/ -q ``` *์ฐธ๊ณ : MAM์˜ ๊ธฐ๋ณธ ๋‹จ์œ„/์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋Š” ๋ชจ์˜(Mock) ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ธŒ๋กœ์ปค ์—ฐ๊ฒฐ ์—ฌ๋ถ€์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ 100% ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ๋„คํŠธ์›Œํฌ ์—ฐ๋™ ๊ฒ€์ฆ์€ Step 1~3์ด ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.* --- ## 8. ๊ถŒ์žฅ ์‹คํ–‰ ์ˆœ์„œ ``` [Phase 1: ๋‚ด๊ฒฐํ•จ์„ฑ ํ™•๋ณด] โ”€โ”€> [Phase 2: ๊ฐœ์ธ ๋ธŒ๋กœ์ปค ๊ฐ€๋™] โ”€โ”€> [Phase 3: A-2 ๋ณด์•ˆ ์™„์ „ ์ข…๊ฒฐ] Track 0 (B-14, B-15) nats-server (nats.conf) ์ง€๋ฌธ ํ† ํ”ฝ ๋ฐ ์ธ์ฆ ํ† ํฐ ๋ฐœ๊ธ‰ ๋กœ์ปฌ ๋””์Šคํฌ ํด๋ฐฑ ํŒจ์น˜ .mam.env ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์—ฐ๋™ ์™ธ๋ถ€ ๊ฐ„์„ญ 100% ์ฐจ๋‹จ ``` 1. **Phase 1 (Track 0 ์„ ํ–‰ ํŒจ์น˜)**: `publish_event.py`์™€ `job_subscriber.py`์˜ ๋กœ์ปฌ ๋””์Šคํฌ ํด๋ฐฑ(`B-14`, `B-15`)์„ ๋จผ์ € ์ ์šฉํ•˜์—ฌ ๋ธŒ๋กœ์ปค ๋‹ค์šด ์‹œ์—๋„ ๋ฃจํ”„๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๋Š” ๋ฐฉํƒ„ ๊ตฌ์กฐ๋ฅผ ํ™•๋ฆฝํ•ฉ๋‹ˆ๋‹ค. 2. **Phase 2 (๊ฐœ์ธ ๋ธŒ๋กœ์ปค ๊ฐ€๋™)**: ๊ฐœ์ธ ์„œ๋ฒ„์— `nats-server -c nats.conf`๋ฅผ ๊ตฌ๋™ํ•˜๊ณ  `.mam.env`์— `MQTT_BROKER`๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. 3. **Phase 3 (A-2 ๋ณด์•ˆ ์™„์ „ ์ข…๊ฒฐ)**: ์›Œํฌ์ŠคํŽ˜์ด์Šค ์ง€๋ฌธ ํ† ํ”ฝ(`mam//jobs/...`) ๋ฐ ๋ฌด์กฐ๊ฑด ํ† ํฐ ๋ฐœ๊ธ‰์„ ์ ์šฉํ•˜์—ฌ ๊ณต๊ฐœ ๋ธŒ๋กœ์ปค ์œ„ํ—˜์„ ์™„์ „ํžˆ ์˜๊ตฌ ํ๊ธฐํ•ฉ๋‹ˆ๋‹ค.