[허수아비] 시스템 아키텍처 의사결정 총정리
조류 충돌 예방 관제 시스템 '허수아비'의 5가지 핵심 아키텍처 의사결정을 정리합니다. 가상 엣지 카메라, 영상 중계 프록시, 실시간 알림 프로토콜, 데이터 파이프라인, 물리 노드 분리까지 각 결정의 트레이드오프와 근거를 다룹니다.
‘허수아비(BirdyBuddy)’ 관제 시스템을 설계하면서 논의한 5가지 핵심 아키텍처 결정 사항입니다. 초기 아이디어에서 출발하여 인프라의 현실적인 제약을 극복하고 시스템 안정성을 확보하기까지, 각 결정의 트레이드오프와 근거를 기록합니다.
미리보기
- 가상 엣지(Mock Edge): 실제 CCTV 없이 FFmpeg + RTSP로 실환경을 완벽히 모사
- 영상 중계: MediaMTX를 전담 Media Proxy로 두어 Spring Boot에서 영상 트래픽을 완전 분리
- 실시간 알림: 양방향이 필요 없는 경보 데이터는 WebSocket 대신 SSE 채택
- 데이터 파이프라인: EC2 자원 한계를 고려해 Lambda 아키텍처(일일 Cron Batch)로 최종 확정
- 노드 분리: EC2 2대를 ‘App & DB’와 ‘AI & Data Infra’로 역할 기반 클러스터링
1. 엣지 디바이스를 Mock Edge 컨테이너로 대체하다
논점: 실제 CCTV·레이더 장비 없이 EC2 내부에서 데이터 수집 환경을 어떻게 구축할 것인가.
검토한 대안
- 파일 직접 읽기(File I/O): AI 워커가 내부 폴더의
.mp4파일을 직접 읽어 분석하면 CPU와 네트워크를 아낄 수 있습니다. 다만 ‘실시간 관제 시스템’이라는 포트폴리오 방향성과 충돌하고, 실제 운영 환경과 너무 동떨어진 구조가 됩니다.
최종 결정: 가상 엣지(Mock Edge) 컨테이너 도입
독립된 Docker 컨테이너를 띄우고, FFmpeg로 bird.mp4 파일을 무한 반복 재생하여 RTSP 스트림으로 변환·송출합니다.
AI 워커와 미디어 중계 서버는 이 가짜 스트림을 실제 카메라처럼 취급합니다. 향후 프로덕션에서 실제 하드웨어 카메라가 도입되더라도, 아키텍처나 코드 수정 없이 IP 주소만 변경하면 즉시 호환되는 확장성을 확보했습니다.
2. 영상 스트리밍 프로토콜 및 중계 아키텍처
논점: 가상 엣지에서 송출되는 영상을 관제 화면(React)까지 지연 없이, 서버 부하 없이 어떻게 전달할 것인가.
검토한 대안
- AI 워커 ↔ 프론트엔드 직접 연결: 다중 접속 시 AI 워커에 부하가 집중(Fan-out)되어 YOLO 추론이 멈출 위험이 있습니다. 인증(Security) 처리도 불가합니다.
- Spring Boot를 통한 중계: 메인 백엔드가 무거운 영상 바이너리 데이터를 처리하게 되어 API 응답 속도 저하 및 OOM(Out of Memory) 위험이 있습니다.
최종 결정: 전담 Media Proxy(MediaMTX) 분리 + WebRTC 사용
영상 트래픽은 메인 백엔드를 완전히 우회합니다. 가상 엣지(RTSP) → MediaMTX → React(WebRTC) 경로를 전담 서버가 처리합니다.
무거운 미디어 처리와 가벼운 비즈니스 로직(DB, API)의 책임을 물리적으로 분리하여 시스템 가용성을 극대화했습니다.
3. 실시간 알림 통신망 설계
논점: 레이더의 1초 단위 위치 데이터와 AI 워커의 위험 탐지 경보를 프론트엔드로 어떻게 밀어줄(Push) 것인가.
검토한 대안
- WebSocket: 양방향 통신이 가능하지만, 현재 요구사항은 서버가 클라이언트에게 일방적으로 알림을 보내는 구조이므로 오버스펙입니다.
최종 결정: SSE(Server-Sent Events) 채택
Kafka → Spring Boot → React 구간을 SSE로 연결합니다.
텍스트 기반 단방향 데이터(좌표, 알림 로그) 전송에 가장 가볍고 최적화된 프로토콜입니다. 브라우저 내장 기능인 자동 재연결(Auto-reconnect)을 지원해 구현 난이도를 낮추고 안정성을 높였습니다.
4. 데이터 파이프라인 구조 확립
논점: 초당 쏟아지는 원시 데이터 아카이빙과 무거운 통계 집계를 어떻게 처리·스케줄링할 것인가.
검토한 대안
- Kappa 아키텍처(All Streaming): Spark Structured Streaming으로 1분/1시간 단위 통계를 계속 연산하면, EC2 한정 자원에서 AI 워커(YOLO)와 심각한 CPU·Memory 경합이 발생합니다.
최종 결정: 1일 단위 배치를 적용한 Lambda 아키텍처
| 계층 | 역할 | 현재 시스템 |
|---|---|---|
| Speed Layer | 최신 실시간 데이터를 즉각 처리·표출 | Kafka → Spring Boot 즉시 컨슘 → SSE로 프론트 전송 |
| Batch Layer | 원본 데이터를 보관하고 무거운 연산을 일괄 처리 | Spark Streaming이 원시 데이터를 HDFS에 적재, 매일 자정 Cron으로 Spark Batch가 통계 집계 |
| Serving Layer | 배치·스피드 계층의 결과를 클라이언트에 제공 | PostgreSQL(통계) + Spring Boot REST API |
인프라 자원의 한계를 인정하고, 실시간 반응성(경보)과 데이터 무결성(일일 통계)을 모두 지켜내는 가장 현실적인 타협안입니다.
5. 물리적 인프라 노드 분리
논점: 무거운 컴포넌트들을 EC2 2대에 어떻게 배치할 것인가.
최종 결정: 역할 기반 클러스터링
| 노드 | 구성 컴포넌트 | 담당 역할 |
|---|---|---|
| Node 1 (App & DB) | Nginx, Spring Boot, PostgreSQL, MinIO, MediaMTX | 사용자 트래픽 응답 및 트랜잭션 전담 |
| Node 2 (AI & Data Infra) | 가상 엣지, AI 워커(YOLO), Kafka, Spark, Hadoop | CPU·Memory 집중 연산 전담 |
Node 2에서 분석 부하가 치솟더라도 Node 1의 관제 웹 서비스는 쾌적하게 동작하도록 부하를 격리(Isolation)했습니다.
완성된 시스템 아키텍처
확장성 관점에서 고려할 사항
현재 아키텍처는 단일 가상 카메라(1채널) 기준으로 자원과 파이프라인을 최적화한 구조입니다.
성공적인 PoC 이후 실제 현장에 투입되어 카메라가 1대에서 100대로 증가하는 Scale-out 상황이 발생한다면, 현재 모든 AI 연산을 담당하는 단일 Node 2는 순식간에 한계에 도달하게 됩니다.
이때 Kafka의 파티션(Partition) 분산 처리와 컨슈머 그룹(Consumer Group) 로드밸런싱을 연동하여 병목 없이 100대의 카메라 데이터를 소화하는 방법이 다음 스텝의 과제가 될 것입니다.
마치며
아키텍처 결정은 ‘무엇이 더 좋은가’가 아니라 ‘주어진 제약 안에서 무엇이 더 적합한가’의 싸움입니다.
EC2 2대라는 인프라 제약 아래, 실시간 경보의 응답성을 포기하지 않으면서 장기 통계를 안정적으로 확보하기 위해 다시 Lambda 아키텍처를 선택했습니다. 각 컴포넌트가 자신의 역할에만 집중하도록 책임을 분리한 설계가 이후 개발 속도와 안정성에 얼마나 기여하는지 지켜볼 예정입니다.
