🌍 네트워크

mDNS

Multicast DNS

로컬 네트워크에서 DNS 서버 없이 .local 도메인으로 장치를 검색하는 프로토콜. Apple Bonjour, Linux Avahi로 구현되며, IoT 기기 자동 검색과 제로 컨피그 네트워킹에 활용됩니다.

📖 상세 설명

mDNS(Multicast DNS)는 로컬 네트워크에서 DNS 서버 없이 호스트 이름을 IP 주소로 해석할 수 있게 해주는 제로 컨피그 네트워킹(Zero-configuration networking) 프로토콜입니다. 일반 DNS는 중앙 서버에 쿼리하지만, mDNS는 멀티캐스트 방식으로 로컬 네트워크의 모든 장치에 쿼리를 브로드캐스트합니다.

mDNS는 Apple이 2002년 Bonjour(구 Rendezvous)로 처음 구현했습니다. 이후 RFC 6762로 표준화되었고, Linux에서는 Avahi 데몬으로 구현됩니다. Windows 10 이후부터는 기본 지원됩니다. mDNS는 주로 .local 도메인을 사용하며, 예를 들어 MacBook.local이나 raspberrypi.local 같은 이름으로 장치에 접근할 수 있습니다.

mDNS는 UDP 포트 5353을 사용하고, 멀티캐스트 주소 224.0.0.251(IPv4) 또는 ff02::fb(IPv6)로 쿼리를 전송합니다. 요청을 받은 장치 중 해당 이름을 가진 장치만 응답합니다. 이 방식은 DHCP 서버나 DNS 서버가 없는 소규모 네트워크에서 특히 유용합니다.

실무에서 mDNS는 프린터 자동 검색, AirPlay/Chromecast 스트리밍, IoT 장치 설정, 개발 환경의 로컬 서버 접근 등에 널리 사용됩니다. DNS-SD(DNS Service Discovery)와 결합하면 네트워크에서 사용 가능한 서비스(프린터, 파일 공유, 웹서버 등)를 자동으로 검색할 수 있습니다.

💻 코드 예제

from zeroconf import ServiceBrowser, Zeroconf, ServiceInfo
import socket
import time

# pip install zeroconf

# 1. mDNS 서비스 검색 (예: HTTP 서버 찾기)
class MyListener:
    def add_service(self, zc, type_, name):
        info = zc.get_service_info(type_, name)
        if info:
            addresses = [socket.inet_ntoa(addr) for addr in info.addresses]
            print(f"발견: {name}")
            print(f"  주소: {addresses}")
            print(f"  포트: {info.port}")
            print(f"  속성: {info.properties}")

    def remove_service(self, zc, type_, name):
        print(f"제거됨: {name}")

    def update_service(self, zc, type_, name):
        print(f"업데이트: {name}")

# HTTP 서비스 검색 시작
zeroconf = Zeroconf()
browser = ServiceBrowser(zeroconf, "_http._tcp.local.", MyListener())

print("서비스 검색 중... (10초)")
time.sleep(10)
zeroconf.close()

# 2. mDNS 서비스 등록 (자신의 서비스 광고)
from zeroconf import ServiceInfo, Zeroconf
import socket

def register_service():
    zc = Zeroconf()

    # 웹서버 서비스 정보
    info = ServiceInfo(
        "_http._tcp.local.",              # 서비스 타입
        "My Web Server._http._tcp.local.", # 서비스 이름
        addresses=[socket.inet_aton("192.168.1.100")],
        port=8080,
        properties={"path": "/api/", "version": "1.0"}
    )

    zc.register_service(info)
    print("서비스 등록 완료: my-web-server.local:8080")

    input("엔터를 누르면 서비스 해제...")
    zc.unregister_service(info)
    zc.close()

# 3. .local 도메인 직접 해석
import socket

try:
    ip = socket.gethostbyname("raspberrypi.local")
    print(f"raspberrypi.local → {ip}")
except socket.gaierror:
    print("mDNS 해석 실패 - Avahi/Bonjour 확인 필요")
# === macOS (Bonjour) ===

# .local 도메인 해석
ping raspberrypi.local
ssh pi@raspberrypi.local

# 네트워크 서비스 검색
dns-sd -B _http._tcp              # HTTP 서버 찾기
dns-sd -B _printer._tcp           # 프린터 찾기
dns-sd -B _airplay._tcp           # AirPlay 장치 찾기
dns-sd -B _googlecast._tcp        # Chromecast 찾기
dns-sd -B _services._dns-sd._udp  # 모든 서비스 타입

# 특정 서비스 상세 정보
dns-sd -L "My Printer" _printer._tcp

# mDNS 쿼리
dns-sd -Q raspberrypi.local A     # A 레코드 (IPv4)
dns-sd -Q raspberrypi.local AAAA  # AAAA 레코드 (IPv6)

# === Linux (Avahi) ===

# Avahi 설치
sudo apt install avahi-daemon avahi-utils

# .local 해석
avahi-resolve -n raspberrypi.local
avahi-resolve -a 192.168.1.100    # 역방향 해석

# 서비스 검색
avahi-browse -a                   # 모든 서비스
avahi-browse -t _http._tcp        # HTTP 서버
avahi-browse -r _ssh._tcp         # SSH 서버 (상세정보)

# 자신의 서비스 광고
avahi-publish -s "My Server" _http._tcp 8080 "path=/api"

# mDNS 상태 확인
systemctl status avahi-daemon

# === Windows 10+ ===

# mDNS 해석 (기본 지원)
ping raspberrypi.local

# Bonjour SDK 설치 후 dns-sd 사용 가능
dns-sd -B _http._tcp local

# 방화벽에서 mDNS 허용 (UDP 5353)
netsh advfirewall firewall add rule name="mDNS" dir=in action=allow protocol=UDP localport=5353
// npm install bonjour-service (Node.js)
const Bonjour = require('bonjour-service').default;
const bonjour = new Bonjour();

// 1. HTTP 서비스 검색
console.log('로컬 네트워크에서 HTTP 서버 검색 중...\n');

const browser = bonjour.find({ type: 'http' }, (service) => {
    console.log('발견된 서비스:');
    console.log(`  이름: ${service.name}`);
    console.log(`  호스트: ${service.host}`);
    console.log(`  포트: ${service.port}`);
    console.log(`  주소: ${service.addresses}`);
    console.log(`  TXT: ${JSON.stringify(service.txt)}`);
    console.log('---');
});

// 2. 자신의 서비스 광고 (등록)
const service = bonjour.publish({
    name: 'My Node.js API Server',
    type: 'http',
    port: 3000,
    txt: {
        version: '1.0.0',
        path: '/api/v1',
        secure: 'false'
    }
});

console.log('서비스 등록됨: my-nodejs-api.local:3000');

// 서비스 타입별 검색 예제
function searchServices(type, description) {
    bonjour.find({ type }, (service) => {
        console.log(`[${description}] ${service.name} at ${service.host}:${service.port}`);
    });
}

// 다양한 서비스 검색
searchServices('http', 'Web Server');
searchServices('printer', 'Printer');
searchServices('ssh', 'SSH Server');
searchServices('ftp', 'FTP Server');
searchServices('airplay', 'AirPlay');
searchServices('googlecast', 'Chromecast');

// 3. mDNS 직접 쿼리 (dns 모듈)
const dns = require('dns');

// .local 도메인 해석 (mDNS 지원 시스템에서 동작)
dns.lookup('raspberrypi.local', (err, address) => {
    if (err) {
        console.log('mDNS 해석 실패:', err.message);
    } else {
        console.log(`raspberrypi.local → ${address}`);
    }
});

// 종료 처리
process.on('SIGINT', () => {
    service.stop(() => {
        bonjour.destroy();
        console.log('\n서비스 해제 및 종료');
        process.exit();
    });
});

🗣️ 실무에서 이렇게 말하세요

💬 IoT 개발 팀 미팅에서
"스마트홈 앱에서 기기 검색할 때 mDNS 기반 서비스 디스커버리를 쓰면 됩니다. 각 기기에서 _myiot._tcp.local로 서비스를 광고하고, 앱에서 브라우즈하면 자동으로 검색됩니다. 사용자가 IP 주소를 수동 입력할 필요가 없어요."
💬 네트워크 장애 대응 시
"프린터가 검색 안 되면 먼저 mDNS 트래픽을 확인해보세요. UDP 5353 포트가 방화벽에서 차단됐을 수 있습니다. VLAN으로 네트워크가 분리된 경우 mDNS 리플렉터나 Avahi reflector를 설정해야 다른 서브넷에서도 보입니다."
💬 개발 환경 설정 논의에서
"Raspberry Pi 설정할 때 avahi-daemon만 활성화하면 raspberrypi.local로 바로 SSH 접속 가능합니다. IP 주소 찾느라 라우터 들어갈 필요 없어요. 이름 바꾸려면 /etc/hostname이랑 /etc/hosts만 수정하면 돼요."

⚠️ 흔한 실수 & 주의사항

VLAN/서브넷 간 mDNS 미작동

mDNS는 멀티캐스트 기반이라 같은 서브넷 내에서만 동작합니다. 다른 VLAN의 기기를 검색하려면 mDNS 게이트웨이나 Avahi reflector 설정이 필요합니다.

방화벽에서 UDP 5353 차단

기업 네트워크나 클라우드 환경에서 mDNS 포트가 차단되어 있는 경우가 많습니다. 로컬 개발 시 .local 도메인이 해석 안 되면 방화벽 설정을 확인하세요.

프로덕션 환경에서 .local 의존

mDNS는 로컬 네트워크 전용입니다. 프로덕션에서는 반드시 정적 DNS나 서비스 레지스트리(Consul, etcd)를 사용하세요. .local 도메인은 인터넷에서 해석되지 않습니다.

올바른 mDNS 활용

개발/테스트 환경에서 편리하게 사용하고, IoT/스마트홈 앱의 로컬 기기 검색에 활용하세요. Avahi(Linux)나 Bonjour(macOS)가 정상 동작하는지 먼저 확인하세요.

🔗 관련 용어

📚 더 배우기