[SWM] 실시간 이상 탐지
반려동물 건강 관리 서비스에서 가장 중요한 기능은 "위험한 순간을 놓치지 않는 것"이다.
혈당이 급격히 떨어지거나(저혈당), 급상승하는(고혈당) 순간을 실시간으로 감지하지 못하면, 반려동물이 위험에 처할 수 있다. 하지만 단순히 "혈당이 높다/낮다"만 판단하는 것으로는 부족했다.
예를 들어:
혈당 180 mg/dL → 위험한가?
평소 100이었다면 → 위험
평소 190이었다면 → 오히려 개선 중
즉, 절대적 기준(Threshold)과 상대적 변화(Trend)를 모두 고려해야 정확한 이상 탐지가 가능하다.
이 글에서는 실시간으로 혈당 이상을 감지하는 시스템을 어떻게 설계하고 구현했는지 정리했다.
🎯 요구사항 정의
탐지해야 할 이상 패턴
절대적 위험 (Threshold-Based)
저혈당: 50 mg/dL 이하
고혈당: 200 mg/dL 이상
즉각적인 알림 필요
급격한 변화 (Trend-Based)
급상승: 30분 내 30% 이상 증가
급하강: 30분 내 30% 이상 감소
스파이크 경고 필요
지속적 이상 (Duration-Based)
혈당 180 이상이 2시간 지속
혈당 60 이하가 30분 지속
만성 문제 경고
시스템 요구사항
실시간성: 데이터 수집 후 5초 이내 감지
정확도: False Positive < 5%
확장성: 동시 접속 사용자 1,000명 처리
안정성: 99.9% 가용성
🏗️ 시스템 아키텍처
💡 핵심 로직: Threshold + Trend 알고리즘
1. Threshold-Based Detection (절대값 기반)
장점:
구현 간단
즉각적 감지 가능
명확한 기준
단점:
개체별 차이 고려 안 됨
False Positive 많음 (평소 혈당이 높은 경우)
2. Trend-Based Detection (변화율 기반)
장점:
개체별 패턴 고려
급격한 변화 감지 가능
단점:
30분 데이터 필요 (초기 데이터 부족 시 작동 안 함)
계산 복잡도 높음
3. Hybrid Approach (통합 접근)
🔍 고도화: 개인별 동적 임계값
문제점
모든 반려동물에게 동일한 임계값(예: 180 mg/dL)을 적용하면:
평소 혈당이 높은 개체 → 항상 알림 (False Positive)
평소 혈당이 낮은 개체 → 위험해도 알림 안 옴 (False Negative)
해결: 개체별 베이스라인 계산
효과:
False Positive 감소: 67% → 8%
개체별 맞춤 알림
🚀 실시간 처리: Spark Streaming 구현
전체 코드
📡 Spring Boot: Redis 구독 및 WebSocket 전송
🎨 프론트엔드: 실시간 알림 표시
📊 성능 최적화
1. Spark Streaming 튜닝
python
2. Redis Pub/Sub 최적화
3. 부하 테스트 결과
🐛 트러블슈팅
문제 1: Spark Streaming 지연
증상: 5분 윈도우인데 실제로 8-10분 걸림
원인: Shuffle 파티션 수 부족 (기본 200개)
해결:
→ 처리 시간 8분 → 2분으로 단축
문제 2: Redis Pub/Sub 메시지 유실
증상: 알림이 간헐적으로 안 옴
원인: Subscriber가 처리 중일 때 새 메시지 유실
해결: Redis Streams로 변경
→ 메시지 유실률 5% → 0%
문제 3: False Positive 과다
증상: 알림이 너무 자주 옴 (하루 50건 이상)
원인: 센서 오작동으로 일시적 스파이크
해결: 연속 2회 이상 이상 시에만 알림
→ False Positive 67% → 8%
📈 개선 결과
Before (단순 Threshold)
False Positive: 67%
평균 감지 시간: 15분
놓친 이상: 23%
After (Threshold + Trend + Personalization)
False Positive: 8%
평균 감지 시간: 1.2초
놓친 이상: 2%
사용자 피드백:
"알림이 정확해서 믿을 수 있다" (92%)
"불필요한 알림이 줄었다" (88%)
🎓 배운 점
1. 단일 지표로는 부족하다
Threshold만으로는 False Positive가 너무 많았다. Trend와 개인화를 결합해야 정확도가 높아졌다.
2. 실시간 처리는 Trade-off
완벽한 정확도 vs 빠른 응답 시간 사이에서 균형을 찾아야 했다. 5분 윈도우가 최적이었다.
3. 도메인 지식이 핵심
수의사 인터뷰를 통해 "저혈당이 고혈당보다 더 위험하다"는 것을 알았고, 우선순위를 조정했다.
4. 모니터링이 필수
Spark Job 지연, Redis 메시지 유실 등은 Grafana 없이는 발견하기 어려웠다.
🚀 향후 개선 계획
1. ML 기반 이상 탐지
2. 다변량 분석
혈당뿐 아니라:
체온
심박수
활동량
모두 고려한 종합 건강 이상 탐지
3. 예측 알림
"30분 후 저혈당 위험"처럼 미리 경고
💬 마무리
실시간 이상 탐지는 단순히 "높다/낮다" 판단이 아니라, 맥락을 이해하는 시스템을 만드는 것이었다.
Threshold + Trend + Personalization 조합으로:
정확도 대폭 향상
사용자 신뢰 확보
실제 위험 순간 놓치지 않음
다음에는 ML을 도입해 더 정교한 패턴 분석을 시도할 계획이다.
관련 포스트:
Spark Streaming 실시간 처리 파이프라인 구축
Redis Pub/Sub vs Streams 비교
시계열 데이터 이상 탐지 알고리즘
Last updated