클라우드 CCTV는 편하다. 대신 채널당 월 구독료를 내고, 녹화본은 남의 서버에 쌓이고, 감지 기능은 상위 요금제에서 풀린다. Frigate는 이 구조를 뒤집는 오픈소스 NVR이다. 카메라 영상을 내 서버에서 받아 AI로 사람·차량을 감지하고, 녹화도 감지도 구독료 없이 처리한다. 이 글은 Frigate와 스트리밍 도우미 go2rtc를 도커로 구축하는 전 과정과, "카메라당 RTSP 클라이언트는 하나만"이라는 원칙을 배우게 된 사연을 담았다.
필요한 것은 RTSP를 지원하는 카메라(요즘 저가 카메라 대부분이 지원)와 서버 한 대뿐이다.
Frigate는 AI 객체 감지에 특화된 오픈소스 NVR(네트워크 녹화기)이다. 카메라 스트림을 받아서 사람·차량·동물을 실시간으로 분류하고, 감지 구간만 골라 녹화하며, 홈어시스턴트와 완전히 연동된다. 상용 클라우드 CCTV의 "사람 감지 알림" 기능을 구독료 없이, 그리고 인터넷 없이(로컬에서) 제공한다.
go2rtc는 스트리밍 만능 어댑터다. RTSP·WebRTC·MSE·HLS 등 프로토콜이 서로 다른 세계를 이어주고, 하나의 입력 스트림을 여러 출력으로 뿌려주는(restream) 역할을 한다. 카메라와 소프트웨어 사이의 통역사이자 분배기라고 보면 된다.
둘을 조합하면 이런 시스템이 된다.
| 구성 요소 | 역할 | 대체 대상(상용) |
|---|---|---|
| 카메라 | RTSP 스트림 제공 | 클라우드 CCTV 본체 |
| go2rtc | 카메라당 1개 세션 → 다수 소비자 분배 | (상용은 앱·웹이 각자 직접 접속) |
| Frigate | AI 감지 + 이벤트 녹화 + 웹UI | 클라우드 감지 구독 + SD카드 녹화 |
| Home Assistant | 대시보드·자동화·푸시 | 클라우드 앱 |
| NAS | 녹화 보관 | 클라우드 저장 공간 구독 |
flowchart LR
subgraph CAM["IP 카메라 - RTSP"]
C1["카메라 A - H.264"]
C2["카메라 B - H.265"]
end
subgraph SRV["Docker 서버"]
G["go2rtc - 카메라당 세션 1개"]
HA["Home Assistant - 실시간 뷰"]
FR["Frigate - AI 감지·녹화"]
MQ["Mosquitto - MQTT 브로커"]
end
N[("NAS - 녹화 7일 보관")]
P["모바일 푸시"]
C1 -->|rtsp| G
G -->|restream| HA
G -->|restream| FR
FR -->|감지 이벤트| MQ
MQ -->|상태| HA
FR --> N
HA --> P
카메라의 RTSP 주소는 보통 앱이나 웹 관리자 화면에 나오지만, 기기별로 다르면 iSpyConnect의 카메라 데이터베이스에서 모델명으로 검색하면 대부분 나온다.
# go2rtc.yaml
api:
listen: ":1984" # 웹UI
rtsp:
listen: ":8554" # restream 출력
webrtc:
listen: ":8555/tcp" # 저지연 시청용
streams:
cam_a:
- rtsp://192.168.0.50:554/live/ch0 # 예시 주소
cam_b:
- rtsp://192.168.0.51:554/unicast
카메라마다 이름을 붙여 등록하면 rtsp://서버IP:8554/cam_a로 어디서든 다시 볼 수 있다. 웹UI(:1984)에서 스트림별 연결 상태와 디버그 페이지를 제공하니 초기 세팅은 여기서 눈으로 확인하며 하면 된다.
이미지 태그 주의. alexxit/go2rtc의 정식 latest 태그(1.9.x)를 쓰면 최신 Home Assistant의 go2rtc 클라이언트가 스트림 연결을 계속 실패시킨다. HA 쪽이 go2rtc master 빌드의 API 응답 형식을 기대하기 때문이다.
image: alexxit/go2rtc:master # latest(1.9.x) → HA 연동 실패
# config.yml
mqtt:
enabled: true
host: mosquitto # MQTT 브로커 컨테이너명
topic_prefix: frigate
cameras:
cam_a:
ffmpeg:
input: rtsp://서버IP:8554/cam_a # go2rtc 경유
detect:
width: 1280
height: 720
fps: 5
record:
enabled: true
retain:
days: 7
detectors:
cpu0:
type: cpu
핵심 포인트 세 가지:
record의 경로를 NAS 마운트로 잡고 보관 일수를 정한다. Frigate는 날짜별(UTC) 폴더로 쌓으므로 용량 관리가 예측 가능하다.Frigate 통합(HACS에서 설치)과 Mosquitto 브로커를 HA에 붙이면, 카메라 엔티티와 감지 이벤트(person, car 등)가 자동으로 생긴다. 여기서 할 수 있는 일:
정지 미리보기 카드가 갱신 없이 굳어 있는 증상은 흔한데, homeassistant.update_entity를 10초마다 호출하는 소형 자동화 하나로 해결된다.
이 구조를 만들게 된 직접적인 계기는 카메라의 부하 한계였다. 처음에는 HA가 카메라에 직접 붙고, Frigate가 직접 붙는 단순한 구성이었다. 화면은 잘 나왔다. 그러다 셋째 클라이언트(VLC 확인용)를 붙였을 때:
| 동시 RTSP 접속 | 카메라 fps | 상태 |
|---|---|---|
| 1 | ~16 | 정상 |
| 2 | 8~10 | 버벅임 |
| 3 | 3.5로 붕괴 | 카메라 자체 리부팅 |
저가형 IP 카메라의 인코더는 동시 접속 수에 극도로 약하다. fps가 선형으로 떨어지는 게 아니라 어떤 벽을 만나면 뚝 떨어지고, 심하면 기기가 스스로 재부팅한다. 위 표의 세 번째 행은 go2rtc 관문 구조에서는 발생하지 않는다 — 카메라 입장에서 클라이언트는 언제나 go2rtc 하나이기 때문이다.
이 원칙은 디버깅에도 적용된다. 스트림이 의심스러울 때 ffprobe를 카메라에 직접 찔러 확인하고 싶은 유혹을 참자. 측정 목적의 접속조차 저가 카메라에게는 부담이고, 실제로 "왜 화면이 끊기지?"의 정답이 "내가 확인하던 그 접속"이었던 적이 있다. 모든 확인은 go2rtc 경유로.
검은 화면 워치독. go2rtc는 끊긴 스트림을 다음 요청 때 재접속하지만, 아무도 보지 않는 카메라는 끊긴 채로 방치되는 경우가 있었다. 아침에 대시보드를 열면 한 대가 검은 화면인 상황이 반복되자, cron으로 매분 카메라별 세션 상태를 확인해 재요청을 유발하는 10줄짜리 셸 스크립트를 붙였다. 이후 검은 화면을 본 적이 없다.
오디오 없는 녹화. 카메라가 AAC를 지원하지 않으면 녹화에 소리가 없다. go2rtc에서 ffmpeg:#audio=opus 트랜스코딩을 걸어 해결된다.
보관 정책. 감지 이벤트 영상은 무한정 쌓지 않는다. 사람 감지 클립 30일, 전체 녹화 7일처럼 감지 종류별로 다른 보관 기간을 두면 저장소가 예측 가능하게 유지된다.
| 증상 | 원인 | 해결 |
|---|---|---|
| HA에서 스트림 계속 실패 | go2rtc latest 이미지 |
master 이미지로 교체 |
| 카메라 fps 붕괴·재부팅 | 다중 RTSP 직접 접속 | go2rtc 관문 구조로 단일화 |
| 녹화에 소리가 없다 | 카메라 AAC 미지원 | go2rtc 오디오 트랜스코딩 |
| 아침마다 화면이 꺼져 있다 | 미요청 시 재접속 없음 | 매분 세션 점검 cron |
| 미리보기가 갱신 안 된다 | 정지 스냅샷 캐시 | update_entity 자동화 |
| 감지가 늦다 | 감지 fps·해상도 과소 설정 | 720p/5fps 확인, GPU 가속 검토 |
SS는 카메라 호환성과 완성도가 좋다. 대신 AI 감지가 브랜드 종속적이고 HA 같은 허브와의 연동이 제한적이다. Frigate는 오픈 모델로 사람·차량을 감지하고 그 이벤트를 자동화에서 즉시 쓸 수 있다는 게 핵심 차이다. 전제는 카메라가 RTSP를 제공하는 것.
서버 전력 + 저장소뿐이다. 카메라를 늘려도 추가 비용이 저장소 증분뿐이라, 채널당 구독료를 내는 모델과는 4대 이상부터 격차가 벌어진다. 무엇보다 영상이 외부로 나가지 않는다는 점이 프라이버시 쪽에서 크다.
카메라 한두 대면 CPU 감지로 가능하다(코어 1개 내외). 그 이상이면 Coral USB 또는 GPU를 더하자. 단 감지 스트림을 1080p 고 framerate로 돌리는 실수만 하지 않으면, 생각보다 사양이 낮아도 버틴다.
서버 한 대로 충분하다 연재 목차 — 시리즈 소개 · 1화. Home Assistant · 3화. Seafile 13
동일 구성으로 카메라 2대를 24/7 운영하며 검증했습니다.