영상은 로컬을
벗어나지 않는다
실내 카메라를 1초 미만 지연으로 보고, 소리 이벤트를 감지해 알리고, 필요한 기록을 남기는 개인용 PWA. 영상과 기록을 외부 서버에 최소한으로만 둔다.
// the brief
보는 것과 알아채는 것은 다르다
실시간 영상을 볼 수 있다는 것과, 못 보고 있을 때 이상을 알아채는 것은 전혀 다른 문제다. 시판 제품은 대개 영상을 업체 클라우드로 올리고, 알림도 그 클라우드가 살아 있어야 온다. 영상이 남의 서버에 상시로 놓이는 것도, 그 서버가 죽으면 알림까지 같이 죽는 구조도 받아들이기 어려웠다.
그래서 목표를 둘로 잡았다. 영상은 로컬을 벗어나지 않는다. 그리고 못 보고 있을 때는 별도 경로로 알린다. 기록 기능은 그 위에 얹었다.
// approach
기록과 모니터링을 한 앱에
Next.js로 만든 PWA를 Cloudflare Pages에 올려 폰에서 앱처럼 쓰도록 했다. 주 사용처가 휴대폰이라 화면은 모바일 우선으로 설계하고, 기록은 몇 번의 탭으로 끝나게 다듬었다.
개발은 AI 도구를 엮어 빠르게 진행했다. 코드 생성과 리팩터의 속도는 AI에서 얻되, 무엇을 남기고 어떤 흐름으로 묶을지, 어디까지 신뢰할지는 사람이 판단하고 마감했다. 그것이 1인 스튜디오가 이만한 범위를 감당하는 방식이다.
기록 앱으로 시작해 카메라를 붙여 '기록 + 안전망'으로 확장했다. 손으로 남기는 기록과 실시간 모니터링이라는 성격이 전혀 다른 기능을, 한 앱 안에서 자연스럽게 오가도록 묶는 것이 설계의 핵심이었다.
AI로 빠르게 짓되, 무엇을 남기고 어디까지 믿을지는 사람이 정했다.
// key decisions
실력이 갈린 세 결정
영상은 클라우드에 올리지 않는다
IP 카메라는 내부망에서만 접근하도록 두고, 같은 망의 맥 한 대가 RTSP를 저지연 WebRTC로 다시 뿌린다. 밖에서 볼 때도 공개 서버가 아니라 Tailscale 사설망만 지난다. 지연과 프라이버시 어느 쪽도 타협할 수 없어 엣지 처리를 택했다.
'못 볼 때'를 위한 별도 신뢰 경로
라이브뷰가 되는 것과, 문제가 생겼을 때 알림이 오는 것은 다른 문제다. 엣지가 죽거나 네트워크가 끊기면 그 사실 자체를 critical 푸시로 알리고, watchdog 프로세스가 파이프라인을 상시 감시한다. 감시가 아니라 '놓쳤을 때 알려주는' 보조 알림으로 설계했다.
개인 데이터에 맞는 인증·보안
남에게 새면 안 되는 데이터라, 로그인은 WebAuthn 패스키로 두고 서버는 CSP 보안헤더·로그인 rate limit·import 검증·XSS 가드로 감쌌다. 편의 기능이 아니라 전제에서 출발한 결정이다.
세 결정을 관통하는 원칙은 하나다. 영상과 개인 기록을 외부 서버에 최소한으로만 둔다. 그래서 무거워 보이는 엣지 처리·패스키·사설망을 개인 프로젝트에도 그대로 적용했다.
// edge pipeline
카메라에서 폰까지, 로컬을 벗어나지 않는 경로
맥 한 대가 카메라에 붙는 유일한 장치다. launchd로 상시 세 프로세스가 돈다. 영상을 재발행하는 go2rtc, 파이프라인 생존을 감시하는 watchdog, 소리를 분석하는 audio. 카메라의 RTSP를 저지연 WebRTC로 바꿔 폰 화면에 띄우고, 바깥에서는 Tailscale 사설망으로만 닿는다. 소리 이벤트는 YAMNet 음향 모델로 분류하고, 엣지가 끊기면 그 사실을 critical 푸시로 알린다. 스냅샷과 야간 요약도 여기서 만든다. 다만 이것은 보조 알림이지 사람의 직접 확인을 대체하는 감시 장치가 아니라는 선을, 코드와 앱 안내 모두에 분명히 적어 두었다.
// result
결과
매일 실사용 중이며, 코드 변경은 빌드부터 배포까지 자동으로 이어진다. 흩어져 있던 기록과 실시간 모니터링을 한 곳에서 처리한다. 데모가 아니라 계속 쓰이면서 다듬어지는 앱이다.
// 이런 걸 만들어 드립니다.