Hospital-at-Home Risk Monitor Scenario
이 문서는 source-repo 예제가 향할 대표 제품 시나리오를 정의합니다. 목적은 하나의 완성된 상용 제품을 설계하는 것이 아니라, 팀원이 참고하고 실행할 수 있는 최소 예제를 기준으로 polyglot monorepo의 app, package, firmware, CI/CD, Nexus, container, 품질 경계를 정렬하는 것입니다.
이 시나리오는 병원 밖 또는 병원-가정 연계 환경에서 환자 생체신호와 문진, 임상 metadata를 수집하고, 조기 악화 위험도를 계산해 의료진이 먼저 확인해야 할 환자를 선별하는 흐름을 가정합니다. Dev-guide 예제는 실제 장비, 실제 환자 데이터, 실제 임상 성능 claim 없이 이 흐름의 핵심 경계만 작게 구현합니다.
상세 데이터 계약과 workflow 실행 기준은 별도 문서에서 관리합니다.
| Document | Purpose |
|---|---|
../architecture/contracts.md |
MQTT, signal packet, preprocessing, risk assessment, review queue, runtime policy contract |
clinical-workflow.md |
signal acquisition부터 monitoring dashboard, alarm audit, runtime drift까지 이어지는 제품 workflow |
../getting-started/local-development.md |
local에서 toolchain을 준비하고 demo를 실행하는 방법 |
1. 배경
1-1. 왜 이 시나리오인가
미국과 유럽에서는 remote patient monitoring, hospital-at-home, digital health application, AI-enabled medical device 흐름이 이미 크게 연구되고 있습니다. 특히 고령화, 만성질환, 의료진 부족, 병상 부족, 퇴원 후 관리 비용 증가 때문에 병원 밖 환자 상태를 지속적으로 보고 위험도를 선별하는 기술 수요가 큽니다.
한국에서도 병원 연구와 PoC는 충분히 가능하지만, 원격 모니터링 수가, 병원 밖 데이터 활용, 의료진 workflow 통합, SaMD 품질 체계까지 한 번에 제품화하는 사례는 아직 제한적입니다. 그래서 dev-guide 예제로 쓰기에 적합합니다. 너무 먼 연구 주제가 아니라 실제 시장이 있고, 동시에 내부적으로 architecture와 품질 기준을 제대로 잡아야 하는 문제입니다.
1-2. 이 시나리오가 보여주는 것
이 시나리오는 각 언어가 같은 기능을 반복 구현하지 않고, 실제 제품 안에서 자연스럽게 맡을 수 있는 책임을 최소 실행 예제로 나눠 가집니다.
| Area | Example responsibility |
|---|---|
| Device/Firmware | 환자 주변 장치가 vitals와 event를 생성하고 전송합니다. |
| Signal processing | Rust preprocessing으로 raw-like signal을 feature로 변환합니다. |
| AI/ML backend | Python API가 ingestion, rule hybrid, policy, review queue를 조합합니다. |
| Clinician UI | TypeScript dashboard가 review queue, evidence, data quality를 확인합니다. |
| Platform automation | Go minimal operator가 RiskMonitor desired state와 status를 reconcile합니다. |
| CD/GitOps | 환경별 배포, promotion, rollback, audit trail을 관리합니다. |
1단계 예제에서 실제로 연결하는 흐름은 아래로 제한합니다.
synthetic device signal
-> HTTP /signal-packets ingestion in local demo
-> app-owned preprocessing adapter called by Python API
-> Python risk assessment and alarm policy
-> TypeScript monitoring dashboard
MQTT는 2차 inbound adapter로 추가합니다. 추가하더라도 HTTP path와 같은 ingest_signal_packet usecase를 호출해야 하며, 별도 business flow를 만들지 않습니다. Go는 이 end-to-end path에 직접 참여하지 않고, RiskMonitor CRD를 감시해 status를 갱신하는 minimal Kubernetes operator 예제로 제공합니다.
1-3. 이 시나리오가 지금 풀지 않는 것
이 repo는 실제 진단이나 치료 결정을 자동화하는 제품으로 배포하지 않습니다. 예제는 engineering guide이며, 임상 성능 주장, 인허가 제출 자료, 병원 EMR 연동, 실제 PHI 처리, 상용 medical device claim은 범위 밖입니다.
범위 밖 항목은 명확히 둡니다.
- 실제 환자 PHI 저장과 전송
- 진단 확정 또는 치료 지시 자동화
- 실제 의료기기 인허가 제출 문서 전체
- 병원 EMR/PACS/LIS 상용 연동
- 실제 임상 성능 claim
- 의료진 alert fatigue를 평가하는 임상시험
- 실제 PCB hardware driver
- HIL 테스트
- production MQTT security 전체 구현
- 실제 모델 학습 pipeline
- production-grade Kubernetes operator
- KubeVirt 기반 device VM
- Rust preprocessing network service
- Node API/BFF
2. 제품 시나리오
2-1. 사용자와 사용 환경
주 사용자는 원격 모니터링 팀, 병동 또는 외래 의료진, care coordinator입니다. 환자는 퇴원 후 회복 중이거나, 만성질환 악화 위험이 있거나, 항암치료 또는 수술 후 부작용 모니터링이 필요한 상태를 가정합니다.
의료진은 모든 환자의 raw data를 직접 보지 않습니다. 시스템은 신호 품질, trend, baseline deviation, patient-reported symptoms, clinical metadata를 조합해 review priority와 alarm state를 만들고, 의료진은 waveform, numeric, evidence, alarm을 함께 보며 우선순위가 높은 환자를 먼저 확인합니다.
2-2. 대표 workflow
대표 workflow는 아래처럼 둡니다.
synthetic signal generator
-> HTTP /signal-packets
-> Python API
-> app-owned preprocessing adapter
-> Python core clinical result builder
-> alarm policy
-> TypeScript clinician monitoring dashboard
첫 단계에서는 실제 hardware나 병원 시스템 없이도 local demo가 동작해야 합니다. Firmware 예제는 synthetic sample과 packet contract를 만들고, signal-device-simulator가 HTTP endpoint로 packet을 전송하며, backend는 deterministic rule hybrid를 사용합니다. MQTT publish/subscribe, KubeVirt, edge gateway, real model serving은 1차 예제 범위 밖입니다.
2-3. 입력 데이터
초기 예제는 PHI가 없는 synthetic data를 사용합니다. 데이터는 실제 임상값과 단위의 형태를 닮되, 특정 환자를 재식별할 수 없어야 합니다.
| Data type | Example |
|---|---|
| Vitals | heart rate, respiratory rate, SpO2, blood pressure, temperature |
| Wearable signal | PPG-like pulse waveform, accelerometer activity, signal quality |
| Patient-reported outcome | dyspnea, dizziness, pain, fatigue, nausea |
| Clinical metadata | age band, diagnosis group, recent discharge, treatment phase |
| Model metadata | model version, horizon, threshold policy |
| Operational context | device id, site id, request id, ingestion timestamp |
2-4. 출력 데이터
시스템 출력은 “진단 결과”가 아니라 “의료진 검토 우선순위, 생체신호 상태, 알람 상태와 근거”입니다. 제품 언어는 SaMD에서 검토 가능한 형태로 둡니다.
| Output | Meaning |
|---|---|
riskLevel |
low, moderate, high, urgent |
riskScore |
calibrated risk score or normalized priority score |
riskHorizon |
6h, 24h, 72h 같은 예측 시간 범위 |
reviewRequired |
의료진 확인 필요 여부 |
evidence |
어떤 signal feature와 symptom이 판단에 기여했는지 |
dataQuality |
missingness, artifact, signal confidence |
recommendedAction |
예제 수준의 review recommendation |
auditContext |
model version, policy version, request id |
monitoringSnapshot |
waveform, numeric, risk, alarm을 묶은 frontend runtime contract |
alarmState |
visual/audible alarm, acknowledge, mute state |
2-5. 임상의가 보는 화면
TypeScript web app은 단순 landing page가 아니라 의료진이 실제로 확인할 수 있는 monitoring dashboard를 보여줍니다. 첫 화면은 환자 리스트, waveform, numeric vital, 위험도, alarm state, trend, signal quality, 주요 evidence를 보여주고, 상세 화면에서는 raw-like trend와 모델/정책 metadata를 확인할 수 있어야 합니다.
초기 버전에서는 실제 EMR처럼 복잡하게 만들지 않습니다. 대신 다음 질문에 답할 수 있어야 합니다.
- 지금 먼저 봐야 할 환자는 누구인가
- 현재 waveform과 numeric이 어떤 상태인가
- 어떤 alarm을 처리해야 하는가
- 왜 이 환자가 high priority인가
- 데이터 품질이 충분한가
- 어떤 model/policy version이 판단에 사용됐는가
- false positive나 noisy signal을 의심할 근거가 있는가
3. 언어별 책임
상세 구현 기준은 언어별 문서에 둡니다. 이 문서는 제품 맥락과 전체 흐름을 설명하는 허브 역할을 합니다.
| Area | Implementation guide |
|---|---|
| C/Firmware device boundary | ../languages/c-firmware.md |
| Rust signal preprocessing | ../languages/rust.md |
| Python risk assessment backend | ../languages/python.md |
| TypeScript clinician dashboard | ../languages/typescript-react.md |
| Go platform automation | ../languages/go.md |
3-1. C/Firmware
C/Firmware 예제는 device boundary를 보여줍니다. 실제 sensor driver가 없어도, firmware app은 sample acquisition, buffering, simple quality flag, event packaging 구조를 가집니다.
Bare-metal 예제는 board-independent C core와 firmware app의 경계를 보여줍니다. Zephyr 예제는 RTOS thread, queue, native simulation, firmware artifact generation을 보여줍니다. C/Firmware의 핵심은 measurement integrity와 embedded artifact입니다.
기준은 아래와 같습니다.
patient-signal-c-core는 board-independent signal domain과 usecase를 제공합니다.- C core는 fixed-size window summary, signal quality flag, calibration metadata boundary,
SignalPacketbuild를 실제로 수행합니다. wearable-firmware는 bare-metal style app으로.elf/.hex/.bin/.map산출물을 만듭니다.wearable-zephyr-firmware는 Zephyr native_sim에서 producer/consumer queue 흐름을 보여줍니다.- firmware 산출물은 Nexus raw repository에 release evidence로 보관합니다.
3-2. Rust
Rust는 heavy signal preprocessing과 invalid state 방지에 집중합니다. 예제는 raw sample window를 받아 artifact 제거, signal quality 계산, feature extraction, baseline deviation 계산을 수행합니다.
Rust가 맡을 책임은 아래와 같습니다.
- time-series window validation
- missingness와 artifact detection
- SpO2 drop count, heart rate trend, motion burden 같은 feature extraction
- preprocessing result의 typed contract 제공
- typed error와 fixture-based test
- 성능 민감한 Rust library 형태 제공
1단계에서는 Rust를 별도 HTTP service로 띄우지 않습니다. pure Rust core와 Python API preprocessing adapter boundary를 고정하고, PyO3/maturin binding package를 app-owned port 뒤에 연결합니다. 1차 demo runtime은 Rust Python package를 기본값으로 사용하고, Rust core와 Python Rust binding 검증을 같은 fixture 기준으로 수행합니다.
3-3. Python
Python은 AI/ML backend와 clinical workflow interface에 집중합니다. HTTP ingestion, Rust Python package 선택 경계, deterministic rule hybrid, policy config, review queue repository를 조합해 risk assessment를 반환합니다.
Python이 맡을 책임은 아래와 같습니다.
- HTTP risk assessment API
- HTTP signal packet ingestion adapter
- typed request/response schema
- demo model metadata와 threshold policy config 관리
- synthetic clinical context repository
- in-memory review queue repository
- structured audit context
- unit/integration test로 model orchestration 검증
1단계 모델은 deterministic rule hybrid로 둡니다. 중요한 것은 모델 크기가 아니라, demo model metadata와 clinical policy가 usecase boundary 안에서 명확히 추적되는 것입니다. 실제 ML training pipeline과 복잡한 model registry는 범위 밖입니다.
3-4. TypeScript
TypeScript는 clinician-facing GUI와 frontend contract를 보여줍니다. Web app은 Python API를 호출하고, waveform, numeric, risk, alarm, review queue와 patient detail을 표시합니다. TypeScript package는 UI가 사용하는 typed contract, view model, API client boundary, clinical component state를 제공합니다.
TypeScript가 맡을 책임은 아래와 같습니다.
- React/Vite clinician monitoring dashboard
- Python API client와 API response -> UI view model 변환
- waveform panel, numeric tile, risk score card
- visual alarm banner, alarm indicator, alarm audio controller
- evidence panel과 data quality warning
- loading, error, empty, stale state 표현
- Storybook으로 주요 상태 문서화
- UI test와 contract test
1단계에서는 Node API/BFF를 만들지 않습니다. Web app의 runtime은 Python API를 기준으로 동작하고, fixture/mock은 Storybook과 test에 사용합니다.
3-5. Go
Go는 Kubernetes operator와 platform automation 예제를 담당합니다. 1단계에서는 production-grade operator가 아니라, RiskMonitor CRD를 감시하고 status를 갱신하는 minimal operator를 보여줍니다.
Go가 맡을 책임은 아래와 같습니다.
RiskMonitorCRD spec/status 예시- model version, threshold, runtime config reconcile
- deployment image tag와 config drift 확인
- rollout policy와 health status 관리
- CD repo와 연결되는 platform contract 표현
초기 단계에서는 controller-runtime 기반 reconcile loop와 envtest/fake client test를 둡니다. 실제 model rollout, Argo CD 조작, webhook, multi-cluster, production HA controller는 이 예제 범위 밖입니다.
4. 아키텍처 경계
4-1. Domain
Domain은 제품 언어를 보존합니다. RiskAssessment, SignalQuality, PreprocessedWindow, ReviewPriority, ClinicalEvidence 같은 이름은 framework나 storage보다 오래 살아야 합니다.
Domain은 pure해야 합니다. 같은 입력이면 같은 출력이어야 하며, 시간, network, DB, logging, random, framework에 의존하지 않습니다.
4-2. Usecase
Usecase는 stateless application flow입니다. 요청 단위 입력과 port dependency를 받아 domain rule을 호출하고 결과를 반환합니다. Usecase는 container를 직접 조회하지 않고, concrete adapter도 알지 않습니다.
이 시나리오의 핵심 usecase 예시는 아래와 같습니다.
ingest_signal_samplepreprocess_signal_windowassess_patient_riskprioritize_clinician_reviewreconcile_risk_monitor_runtime
4-3. Ports
Ports는 바깥 세계와의 계약입니다. Usecase는 port를 통해 외부 정보를 얻거나 결과를 내보냅니다.
대표 port는 아래와 같습니다.
ClinicalContextRepositorySignalWindowRepositoryModelRegistryRiskPolicyRepositoryReviewQueuePublisherAuditEventSinkDeviceSamplePublisher
4-4. Adapters
Adapters는 framework, protocol, storage, device driver를 usecase contract로 변환합니다. Inbound adapter는 HTTP, CLI, worker, firmware thread처럼 외부 입력을 받아 usecase를 호출합니다. Outbound adapter는 repository, model registry, queue, log sink, device driver를 구현합니다.
초기 예제는 mock adapter를 사용할 수 있습니다. 단, mock adapter도 domain language를 훼손하지 않아야 합니다. MockDB라는 이름보다 DemoClinicalContextRepository, SyntheticSignalWindowRepository처럼 역할이 드러나는 이름을 사용합니다.
5. 1단계 구현 범위
5-1. 현재 guide가 고정하는 범위
이 문서에서는 시나리오 관점에서 1단계 범위를 요약합니다. 실제 contract는 Contracts, 실행 방법은 Local Development와 Demo Flow를 따릅니다.
- C/Firmware 예제가 synthetic signal packet을 생성합니다.
signal-device-simulator가 packet을 HTTP endpoint로 전송합니다.- Python API가 packet을 받고 같은 ingestion usecase를 검증합니다.
- Python API가 app-owned preprocessing port를 호출해 sample window를 feature로 변환하고, 그 결과를 Python core에 넘깁니다.
- Python risk API가 feature와 synthetic clinical context로 risk assessment를 반환합니다.
- TypeScript web app이 review queue와 patient detail을 보여줍니다.
- Go app은
RiskMonitorCRD를 감시하는 minimal operator를 별도 예제로 제공합니다. - CI는 package test, app test, container build, firmware build를 분리해 검증합니다.
6. Dev Guide 적용 원칙
6-1. 껍데기 예제를 피한다
이 scenario로 전환한 뒤에는 hello나 단순 scaffold 수준의 app을 남기지 않습니다. 모든 언어 예제는 최소한 자기 책임 안에서 실제 동작을 해야 합니다. 단순 mock이라도 product language와 architecture boundary를 보여줘야 합니다.
6-2. 모든 언어가 같은 일을 하지 않는다
Polyglot guide의 목적은 같은 API를 여러 언어로 반복 작성하는 것이 아닙니다. 각 언어의 강점과 팀이 실제로 사용할 가능성이 높은 책임을 기준으로 예제를 둡니다.
6-3. SaMD claim은 보수적으로 둔다
예제는 clinical decision support와 engineering architecture를 보여줍니다. 문서와 UI는 실제 진단/치료 claim처럼 읽히지 않게 작성합니다. 위험도 결과는 의료진 검토를 돕는 priority와 evidence로 표현합니다.
6-4. 품질 evidence를 코드와 함께 남긴다
의료 AI 제품에서는 “동작한다”만으로 부족합니다. 어떤 version의 source, model, policy, container, firmware가 어떤 test를 통과했는지 추적할 수 있어야 합니다. 이 repo의 CI/CD와 Nexus 흐름은 이 evidence를 남기는 예제로 유지합니다.