
SOFTMOA TECHNOLOGY
냉장 보관 온도 이탈 알림 시스템 구축 방법
소프트모아가 정리한 기술 기록입니다.
Contents
냉장고와 냉동고 온도를 센서로 받기 시작하면 다음 문제는 알림입니다. 문을 잠깐 열 때마다 울리면 아무도 보지 않고, 기준을 느슨하게 잡으면 실제 이탈을 놓칩니다. 결론부터 말하면 온도 이탈 알림 시스템은 측정값 하나가 아니라 이탈 구간을 단위로 판단하고, 기록이 끊긴 센서를 정상과 별도 상태로 보여 줘야 합니다. 알림 확인과 제품 판정도 서로 다른 단계로 나눠야 합니다.
기준값은 화면보다 먼저 정합니다
아래 숫자는 설명을 위한 예시입니다. 실제 허용 범위와 지연 시간은 보관하는 제품의 규격, 적용되는 법령, 장비의 회복 시간을 근거로 품질 담당자가 정해야 합니다. 일반 냉장고 설정 온도를 그대로 가져다 쓰면 안 됩니다.
- 허용 범위: 2.0℃ 이상 8.0℃ 이하. 8.0℃는 정상이고 8.1℃부터 이탈입니다.
- 알림 지연: 이탈이 30분(1,800초) 이상 이어질 때 담당자에게 알림을 보냅니다.
- 수신 끊김: 마지막 측정 후 15분(900초)이 지나도 새 값이 없으면 '기록 없음'으로 표시합니다.
- 측정 간격: 5분. 간격이 길수록 짧은 이탈은 기록에 나타나지 않습니다.
경계값을 '초과'로 볼지 '이상'으로 볼지 문서에 적어 두지 않으면 같은 8.0℃를 화면과 보고서가 다르게 판정합니다.
담당자가 보는 처리 흐름
- 수신: 센서 또는 게이트웨이가 센서 ID, 측정 시각, 온도를 보냅니다. 원본 측정값은 수정하지 않고 쌓기만 합니다.
- 현황 화면: 장비별로 '정상', '이탈 진행 중', '알림 발송', '기록 없음' 네 가지 상태를 보여 줍니다.
- 알림 확인: 현장 담당자가 문 닫힘, 전원, 성에 같은 확인 내용을 남깁니다. 확인 버튼은 알림 상태만 바꾸고 측정값은 바꾸지 않습니다.
- 제품 판정: 품질 담당 권한을 가진 사람만 해당 시간대 제품을 '보류', '사용', '폐기'로 판정합니다. 판정 전 제품은 보류로 둡니다.
이탈 구간을 묶는 SQL
예시로 센서 4개(F01~F04)의 측정값 30건을 넣고, 09:40 기준으로 조회했습니다. 아래 SQL은 이 서버의 SQLite 3.54.0에서 실행해 확인했습니다. LAG 같은 윈도 함수는 SQLite 3.25.0부터 지원됩니다.
WITH p AS (SELECT 2.0 AS low_c, 8.0 AS high_c, 1800 AS delay_s,
'2026-11-01 09:40:00' AS report_at),
f AS (SELECT r.*, (r.temp_c < p.low_c OR r.temp_c > p.high_c) AS is_out
FROM readings r CROSS JOIN p),
m AS (SELECT *, LAG(is_out, 1, 0) OVER
(PARTITION BY sensor_id ORDER BY read_at) AS prev_out FROM f),
g AS (SELECT *, SUM(is_out = 1 AND prev_out = 0) OVER
(PARTITION BY sensor_id ORDER BY read_at) AS ep FROM m),
e AS (SELECT sensor_id, MIN(read_at) AS started_at,
MAX(read_at) AS last_out_at, COUNT(*) AS out_readings
FROM g WHERE is_out = 1 GROUP BY sensor_id, ep),
c AS (SELECT e.*, (SELECT MIN(r.read_at) FROM readings r
WHERE r.sensor_id = e.sensor_id
AND r.read_at > e.last_out_at) AS recovered_at FROM e)
SELECT c.sensor_id, c.started_at, c.recovered_at, c.out_readings,
unixepoch(COALESCE(c.recovered_at, p.report_at))
- unixepoch(c.started_at) AS duration_s,
CASE WHEN unixepoch(COALESCE(c.recovered_at, p.report_at))
- unixepoch(c.started_at) >= p.delay_s THEN 'ALERT'
WHEN c.recovered_at IS NULL THEN 'OPEN'
ELSE 'SHORT' END AS status
FROM c CROSS JOIN p ORDER BY c.sensor_id, c.started_at;
sensor_id started_at recovered_at out_readings duration_s status
F01 2026-11-01 09:00:00 2026-11-01 09:30:00 6 1800 ALERT
F02 2026-11-01 09:00:00 2026-11-01 09:29:59 6 1799 SHORT
F04 2026-11-01 09:35:00 1 300 OPEN
F01과 F02는 똑같이 09:00에 8.0℃를 넘었습니다. F01은 정확히 30분 뒤에 돌아와 알림 대상이 되었고, F02는 1초 먼저 돌아와 짧은 이탈로 남았습니다. 알림이 없었다고 이탈이 없었던 것은 아니므로 SHORT 구간도 목록에 남겨 반복 여부를 봐야 합니다. F04는 1.8℃로 낮은 쪽 이탈이 진행 중이며, 조회 시점까지 5분이라 아직 알림 전입니다. F04의 09:05 측정값 8.0℃는 기준에 따라 이탈로 세지 않았습니다.
조회 결과에 없는 F03
F03은 위 결과에 한 줄도 나오지 않습니다. 이탈이 없어서가 아니라 08:40 이후 값이 들어오지 않았기 때문입니다. 마지막 측정 시각만 따로 보는 조회를 함께 둬야 이 상태가 드러납니다.
SELECT sensor_id, MAX(read_at) AS last_read_at,
unixepoch('2026-11-01 09:40:00') - unixepoch(MAX(read_at)) AS silent_s
FROM readings GROUP BY sensor_id;
결과에서 F03의 silent_s는 3,600초로 기준 900초를 넘고, 나머지 세 센서는 300초입니다. 화면에는 F03을 녹색이 아닌 '기록 없음'으로 표시하고 배터리, 통신, 전원을 점검하도록 따로 알려야 합니다.
로거가 내부에 저장했던 F03 측정값 10건을 나중에 올렸다고 가정하고, 복사한 데이터에서 같은 SQL을 다시 실행했습니다. F03에서 08:50부터 09:25까지 2,100초 이탈이 새로 나타났고 상태는 ALERT였습니다. 이탈은 08:50에 시작했고 09:20에 이미 30분 기준을 채웠지만, 알림은 기록이 올라온 뒤에야 보낼 수 있습니다. 그래서 알림 이력에는 이탈 시작 시각과 시스템이 발견한 시각을 따로 저장합니다. 원본 30건이 들어 있던 데이터는 그대로였고, 다른 센서의 결과도 바뀌지 않았습니다.
기록이 말해 주지 않는 것
- 센서는 대개 공기 온도를 잽니다. 공기가 30분 넘게 8℃를 넘었다는 기록이 곧 제품 온도가 그랬다는 뜻은 아닙니다. 센서 위치와 제품 상태는 현장에서 따로 확인합니다.
- duration_s는 측정 간격으로 잘린 추정치입니다. 실제로 기준을 넘은 순간은 직전 정상 측정과 첫 이탈 측정 사이 어딘가입니다.
- 알림 발송과 담당자 확인은 조치가 끝났다는 증거가 아닙니다. 판정 권한과 판정 결과를 별도 기록으로 남겨야 감사 때 설명할 수 있습니다.
소프트모아의 서비스와 포트폴리오는 softmoa.com에서 확인할 수 있습니다.
소프트모아는 해당 시스템을 구축합니다. 문의하기
