본문 바로가기
Tech/AI & LLM

실전 서비스 배포 — 미디어 서버 + 사진 관리 + 스마트홈 + 광고 차단 [Mac Mini 홈서버 완전 정복 9/10]

by Hoft 2026. 4. 1.

🖥️ Mac Mini 홈서버 완전 정복 시리즈

9/10 — 실전 서비스 배포

미디어 서버 + 사진 관리 + 스마트홈 + 광고 차단

지금까지 8편에 걸쳐 Mac Mini 홈서버의 하드웨어 선택부터 OS 최적화, Docker 환경, 리버스 프록시, 외부 접속, 보안, 백업까지 인프라의 뼈대를 완성했습니다. 이번 편에서는 드디어 그 인프라 위에 실제로 쓸 서비스를 올립니다. 넷플릭스 대신 쓸 미디어 서버 Jellyfin, Google Photos를 대체할 Immich, 스마트홈 허브 Home Assistant, 그리고 네트워크 전체의 광고를 차단하는 AdGuard Home까지 — 4개 서비스를 Docker Compose로 배포하고, 이전 편에서 만든 NPM + Cloudflare Tunnel과 연결하는 전 과정을 다룹니다.

📌 이런 분들에게 추천합니다

  • 1~8편까지 따라와서 인프라가 준비된 분 (Docker + NPM + Cloudflare Tunnel)
  • 넷플릭스/유튜브 프리미엄 대신 셀프호스팅 미디어 서버를 만들고 싶은 분
  • Google Photos에서 벗어나 사진을 직접 관리하고 싶은 분
  • 스마트홈 자동화와 네트워크 광고 차단에 관심 있는 분

⚠️ 사전 준비물 — 이 글은 1~8편의 환경이 갖춰져 있다고 가정합니다. 최소한 Docker(OrbStack)가 설치되어 있고, Nginx Proxy Manager와 Cloudflare Tunnel이 동작 중이어야 합니다. 아직이라면 해당 편을 먼저 완료해주세요.

1. 서비스 전체 구성도 — 무엇을 어떻게 배포하나

이번 편에서 배포할 4개 서비스와 역할을 먼저 정리하겠습니다.

서비스 역할 포트 RAM 사용량
🎬 Jellyfin 미디어 서버 (영화, 드라마, 음악) 8096 약 300~500MB
📸 Immich 사진/영상 자동 백업 + AI 검색 2283 약 2~4GB
🏠 Home Assistant 스마트홈 자동화 허브 8123 약 300~500MB
🛡️ AdGuard Home DNS 기반 광고/트래커 차단 53, 3080 약 50~100MB
합계 - - 약 3~5GB

💡 Mac Mini M4 16GB 기준으로 4개 서비스 + 4편의 필수 서비스(Portainer, Watchtower 등) + NPM + Cloudflare Tunnel을 모두 돌려도 RAM 여유가 충분합니다. Immich의 ML 기능을 적극 활용하거나, 대형 미디어 라이브러리를 운영할 계획이라면 24GB 이상을 권장합니다.

전체 트래픽 흐름은 다음과 같습니다.

# 외부 접속 흐름 (6편에서 구축한 구조)
사용자 → Cloudflare Tunnel → NPM → 각 서비스

# 서브도메인 매핑 예시
media.yourdomain.com   → Jellyfin  (localhost:8096)
photos.yourdomain.com  → Immich    (localhost:2283)
home.yourdomain.com    → Home Assistant (localhost:8123)
dns.yourdomain.com     → AdGuard Home  (localhost:3080)

2. 디렉토리 구조 설계

4편에서 만든 ~/homelab/ 구조를 확장합니다. 각 서비스별로 디렉토리를 분리하고, 미디어 파일과 사진은 별도 경로에 저장하는 것이 핵심입니다.

~/homelab/
├── jellyfin/
│   ├── docker-compose.yml
│   └── config/              # Jellyfin 설정 데이터
├── immich/
│   ├── docker-compose.yml
│   ├── .env                 # Immich 환경 변수
│   ├── library/             # 업로드된 사진/영상
│   └── postgres/            # DB 데이터 (반드시 로컬 SSD)
├── homeassistant/
│   ├── docker-compose.yml
│   └── config/              # HA 설정 + 자동화 파일
├── adguard/
│   ├── docker-compose.yml
│   ├── work/                # 쿼리 로그
│   └── conf/                # 설정 파일
└── media/                   # 공유 미디어 저장소
    ├── movies/
    ├── tvshows/
    └── music/

터미널에서 한 번에 생성합니다.

mkdir -p ~/homelab/{jellyfin/config,immich/{library,postgres},homeassistant/config,adguard/{work,conf},media/{movies,tvshows,music}}

💡 외장 드라이브를 사용하는 경우 — 미디어 파일이나 사진 라이브러리가 크다면 ~/homelab/media~/homelab/immich/library를 외장 드라이브 경로로 심볼릭 링크하세요. 예: ln -s /Volumes/ExternalSSD/media ~/homelab/media. 단, Immich의 PostgreSQL DB(postgres/)는 반드시 내장 SSD에 두어야 합니다. 네트워크 공유 스토리지도 DB에는 사용 불가입니다.

3. Jellyfin — 나만의 넷플릭스 구축

Jellyfin은 완전 무료 오픈소스 미디어 서버입니다. Plex와 달리 프리미엄 기능 잠금이 없고, 모든 기능을 무료로 사용할 수 있습니다. 영화, 드라마, 음악, 사진까지 관리하며, 웹 브라우저, iOS/Android 앱, TV 앱에서 스트리밍할 수 있습니다.

Jellyfin vs Plex — 왜 Jellyfin인가

항목 Jellyfin Plex
💰 가격 완전 무료 기본 무료 + Plex Pass $120/평생
📱 모바일 앱 무료 재생 1분 제한 (Pass 필요)
🔒 데이터 주권 100% 로컬, 외부 통신 없음 Plex 서버 경유 (계정 필수)
🏗️ 하드웨어 가속 지원 (Apple VideoToolbox 포함) 지원 (Pass 필요)
🐳 Docker ARM64 공식 지원 공식 지원
홈서버 추천도 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐

Docker Compose 배포

~/homelab/jellyfin/docker-compose.yml을 생성합니다.

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./config:/config                # 설정 데이터
      - ../media/movies:/data/movies    # 영화
      - ../media/tvshows:/data/tvshows  # 드라마
      - ../media/music:/data/music      # 음악
    ports:
      - "8096:8096"
    restart: unless-stopped

배포하고 로그를 확인합니다.

cd ~/homelab/jellyfin
docker compose up -d
docker compose logs -f jellyfin

브라우저에서 http://Mac-Mini-IP:8096에 접속하면 초기 설정 마법사가 나타납니다. 언어 선택 → 관리자 계정 생성 → 미디어 라이브러리 추가(/data/movies, /data/tvshows 등) → 완료 순서로 진행하면 됩니다.

⚠️ Mac Mini + Docker에서 하드웨어 트랜스코딩 주의

Jellyfin 공식 문서에 따르면 macOS Docker 컨테이너 안에서는 VideoToolbox 하드웨어 가속이 작동하지 않습니다. Docker는 Linux VM 위에서 실행되기 때문에 macOS의 GPU에 접근할 수 없습니다. 두 가지 해결책이 있습니다.

방법 1 (권장): Jellyfin을 네이티브 macOS 앱으로 설치 — 공식 사이트에서 macOS용 .dmg를 받아 설치하면 VideoToolbox 하드웨어 가속이 완전히 작동합니다. M4 기준으로 4K HDR 트랜스코딩을 동시에 3개까지 처리할 수 있습니다.

방법 2: Docker + 소프트웨어 트랜스코딩 — 위 Compose 설정 그대로 사용하되, 트랜스코딩은 CPU에서 처리됩니다. M4의 성능이면 1080p 소프트웨어 트랜스코딩 1~2개는 무리 없이 처리 가능합니다.

이 가이드에서는 시리즈의 일관성을 위해 Docker 배포를 기준으로 설명하되, 트랜스코딩이 중요한 경우 네이티브 설치를 권장합니다. 대부분의 기기에서 Direct Play(트랜스코딩 없이 재생)가 되므로 실제로 트랜스코딩이 필요한 경우는 생각보다 적습니다.

💡 Direct Play를 최대화하는 팁

• 미디어를 H.264(AVC) + AAC 조합으로 저장하면 거의 모든 클라이언트에서 Direct Play됩니다.

• Jellyfin 대시보드 → 재생 설정에서 최대 스트리밍 비트레이트를 클라이언트 네트워크에 맞게 설정하세요.

• 외부에서 접속할 때 8~10Mbps 정도면 1080p 스트리밍에 충분합니다.

4. Immich — Google Photos 완전 대체

Immich는 셀프호스팅 사진/영상 관리 플랫폼으로, Google Photos와 거의 동일한 사용 경험을 제공합니다. 2022년 개인 프로젝트로 시작해서 2025년 10월 v2.0으로 정식 안정 버전이 출시됐고, 2026년 3월 현재 v2.6까지 릴리스되며 GitHub 스타 9만 개를 넘긴 초대형 오픈소스 프로젝트입니다.

📸 Immich 핵심 기능

  • 자동 모바일 백업 — iOS/Android 앱이 백그라운드에서 사진·영상을 자동 업로드
  • AI 스마트 검색 — "해변 석양", "생일 케이크" 같은 자연어로 사진 검색 (CLIP 기반, 완전 로컬 처리)
  • 얼굴 인식 — 사람별 자동 분류, 완전히 서버 내에서 처리
  • 지도 보기 — GPS 메타데이터 기반 사진 지도 표시
  • 공유 앨범 — 가족/친구와 앨범 공유, 다중 사용자 지원
  • 외부 라이브러리 — 기존 사진 폴더를 복사 없이 그대로 인덱싱

시스템 요구사항

💡 Immich 최소 사양

RAM: 최소 6GB (ML 기능 포함), 4GB에서는 ML 비활성화 필요

CPU: 최소 2코어, 권장 4코어

스토리지: 썸네일 + 트랜스코딩으로 원본 대비 10~20% 추가 공간 필요

DB: PostgreSQL 데이터는 반드시 로컬 SSD에 저장 (네트워크 스토리지 불가)

Mac Mini M4 16GB라면 여유 있게 돌릴 수 있습니다.

Docker Compose 배포

Immich는 공식 Docker Compose를 제공하지만, 여기서는 시리즈 구조에 맞춰 정리한 버전을 사용합니다. 먼저 환경 변수 파일을 만듭니다.

~/homelab/immich/.env:

# Immich 환경 변수
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
TZ=Asia/Seoul

# PostgreSQL 설정
DB_PASSWORD=your-secure-password-here
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

# Immich 버전 (latest 대신 특정 버전 고정 권장)
IMMICH_VERSION=release

~/homelab/immich/docker-compose.yml:

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - "2283:2283"
    depends_on:
      - redis
      - database
    restart: always

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always

  redis:
    container_name: immich_redis
    image: docker.io/valkey/valkey:8-bookworm
    healthcheck:
      test: valkey-cli ping || exit 1
    restart: always

  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME} -d ${DB_DATABASE_NAME}"]
      interval: 30s
      start_period: 30s
      retries: 5
    restart: always

volumes:
  model-cache:

배포합니다.

cd ~/homelab/immich
docker compose up -d

# 모든 컨테이너가 healthy인지 확인
docker compose ps

# 서버 로그 확인
docker compose logs -f immich-server

http://Mac-Mini-IP:2283에 접속하면 초기 설정 화면이 나타납니다. 관리자 계정을 생성하고, iOS/Android에서 Immich 앱을 설치한 뒤 서버 URL을 입력하면 자동 백업이 시작됩니다.

💡 기존 사진 가져오기 — 이미 다른 곳에 사진이 저장되어 있다면, Immich의 외부 라이브러리 기능을 활용하세요. docker-compose.ymlimmich-server 볼륨에 기존 사진 폴더를 읽기 전용으로 마운트(/home/user/Photos:/mnt/external-photos:ro)하면 사진을 복사하지 않고 그대로 인덱싱할 수 있습니다.

⚠️ ML 모델 첫 다운로드immich-machine-learning 컨테이너가 처음 시작될 때 CLIP, 얼굴 인식 모델을 다운로드합니다(약 1~2GB). 이 과정에서 잠시 CPU/메모리 사용량이 올라갈 수 있으니, 첫 배포 후 5~10분 정도 여유를 두세요.

5. Home Assistant — 스마트홈 허브

Home Assistant는 오픈소스 스마트홈 자동화 플랫폼입니다. Philips Hue, Samsung SmartThings, Apple HomeKit, Zigbee, Z-Wave 등 수천 가지 스마트홈 기기를 하나의 대시보드에서 관리하고, 자동화 규칙을 만들 수 있습니다. 현재 2026.3 버전까지 릴리스되어 있으며, 매달 활발하게 업데이트됩니다.

설치 방식 선택

Home Assistant는 크게 두 가지 설치 방식이 있습니다.

방식 Home Assistant OS Home Assistant Container
설치 형태 전용 OS (VM 또는 전용 기기) Docker 컨테이너
애드온 지원 ✅ 완전 지원 ❌ 미지원
업데이트 자동 (UI에서 원클릭) 수동 (docker compose pull)
유연성 HA 전용 환경 다른 서비스와 공존 가능
홈서버 추천 스마트홈이 주 목적일 때 다른 서비스와 함께 운영할 때

Mac Mini 홈서버에서는 다른 서비스와 함께 돌리는 게 목적이므로 Container 방식을 사용합니다. 애드온이 필요한 기능(예: Zigbee2MQTT)은 별도 Docker 컨테이너로 대체할 수 있습니다.

Docker Compose 배포

~/homelab/homeassistant/docker-compose.yml:

services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    environment:
      - TZ=Asia/Seoul
    volumes:
      - ./config:/config
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "8123:8123"
    restart: unless-stopped

💡 USB 스마트홈 동글 연결 — Zigbee/Z-Wave USB 동글을 사용한다면 devices 옵션으로 컨테이너에 전달해야 합니다. OrbStack에서 USB 디바이스 패스스루는 제한적일 수 있으므로, 이 경우 Home Assistant OS를 VM으로 설치하는 것이 더 안정적입니다.

배포합니다.

cd ~/homelab/homeassistant
docker compose up -d
docker compose logs -f homeassistant

http://Mac-Mini-IP:8123에 접속하면 초기 설정(계정 생성 → 집 위치 설정 → 기기 검색)이 시작됩니다. 같은 네트워크에 있는 스마트 기기를 자동으로 발견해줍니다.

자동화 예시 — 일몰에 조명 켜기

Home Assistant의 진짜 힘은 자동화에 있습니다. 웹 UI에서 설정 → 자동화에서 다음과 같은 규칙을 만들 수 있습니다.

# config/automations.yaml 예시
- alias: "일몰 시 거실 조명 켜기"
  trigger:
    - platform: sun
      event: sunset
      offset: "-00:30:00"  # 일몰 30분 전
  action:
    - service: light.turn_on
      target:
        entity_id: light.living_room
      data:
        brightness_pct: 70
        color_temp_kelvin: 3000

6. AdGuard Home — 네트워크 광고 차단


AdGuard Home은 DNS 레벨에서 작동하는 광고/트래커 차단 서버입니다. 네트워크에 연결된 모든 기기 — PC, 스마트폰, 스마트TV, IoT 기기까지 — 의 광고를 차단합니다. 브라우저 확장 프로그램과 달리 기기마다 설치할 필요가 없고, 한 번 설정하면 네트워크 전체에 적용됩니다.

🛡️ AdGuard Home이 Pi-hole보다 나은 점

  • 깔끔한 웹 UI — 별도 설정 없이 세련된 대시보드 제공
  • DNS-over-HTTPS/TLS 기본 지원 — Pi-hole은 추가 설정 필요
  • 단일 컨테이너 — Pi-hole은 여러 서비스로 구성, AdGuard는 올인원
  • ARM64 완벽 지원 — Mac Mini M4에서 네이티브 실행

Docker Compose 배포

~/homelab/adguard/docker-compose.yml:

services:
  adguardhome:
    image: adguard/adguardhome:latest
    container_name: adguardhome
    ports:
      - "53:53/tcp"     # DNS
      - "53:53/udp"     # DNS
      - "3080:3000/tcp" # 웹 UI (초기 설정용, 4편 Homepage 3000번과 충돌 방지)
      - "8080:80/tcp"   # 웹 UI (설정 완료 후 관리용)
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf
    restart: unless-stopped

💡 포트 설명

53 — 실제 DNS 쿼리를 받는 포트 (TCP + UDP 모두 필요)

3080:3000 — 초기 설정 마법사 전용 포트. 설정 완료 후에는 사용하지 않음

8080:80 — 설정 완료 후 관리 대시보드 접속 포트

포트 53 충돌 해결 (macOS)

macOS에서는 mDNSResponder가 포트 53을 점유하고 있어 충돌이 발생할 수 있습니다. 먼저 충돌 여부를 확인합니다.

# 포트 53 사용 중인 프로세스 확인
sudo lsof -i :53

# 출력 예시 — mDNSResponder가 잡고 있으면 충돌
COMMAND     PID   USER   FD   TYPE  SIZE/OFF NODE NAME
mDNSRespon  123   root   ...  UDP   *:domain
mDNSRespon  123   root   ...  TCP   *:domain

충돌이 발생하면 두 가지 해결법이 있습니다.

방법 A (권장): Docker 포트를 5353으로 변경

Compose에서 DNS 포트 매핑을 "5353:53/tcp", "5353:53/udp"로 변경하고, 라우터 DNS를 Mac-Mini-IP:5353으로 설정합니다. macOS 시스템은 건드리지 않으므로 가장 안전합니다.

방법 B: mDNSResponder 포트 해제

macOS 시스템 설정 → 일반 → 공유에서 "콘텐츠 캐싱"이 켜져 있다면 끄세요. 또한 터미널에서 다음 명령으로 mDNSResponder를 재시작한 뒤 다시 확인합니다.

sudo killall -HUP mDNSResponder
sudo lsof -i :53

그래도 해결되지 않으면 방법 A를 사용하세요.

컨테이너 실행

cd ~/homelab/adguard
docker compose up -d

# 정상 실행 확인
docker compose ps
# STATUS가 "Up"이면 성공

# 포트 53 바인딩 에러가 나면 위의 충돌 해결 참고

pf 방화벽에서 DNS 포트 열기 (7편 연동)

🚨 필수 — 이 단계를 빠뜨리면 다른 기기에서 AdGuard Home을 사용할 수 없습니다

7편에서 설정한 pf 방화벽은 기본적으로 외부에서 들어오는 모든 트래픽을 차단합니다. SSH(22), 화면공유(5900), ICMP만 열어뒀기 때문에, DNS 포트(53)도 명시적으로 열어줘야 합니다. 이걸 안 하면 Mac Mini 자체에서는 DNS가 잘 되지만, 스마트폰이나 다른 기기에서 Mac Mini의 DNS로 접근할 때 방화벽에 막혀서 인터넷이 먹통이 됩니다.

# pf 방화벽 규칙 파일 열기
sudo nano /etc/pf.anchors/homeserver

# 기존 규칙 아래에 다음 한 줄 추가:
# AdGuard Home DNS (TCP + UDP 모두 필요)
pass in on $ext_if proto { tcp, udp } to any port 53

💡 { tcp, udp } 둘 다인가? — 일반 DNS 쿼리는 UDP 53을 사용하고, 응답이 512바이트를 넘거나 DNS-over-TLS를 사용할 때 TCP 53으로 전환됩니다. 둘 다 열어야 정상 동작합니다.

저장 후 적용합니다.

# 문법 검증 (에러 없으면 아무 출력 없음)
sudo pfctl -n -f /etc/pf.conf

# 규칙 적용
sudo pfctl -f /etc/pf.conf

# 적용된 규칙 확인 — port = 53이 보이면 성공
sudo pfctl -a homeserver -s rules

💡 다른 서비스 포트도 열어야 할까? — Jellyfin(8096), Immich(2283) 등은 NPM 리버스 프록시(80/443)를 경유하면 별도로 열 필요 없습니다. 하지만 LAN에서 IP:포트로 직접 접속하고 싶다면 같은 방식으로 해당 포트를 추가하세요.

초기 설정 마법사 (Step by Step)

브라우저에서 http://Mac-Mini-IP:3080에 접속합니다. 초기 설정 마법사가 5단계로 진행됩니다.

🔧 Step 1/5 — 시작 화면

"Get Started" (시작하기) 버튼을 클릭합니다.

🔧 Step 2/5 — 관리 웹 인터페이스 + DNS 리스닝 포트 설정

이 화면에서 두 가지를 설정합니다.

① Admin Web Interface (관리 웹 인터페이스)

• Listen interface: All interfaces (모든 인터페이스) 선택

• Port: 80 (기본값 유지)

② DNS Server (DNS 서버)

• Listen interface: All interfaces 선택

• Port: 53 (기본값 유지)

⚠️ 이 설정은 컨테이너 내부 포트입니다. Docker Compose에서 이미 외부 포트 매핑(3080→3000, 8080→80, 53→53)을 했으므로, 여기서는 기본값 그대로 두면 됩니다.

🔧 Step 3/5 — 관리자 계정 생성

• Username: 원하는 관리자 아이디 입력

• Password: 강력한 비밀번호 설정 (이 계정으로 DNS 설정을 제어하므로 중요합니다)

🔧 Step 4/5 — 기기 설정 안내

각 기기에서 DNS를 AdGuard Home으로 변경하는 방법을 안내하는 화면입니다. 여기서 보여주는 IP 주소는 Docker 내부 IP이므로 무시하세요. 실제로는 Mac Mini의 내부 IP(예: 192.168.0.10)를 사용해야 합니다. "Next"를 눌러 넘어갑니다.

🔧 Step 5/5 — 설정 완료

"Open Dashboard" (대시보드 열기)를 클릭하면 설정이 완료됩니다.

이제부터 관리 대시보드는 http://Mac-Mini-IP:8080에서 접속합니다. (초기 설정용 3080 포트는 더 이상 사용하지 않습니다.)

대시보드 기본 설정

초기 마법사가 끝나면 대시보드(http://Mac-Mini-IP:8080)에서 몇 가지 중요한 설정을 해줍니다.

① 상위 DNS 서버 설정

설정(Settings) → DNS 설정(DNS settings) → 상위 DNS 서버(Upstream DNS servers)에서 AdGuard Home이 실제 DNS 조회를 위임할 서버를 지정합니다.

# 상위 DNS 서버 (하나씩 줄바꿈으로 입력)
https://dns.cloudflare.com/dns-query
https://dns.google/dns-query
https://dns.quad9.net/dns-query

💡 DNS-over-HTTPS 권장https://로 시작하는 주소를 사용하면 DNS 쿼리가 암호화됩니다. ISP가 어떤 사이트에 접속하는지 볼 수 없게 되므로 프라이버시가 크게 향상됩니다. 일반 DNS(1.1.1.1)는 암호화 없이 평문으로 전송됩니다.

같은 화면 아래쪽에 부트스트랩 DNS 서버(Bootstrap DNS servers)가 있습니다. DNS-over-HTTPS 주소를 해석하기 위한 초기 DNS입니다.

# 부트스트랩 DNS (기본값 유지하거나 아래로 설정)
1.1.1.1
8.8.8.8

"Apply (적용)" 또는 "Save (저장)" 버튼을 클릭합니다.

② 필터 목록 추가

필터(Filters) → DNS 차단 목록(DNS blocklists)에서 광고/트래커 차단 리스트를 관리합니다. 기본으로 AdGuard DNS filter가 활성화되어 있고, 여기에 추가 리스트를 넣을 수 있습니다.

"차단 목록 추가(Add blocklist)" → "URL로 추가(Add a custom list)"를 선택하고 아래 URL을 하나씩 추가합니다.

리스트 이름 특징 URL
AdGuard DNS filter 기본 내장 (이미 활성화) 별도 추가 불필요
OISD Basic 가장 인기 있는 통합 리스트 https://abp.oisd.nl/basic/
AdAway Default 모바일 광고 특화 https://adaway.org/hosts.txt
1Hosts Lite 오탐 적은 보수적 리스트 https://o0.pages.dev/Lite/adblock.txt

💡 처음에는 기본 + OISD Basic 2개만으로 시작하세요. 너무 많은 리스트를 추가하면 정상 사이트(쇼핑몰 결제, 은행 앱 등)가 차단되는 오탐이 발생할 수 있습니다. 문제가 없으면 하나씩 추가하는 게 안전합니다.

③ DHCP 설정 — macOS에서는 비활성화

AdGuard Home에는 DHCP 서버 기능이 있지만, Docker의 --network host 모드가 필요하고 macOS Docker에서는 호스트 네트워크 모드가 지원되지 않습니다. DHCP 기능은 끈 상태로 두세요. DNS 기능만 사용합니다.

네트워크 전체에 적용하기

AdGuard Home이 실제로 광고를 차단하려면, 기기들의 DNS 쿼리가 AdGuard Home을 경유해야 합니다. 하지만 공유기 DNS를 변경하기 전에 반드시 해야 할 작업이 있습니다.

🚨 필수 선행 작업: Mac Mini의 DNS를 수동으로 고정하기

이 단계를 건너뛰면 인터넷이 완전히 먹통이 됩니다. 원인은 DNS 순환 참조(circular dependency)입니다.

❌ 순환 참조가 발생하는 과정:

# Mac Mini도 DHCP로 IP를 받고 있음
# 공유기 DNS를 Mac Mini IP로 변경하면...

공유기 DHCP → 모든 기기에 DNS=Mac Mini IP 배포
             → Mac Mini 자신에게도 DNS=자기 자신 배포 ❌

Mac Mini DNS → AdGuard Home (자기 자신)
AdGuard Home → "dns.cloudflare.com을 해석해야 해"
             → Mac Mini의 DNS에 물어봄
             → 다시 AdGuard Home (자기 자신) 🔄
             → 무한 루프 → 모든 인터넷 먹통

✅ 해결: Mac Mini의 DNS만 수동으로 고정

Mac Mini는 IP는 DHCP로 받되, DNS만 수동으로 외부 서버를 직접 지정합니다.

# macOS 설정 방법:
시스템 설정 → 네트워크 → Wi-Fi (또는 이더넷)
→ 연결된 네트워크의 "세부사항..." 클릭
→ DNS 탭
→ DHCP가 제공한 DNS 서버를 모두 삭제 (- 버튼)
→ 아래 서버를 직접 추가 (+ 버튼):
   1.1.1.1
   8.8.8.8
→ "확인" 클릭

또는 터미널에서 한 줄로 설정할 수 있습니다.

# 현재 네트워크 서비스 이름 확인
networksetup -listallnetworkservices

# DNS를 수동으로 설정 (Wi-Fi인 경우)
sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 8.8.8.8

# 이더넷인 경우
sudo networksetup -setdnsservers "Ethernet" 1.1.1.1 8.8.8.8

# 설정 확인
networksetup -getdnsservers Wi-Fi
# 출력: 1.1.1.1 / 8.8.8.8 이면 성공

💡 이렇게 하면: Mac Mini는 DNS를 1.1.1.1로 직접 해석 → AdGuard Home도 정상적으로 상위 DNS에 접근 가능 → 나머지 기기들은 공유기를 통해 Mac Mini(AdGuard Home)를 DNS로 사용 → 모든 것이 정상 작동합니다.

Mac Mini의 DNS 수동 설정을 완료한 후, 아래 방법으로 다른 기기들에 AdGuard Home을 적용합니다.

✅ 방법 1 (권장): 공유기 DNS 변경 — 네트워크 전체 적용

공유기 관리 페이지에 접속해서 DHCP 설정의 DNS 서버를 변경합니다.

# 공유기 관리 페이지 접속 (기종마다 다름)
# 보통 192.168.0.1 또는 192.168.1.1

# DHCP 설정에서 DNS 서버 변경:
기본(Primary) DNS:  192.168.0.10   # ← Mac Mini의 내부 IP
보조(Secondary) DNS: 1.1.1.1       # ← 폴백 (서버 장애 시 인터넷 끊김 방지)

공유기 기종별 경로 예시:

ipTIME: 관리도구 → 고급 설정 → 내부 네트워크 설정 → DHCP 서버 설정 → DNS 서버

KT 공유기: 장치설정 → DHCP 설정 → DNS 서버 주소

SK/LG 공유기: 네트워크 설정 → DHCP → DNS 서버

설정 후 저장하면 새로 접속하는 기기부터 자동 적용됩니다.

🔵 방법 2: 개별 기기 DNS 변경 — 특정 기기만 적용

공유기 설정을 바꿀 수 없는 경우, 개별 기기에서 수동으로 DNS를 변경합니다.

# macOS (Mac Mini가 아닌 다른 Mac)
시스템 설정 → Wi-Fi → 연결된 네트워크 "세부사항" → DNS
  → DNS 서버에 192.168.0.10 추가 (기존 서버 삭제)
# ⚠️ Mac Mini 서버 자체는 위에서 1.1.1.1로 설정했으므로 건드리지 않음!

# iPhone/iPad
설정 → Wi-Fi → 연결된 네트워크 (i) → DNS 구성
  → "수동"으로 변경 → DNS 서버에 192.168.0.10 입력

# Windows
설정 → 네트워크 → Wi-Fi → 어댑터 속성 → DNS 서버 할당
  → "수동" → IPv4 → 기본 DNS: 192.168.0.10

# Android
설정 → Wi-Fi → 연결된 네트워크 → 고급 → IP 설정: 고정
  → DNS 1: 192.168.0.10

적용 확인 — 진짜 차단되는지 테스트

DNS 설정을 변경한 후, 정상 작동하는지 확인하는 방법입니다.

# 1. DNS 캐시 비우기 (Mac)
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

# 2. AdGuard Home이 DNS를 처리하는지 확인
nslookup google.com
# Server: 192.168.0.10 이 나오면 성공

# 3. 차단이 작동하는지 확인
nslookup ads.google.com
# 0.0.0.0 또는 NXDOMAIN이 나오면 차단 성공

브라우저에서 AdGuard Home 대시보드(http://Mac-Mini-IP:8080)를 열면 쿼리 통계가 실시간으로 올라가는 것을 확인할 수 있습니다. 차단된 쿼리 수가 증가하면 정상 작동 중입니다.

⚠️ 특정 사이트가 안 열릴 때 (오탐 대처법)

필터가 정상 사이트를 차단하는 경우가 간혹 있습니다. 이때 대시보드 → 쿼리 로그(Query Log)에서 차단된 도메인을 찾아 "Unblock (차단 해제)"를 클릭하면 해당 도메인이 화이트리스트에 추가됩니다.

자주 쓰는 사이트가 차단되면: 필터 → 사용자 정의 필터링 규칙(Custom filtering rules)에 다음을 추가하세요.

# 특정 도메인 차단 해제 (예시)
@@||example.com^

🚑 인터넷이 완전히 먹통이 됐을 때 — 긴급 복구

공유기 DNS를 변경한 뒤 모든 기기의 인터넷이 안 되면 DNS 순환 참조가 원인입니다. 다음 순서로 복구하세요.

# 1단계: Mac Mini의 DNS를 수동으로 고정 (터미널)
sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 8.8.8.8
# 이더넷이면: sudo networksetup -setdnsservers "Ethernet" 1.1.1.1 8.8.8.8

# 2단계: DNS 캐시 비우기
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

# 3단계: AdGuard Home 컨테이너 재시작
cd ~/homelab/adguard
docker compose restart

# 4단계: 인터넷 연결 확인
ping -c 3 google.com
# 응답이 오면 복구 완료!

위 순서로도 안 되면 공유기 DNS를 원래대로(자동 또는 ISP 기본값) 되돌린 뒤 처음부터 다시 시도하세요.

💡 AdGuard Home 설정이 잘 됐다면 — 유튜브 앱 광고는 DNS 레벨에서 차단이 어렵지만, 대부분의 웹사이트 배너 광고, 팝업 광고, 앱 내 광고, 스마트TV 광고가 사라집니다. 하루 뒤 대시보드를 확인해보면 전체 DNS 쿼리 중 10~30%가 차단되는 것을 볼 수 있습니다.

7. NPM + Cloudflare Tunnel 연결

4개 서비스를 배포했으니 5편(NPM)과 6편(Cloudflare Tunnel)에서 만든 인프라와 연결합니다. 외부에서 서브도메인으로 각 서비스에 접근할 수 있게 하는 과정입니다.

Step 1: Cloudflare DNS에 서브도메인 추가

Cloudflare 대시보드 → DNS 레코드에서 CNAME 레코드를 추가합니다. 6편에서 Tunnel을 연결했다면 CNAME 대상은 Tunnel의 UUID 주소입니다.

# Cloudflare DNS 레코드 추가
media.yourdomain.com   → CNAME → {tunnel-id}.cfargotunnel.com
photos.yourdomain.com  → CNAME → {tunnel-id}.cfargotunnel.com
home.yourdomain.com    → CNAME → {tunnel-id}.cfargotunnel.com

💡 AdGuard Home은 외부 공개 비권장 — DNS 서버를 인터넷에 공개하면 DNS 증폭 공격에 악용될 수 있습니다. AdGuard Home의 웹 관리 UI만 필요한 경우에만 Tunnel에 연결하고, DNS 기능은 내부 네트워크에서만 사용하세요.

Step 2: Cloudflare Tunnel 설정에 서비스 추가

Cloudflare Zero Trust 대시보드 → Access → Tunnels → 기존 터널 설정에서 Public Hostname을 추가합니다.

Subdomain Service Type URL
media HTTP localhost:8096
photos HTTP localhost:2283
home HTTP localhost:8123

또는 6편에서 구축한 NPM을 경유하도록 설정할 수도 있습니다.

Step 3: NPM Proxy Host 설정 (LAN 접속용)

내부 네트워크에서 서브도메인으로 접근하려면 NPM에도 Proxy Host를 추가합니다.

# NPM Proxy Host 설정 (각 서비스당 하나)

Domain Names:    media.yourdomain.com
Scheme:          http
Forward Host:    192.168.0.10  # Mac Mini IP
Forward Port:    8096
# SSL: Cloudflare Tunnel 경유 시 Force SSL 끄기 (6편 참고)

Domain Names:    photos.yourdomain.com
Forward Port:    2283

Domain Names:    home.yourdomain.com
Forward Port:    8123

⚠️ 6편에서 다룬 Force SSL 주의사항 — Cloudflare Tunnel을 사용할 때 NPM의 Force SSL을 반드시 꺼야 합니다. Tunnel이 이미 HTTPS를 처리하므로, NPM에서 또 SSL 리디렉트를 하면 무한 리디렉트 루프가 발생합니다.

Step 4: Home Assistant 외부 접근 특별 설정

Home Assistant는 기본적으로 리버스 프록시를 통한 접근을 차단합니다. configuration.yaml에 다음을 추가해야 합니다.

# ~/homelab/homeassistant/config/configuration.yaml
http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.16.0.0/12   # Docker 네트워크 대역
    - 192.168.0.0/16  # LAN 대역
    - 127.0.0.1       # localhost (Cloudflare Tunnel)

설정 후 Home Assistant 컨테이너를 재시작합니다.

cd ~/homelab/homeassistant
docker compose restart

8. 완성 체크리스트

모든 서비스가 정상적으로 동작하는지 확인합니다.

✅ 서비스 배포 체크리스트

☐ Jellyfin — http://Mac-Mini-IP:8096 접속 + 미디어 라이브러리 표시 확인

☐ Jellyfin — 영상 재생 테스트 (Direct Play + 트랜스코딩)

☐ Immich — http://Mac-Mini-IP:2283 접속 + 관리자 계정 생성

☐ Immich — 모바일 앱에서 사진 자동 백업 동작 확인

☐ Immich — AI 스마트 검색 동작 확인 (첫 ML 모델 로드 후)

☐ Home Assistant — http://Mac-Mini-IP:8123 접속 + 초기 설정 완료

☐ Home Assistant — 네트워크 기기 자동 탐색 확인

☐ AdGuard Home — http://Mac-Mini-IP:3080 접속 + 초기 설정 완료

☐ AdGuard Home — pf 방화벽에 포트 53 허용 규칙 추가 (7편 연동)

☐ AdGuard Home — Mac Mini DNS를 수동으로 1.1.1.1/8.8.8.8로 고정 (순환 참조 방지)

☐ AdGuard Home — 공유기 DNS 변경 후 다른 기기에서 광고 차단 확인

☐ 외부 접속 — Cloudflare Tunnel 경유 서브도메인 접속 확인

☐ NPM — 각 서비스별 Proxy Host 설정 완료

☐ 전체 — docker ps로 모든 컨테이너 running 상태 확인

전체 컨테이너 상태 한눈에 보기

# 모든 컨테이너 상태 확인
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

# 예상 출력:
NAMES                    STATUS          PORTS
jellyfin                 Up 2 hours      0.0.0.0:8096->8096/tcp
immich_server            Up 2 hours      0.0.0.0:2283->2283/tcp
immich_machine_learning  Up 2 hours
immich_redis             Up 2 hours
immich_postgres          Up 2 hours
homeassistant            Up 2 hours      0.0.0.0:8123->8123/tcp
adguardhome              Up 2 hours      0.0.0.0:53->53/tcp+udp, ...

🎉 여기까지 왔다면

Mac Mini 하나에 미디어 서버, 사진 클라우드, 스마트홈 허브, 광고 차단까지 — 클라우드 서비스 없이 완전한 디지털 인프라를 구축한 것입니다. 넷플릭스 $17/월, Google One $10/월, 유튜브 프리미엄 $14/월을 합치면 연간 약 $500인데, 이 모든 걸 Mac Mini의 연간 전기요금 약 5,000원으로 대체할 수 있습니다.

🖥️ Mac Mini 홈서버 완전 정복 시리즈

✅ 1편. 왜 Mac Mini인가? — 홈서버 하드웨어 선택 가이드

✅ 2편. macOS 서버 초기 세팅 — 헤드리스 최적화 완벽 가이드

✅ 3편. Docker 환경 구축 — OrbStack vs Docker Desktop vs Colima 비교

✅ 4편. 홈서버 필수 서비스 한방에 띄우기 — Docker Compose 실전편

✅ 5편. 리버스 프록시 구축 — Nginx Proxy Manager로 도메인 + HTTPS

✅ 6편. 외부 접속 — Cloudflare Tunnel로 포트포워딩 없이 공개

✅ 7편. 보안 강화 — SSH 키 인증 + Fail2Ban + 방화벽

✅ 8편. 스토리지 & 백업 전략

✅ 9편. 실전 서비스 배포 — 미디어 + 사진 + 스마트홈 + 광고 차단 (현재 글)

📌 10편. 종합 모니터링 & 운영 자동화 — 프로덕션급 홈랩 완성

이 글에서는 Mac Mini 홈서버 위에 실제로 사용할 4가지 핵심 서비스를 배포했습니다. 미디어 스트리밍(Jellyfin), 사진 관리(Immich), 스마트홈(Home Assistant), 광고 차단(AdGuard Home) — 이전 편에서 만든 인프라와 결합하면 클라우드 구독 서비스를 대부분 대체할 수 있는 홈서버가 완성됩니다. 마지막 10편에서는 이 모든 것을 안정적으로 운영하기 위한 Prometheus + Grafana 모니터링 대시보드 구축, Discord/Telegram 알림 자동화, Watchtower 업데이트 전략, 그리고 전체 아키텍처 다이어그램을 포함한 시리즈 총정리를 다룹니다.

도움이 됐다면 공감 ❤️ 부탁드려요! 시리즈 알림 받으시려면 구독도 부탁합니다.

반응형

▲ TOP