# ๐ 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/...`) ๋ฐ ๋ฌด์กฐ๊ฑด ํ ํฐ ๋ฐ๊ธ์ ์ ์ฉํ์ฌ ๊ณต๊ฐ ๋ธ๋ก์ปค ์ํ์ ์์ ํ ์๊ตฌ ํ๊ธฐํฉ๋๋ค.