02

SOS · 생존지킴이

기능설계 표본 ② — 생명 직결 + 3주체 실시간 알림

항목내용
기능 IDFEAT-SOS
연결 시스템마스터 시스템③ 생존지킴 · ⑩ 신뢰안전망 · PART4 3주체 연결
한 줄 정의위급(수동 SOS·낙상·무응답)을 감지→확인→가족·지자체·119로 단계적 자동 호출
관련 액터PWA실벗서버가족지자체/119/SMS
설계 최우선 원칙 — 이 기능은 생명과 직결됩니다. 다른 모든 시스템(원챗·LLM·결제)이 죽어도 SOS는 자체 로직으로 반드시 동작해야 합니다. 외부 의존을 최소화하고, 모든 알림 경로를 이중·삼중화합니다.

설계 7계층 체크리스트

계층이 문서의 섹션
① UISOS 버튼·확인 카운트다운·진행 화면 / 가족 알림 화면
② FlowSOS 상태 머신 + 에스컬레이션 단계
③ Datasos_events / emergency_contacts / sos_notifications DDL
④ API발동·취소·확인응답·상태조회 엔드포인트
⑤ Seq감지→확인→에스컬레이션 시퀀스 + 이중화
⑥ Comp프론트/백엔드 + 백그라운드 워커 구조
⑦ Test검수 체크리스트 (생명 직결 — 무결성 필수)

① UI · 화면 명세

SCR-SOS-01 SOS 발동(어르신)

요소사양 · 동작
🆘 대형 빨간 버튼홈·모든 화면 접근 가능(고정). 화면 1/3 크기, 강한 대비. 길게 눌러 오작동 방지(2초)
음성 발동"도와줘 / 살려줘 / 아리야 119" 호출어 상시 감지
확인 카운트다운발동 후 "10초 후 가족에게 연락할게요" 큰 숫자 + "괜찮아요(취소)" 큰 버튼
진행 표시"딸 OO님께 연락 중…" → "동네 돌봄센터 연락 중…" 단계 실시간 표시
연결됨"OO님이 확인했어요. 곧 연락 올 거예요." 안심 메시지 + 통화 버튼

SCR-SOS-02 긴급 알림(가족)

요소사양 · 동작
풀스크린 경보앱 푸시 + 잠금화면 표시. 진동·경고음(무음모드 무시 옵션)
핵심 정보"OO님 SOS · HH:MM · 위치(지도) · 유형(수동/낙상/무응답)"
1탭 액션[전화 걸기] [확인했음] [119 함께 신고] 큰 버튼
확인 처리"확인했음" 누르면 에스컬레이션 중단 + 어르신 화면에 반영
접근성 — SOS 발동 화면은 어떤 상황(저시력·당황)에서도 인지 가능하도록 색·소리·진동·음성 4중 피드백.

② Flow · SOS 상태 머신

triggered confirming escalating acknowledged resolved
상태의미전이
triggeredSOS 발동(버튼/음성/감지)→ confirming
confirming10초 취소 대기(오작동 방지)취소→resolved(false alarm) / 만료·즉시→escalating
escalating연락처 우선순위대로 순차 호출누군가 ack→acknowledged / 전원 무응답→119/지자체
acknowledged가족/돌봄 확인함상황 종료→resolved
resolved종료(해결/오작동)(기록·리포트)

에스컬레이션 단계 (단계별 자동 상향)

0~10초
confirming — "괜찮아요" 누르면 즉시 중단. 무응답 시 다음 단계
~1분
가족 1순위 푸시+SMS+전화 시도. ack 없으면 다음
~2분
가족 2~N순위 + 지자체 돌봄 대시보드 경보
~3분
119/긴급기관 연계(위치·기저질환 정보 전달). 동시 전원 재알림
낙상·무응답 자동감지 분기 — 자동 감지(triggered by sensor)는 confirming에서 음성 확인("괜찮으세요?")을 먼저 시도하고, 응답 없을 때만 escalating. 수동 버튼은 더 빠른 임계 적용.

③ Data · DB 스키마

기존 p25 sos_events·emergency_contacts 재활용 + 알림 추적 sos_notifications 신설.

sbdb · DDL
-- 긴급 연락처 (우선순위) CREATE TABLE emergency_contacts ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, name VARCHAR(50), phone VARCHAR(20) NOT NULL, relation VARCHAR(20), priority TINYINT DEFAULT 1, -- 1이 최우선 channel SET('push','sms','call') DEFAULT 'push,sms,call', INDEX idx_user_pri (user_id, priority), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- SOS 이벤트 CREATE TABLE sos_events ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, source ENUM('manual','voice','fall','no_response') NOT NULL, status ENUM('triggered','confirming','escalating','acknowledged','resolved') DEFAULT 'triggered', lat DECIMAL(10,7), lng DECIMAL(10,7), resolution ENUM('real','false_alarm','unknown') NULL, ack_by BIGINT NULL, -- 확인한 사용자 id triggered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, resolved_at TIMESTAMP NULL, INDEX idx_user_status (user_id, status), FOREIGN KEY (user_id) REFERENCES users(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ★ 신설: 알림 발송 추적 (이중화 보장·감사) CREATE TABLE sos_notifications ( id BIGINT PRIMARY KEY AUTO_INCREMENT, sos_id BIGINT NOT NULL, contact_id BIGINT NULL, -- 119는 NULL channel ENUM('push','sms','call','gov','119'), step TINYINT, -- 에스컬레이션 단계 status ENUM('sent','failed','delivered','acked') DEFAULT 'sent', sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_sos (sos_id), FOREIGN KEY (sos_id) REFERENCES sos_events(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
설계 포인트 — sos_notifications가 "누구에게·어떤 채널로·성공했는지"를 모두 기록 → 무응답 판정과 이중화 재시도, 사후 감사의 근거.

④ API · 엔드포인트 명세

M경로설명
POST/api/v1/sos/triggerSOS 발동(수동/음성/감지) → 이벤트 생성
POST/api/v1/sos/{id}/cancelconfirming 중 어르신 취소(오작동)
POST/api/v1/sos/{id}/ack가족/돌봄 "확인했음" → 에스컬레이션 중단
GET/api/v1/sos/{id}/status진행 상태 폴링(또는 WSS 구독)
WSS/ws/sos/{id}상태 실시간 푸시(어르신·가족 화면)
POST /api/v1/sos/trigger — 요청
{ "source": "manual", // manual | voice | fall | no_response "lat": 37.6512, "lng": 127.0473, "confirm_window": 10 // 취소 대기초(감지원은 음성확인 후) }
응답
{ "success": true, "data": { "sos_id": 9001, "status": "confirming", "confirm_until": 1718000010, "ws": "wss://sb.kiam.kr/ws/sos/9001?t=..." } }
멱등성·중복방지 — 동일 user의 미해결 SOS가 있으면 새로 만들지 않고 기존 이벤트를 반환(따닥 클릭·동시 감지 대비).

⑤ Seq · 에스컬레이션 시퀀스 + 이중화

PWA
버튼/음성/감지 → POST /sos/trigger → 상태 confirming, WSS 구독
실벗서버
이벤트 저장 → 백그라운드 EscalationWorker 예약(10초 후 시작)
confirming
어르신 "괜찮아요" → /cancel → resolved(false_alarm) 종료. (감지원은 음성확인 병행)
Worker step1
escalating. 1순위 연락처에 push+SMS+call 동시 발송 → sos_notifications 기록
가족
알림 수신 → "확인했음" /ackacknowledged, Worker 중단, 어르신 화면에 안심 표시
Worker step2/3
ack 없으면 단계 상향: 2~N순위 + 지자체 → 최종 119/긴급기관(위치·기저질환 전달)
resolved
상황 종료 기록 → 가족·지자체에 결과 리포트(① 안심보고 연계)

이중화·견고성

위험대응
푸시 미수신push+SMS+전화 동시 멀티채널. delivered 콜백 없으면 다음 채널 강제
실벗 서버 다운단말 측 폴백: 등록된 1순위에게 단말이 직접 SMS/전화 시도(서버 무관)
Worker 장애이벤트 status·타임스탬프 기반 크론 복구 스캐너가 미처리 escalating 재구동
위치 불명마지막 알려진 위치 + 등록 주소 fallback 전달
절대 원칙 — SOS 경로는 원챗·DeepSeek·결제 등 어떤 외부 모듈에도 의존하지 않는다. 순수 실벗 서버 + 단말 + 통신사 채널만으로 완결된다.

⑥ Comp · 컴포넌트 / 모듈 구조

프론트엔드 (PWA)

features/sos/ ├─ SosButton # 전역 고정 대형 버튼(롱프레스) ├─ SosConfirmView # SCR-SOS-01 카운트다운·취소 ├─ SosProgressView # 에스컬레이션 진행 표시(WSS) ├─ VoiceTrigger.js # "도와줘" 호출어 상시 감지 ├─ FallDetector.js # 가속도센서 낙상 추정 └─ offlineSos.js # 서버 불가 시 단말 직접 SMS/전화 family/ └─ SosAlertView # SCR-SOS-02 가족 풀스크린 경보

백엔드 (PHP)

app/ ├─ Controllers/SosController.php # trigger/cancel/ack/status ├─ Services/ │ ├─ SosService.php # 상태머신·멱등성 │ ├─ EscalationWorker.php # 단계별 발송(큐/잡) │ ├─ NotifyGateway.php # push/SMS/call 멀티채널 추상화 │ └─ GovBridge.php # 지자체/119 연계 ├─ Jobs/SosEscalationJob.php # 지연 실행 잡 ├─ Cron/SosRecoveryScanner.php # 미처리 이벤트 복구 └─ Models/ SosEvent EmergencyContact SosNotification
모듈책임
SosService상태 전이의 단일 진실원천. 멱등성·중복 SOS 병합
EscalationWorker단계별 타이머·발송·ack 감시. 핵심 안전 로직
NotifyGateway채널 추상화 + 실패 시 자동 다음 채널. 모든 발송 sos_notifications 기록
SosRecoveryScanner장애로 멈춘 escalating 이벤트를 주기적으로 되살림(안전망의 안전망)
표본 2종 완성 — 가장 복잡한 기능(아리 대화)과 가장 위험한 기능(SOS)을 6계층으로 끝까지 설계했습니다. 이 틀이 작동함을 증명했으니, 나머지 10개 시스템도 동일 틀(UI→Flow→DB→API→Seq→Comp)로 확장하면 됩니다.

⑦ Test · 검수 체크리스트

생명 직결 — SOS는 단 한 번의 실패도 허용되지 않는다. 아래 시나리오는 전수 통과 + 회귀 테스트 의무화 대상이며, 외부 모듈 장애 상황(T-07~T-11)이 핵심이다.

정상 흐름 (Happy Path)

ID시나리오기대 결과
T-01빨간 SOS 버튼 2초 롱프레스확인 카운트다운(10초) → 자동 발동, sos_events 생성(status=triggered→escalating)
T-02음성 "도와줘" 발화버튼과 동일하게 SOS 발동 (source=voice)
T-03카운트다운 중 "취소" 탭발동 중단, status=resolved(cancelled), 알림 미발송
T-04가족 단말에 긴급 알림 수신풀스크린 경보 + 위치·1탭 전화/119/확인 액션 표시
T-05가족이 "확인" 탭sos_notifications.status=acked, 어르신 화면 "가족이 오고 있어요" 표시, 에스컬레이션 중단

에스컬레이션 / 장애 견고성 (필수)

ID시나리오기대 결과
T-06가족 1순위 무응답 1분 경과가족 2~N순위 + 지자체로 자동 확대(step 증가), 2분 후 119
T-07FCM 푸시 미수신(앱 미실행)SMS 게이트웨이로 이중 발송 — 채널 다중화로 도달 보장
T-08실벗 서버 다운 중 SOS 발동단말 offlineSos.js가 직접 SMS·전화 발신 (서버 무의존 폴백)
T-09Service Worker/백그라운드 워커 장애SosRecoveryScanner가 미완료 이벤트 재스캔·재발송 (유실 0)
T-10GPS 위치 불명최근 알려진 위치 + 주소(프로필) 대체 전송, "위치 부정확" 표시
T-11같은 사용자가 SOS 연속 2회 발동멱등키로 중복 이벤트 차단 — 1건만 처리, 중복 알림 없음
완료 판정 — T-01~T-11 전수 통과해야 FEAT-SOS 출시 가능. T-07·T-08·T-09(이중화·서버무의존·자가복구)는 면제 불가 — "한 번 실패 = 사람이 죽을 수 있다"는 기준으로 검수한다.
표본 2종 최종 완성 — 가장 복잡한 기능(아리 대화)과 가장 위험한 기능(SOS)을 7계층(UI→Flow→DB→API→Seq→Comp→Test)으로 끝까지 설계했습니다. 이 틀이 작동함을 증명했으니, 나머지 10개 시스템도 동일 틀로 확장합니다.