5P by GN⁺ | ★ favorite | 댓글 1개
  • 2020년 1형 당뇨병 진단을 받은 엔지니어가 혈당 관리를 장애 대응처럼 다루기 위해 Go 기반 개인 모니터링 시스템을 만듦
  • Libre에 공식 API나 SDK가 없어 직접 연동하기 어려웠지만, Miao Miao와 Tomato 앱을 거쳐 2분마다 혈당 데이터를 외부 엔드포인트로 보내는 경로를 찾음
  • Go echo server와 Encore 배포로 Tomato 요청 형식을 확인하고, SGV 값을 변환해 Grafana 대시보드에 실시간 혈당 게이지로 표시함
  • Telegram 봇은 식사·운동·인슐린 주입 같은 맥락 주석을 남기고, 5분마다 실행되는 cron은 혈당이 기준치 아래로 떨어지면 incident.io 인시던트를 생성함
  • 알림, 에스컬레이션, 리포트를 붙인 이 워크플로는 낮은 혈당이나 측정 공백을 혼자 감당하지 않게 해주는 개인 건강 관측성 시스템으로 동작함

1형 당뇨병을 장애 대응 문제로 다루기

  • 1형 당뇨병은 췌장이 탄수화물을 에너지로 바꾸는 데 필요한 인슐린을 거의 또는 전혀 만들지 못하는 자가면역 질환임
  • 인슐린은 주사로 보충해야 하며, 주입량은 먹는 음식과 여러 변수에 따라 달라짐
  • 1형은 생활습관 때문에 생기는 병이 아니며, 현재 치료법은 없음
  • 혈당은 계속 모니터링해야 함
    • 너무 높은 상태가 오래 지속되면 장기 손상과 수명 단축으로 이어질 수 있음
    • 너무 낮은 상태는 짧은 시간에도 치명적일 수 있음
    • 혈당이 너무 낮아지면 안전하게 먹거나 마실 수 없어 다른 사람의 도움이 필요할 수 있음
  • 음식이나 음료를 볼 때마다 탄수화물 양과 필요한 인슐린 양을 계산해야 하고, 짧은 산책 같은 운동도 체내 활성 인슐린 양을 고려해야 함
  • 혈당 가시성이 사라지는 상황은 업무 시스템에서 관측성이 0이 되는 상태와 비슷하게 다룰 수 있음
    • 업무라면 인시던트를 선언하고, 가시성이 복구될 때까지 닫지 않음
    • 낮은 혈당은 무언가 잘못되고 있다는 첫 신호이며 즉시 조치가 필요한 상태임

Libre, Miao Miao, Tomato로 데이터 경로 찾기

  • 팔에 부착하는 Libre 기기는 휴대폰을 가까이 대면 혈당을 읽을 수 있음
    • 기기는 2주마다 교체해야 함
    • 혈당이 특정 빨간 선 아래로 내려가면 탄수화물을 섭취해야 함
    • 초록 범위를 벗어나면 인슐린 투여 등 조치를 고려할 수 있음
  • 운동, 체중 증가, 질병은 인슐린 민감도를 바꾸기 때문에 혈당 관리는 계속 움직이는 목표가 됨
  • Libre가 오류를 보이면 기기에서 혈당을 읽을 수 없고 위험 수준 알림도 받을 수 없음
  • Libre는 폐쇄형 기기라 API나 SDK 없이 직접 데이터를 가져오기 어려움
  • Miao Miao는 Libre 위에 장착해 약 2분마다 스캔하고 Tomato 앱으로 결과를 보냄
    • 기본 Libre 앱이 오류를 보여도 Miao Miao 경로는 데이터를 계속 발행함
    • 기본 앱은 혈당의 급격한 변화를 이상치로 보고 데이터를 보내지 않는 것으로 보였고, 사용자는 그런 데이터도 직접 판단하고 싶어 함
  • Tomato 앱에는 혈당을 Google Calendar 이벤트로 5분마다 발행하는 기능이 있어 Apple Watch 컴플리케이션으로 혈당을 볼 수 있었음

Go echo server로 Tomato 요청 확인하기

  • Tomato 앱 설정에는 Nightscout용 데이터 동기화 URL 입력란이 있었음
  • Nightscout URL 대신 직접 운영하는 웹서버 주소를 넣자, 게이트웨이 로그에 다음 경로가 나타남
/id/e1d67817-4591-4e8e-9bca-58a07a1087d8/api/v1/devicestatus
  • 요청 본문을 확인하기 위해 Go로 간단한 echo server를 작성함
  • 배포와 실행에는 encore.dev를 사용했으며, 무료로 모니터링 시스템을 운영할 수 있었음
// encore:api public raw method=POST path=/id/:id/api/v1/devicestatus
func Echo(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", req.Header.Get("Content-Type"))
    if _, err := io.Copy(w, req.Body); err != nil {
        http.Error(w, "Failed to echo request", http.StatusInternalServerError)
        return
    }
}
  • echo server는 클라이언트가 보낸 데이터를 그대로 돌려주는 서버로, 네트워크 문제 해결이나 애플리케이션 동작 테스트에 유용함
  • 같은 방식으로 다른 요청도 확인해 혈당 데이터가 담긴 응답을 얻음
- date: 1696171541297
sgV: 73
delta: 0
sysTime: 1696171541381
dateString: "2023-10-01T14:45:41.297Z"
_id: "dOUXaI8HcaulCGrQfxe23UE0"
type: "sgv"
device: "Tomato"
direction: "Flat"
  • sgV는 혈당 값이며, 영국에서 사용하는 단위로 맞추기 위해 18로 나눔
  • 응답에는 날짜·시간과 추세 방향도 포함됨
  • 이 호출은 2분마다 발생함

Grafana 대시보드와 혈당 게이지

  • 수신한 혈당 값을 Gauge metric으로 설정함
  • 게이지는 오르내릴 수 있는 단일 수치를 나타내는 메트릭이며, 메모리 사용량, 동시 요청 수, 온도 같은 변동값 측정에 쓰임
  • 혈당 값은 newValue / 18로 변환해 기록함
var BloodSugar = metrics.NewGauge[float64]{
    name: "blood_sugar",
    metrics.GaugeConfig{},
}
BloodSugar.Set(float64(newValue) / 18)
if err := insertReading(ctx, newValue); err != nil {
    rlog.Error(msg: "failed to insert blood sugar, proceeding", keysAndValues: "db_err", err)
}
  • 혈당 데이터는 최선 노력 방식으로 Postgres에도 저장함
    • DB 저장 실패가 나머지 로직을 실패시키지 않도록 로그만 남기고 계속 진행함
  • Grafana 대시보드는 2분마다 갱신되는 혈당 게이지를 실시간으로 보여줌
  • 목표 혈당 범위는 이상적으로 4에서 9 사이
  • 대시보드를 공유하거나 집에 모니터를 두면 혈당 상태를 한눈에 확인할 수 있음

Telegram 봇으로 혈당 맥락 추가하기

  • 혈당 수치만으로는 충분하지 않으며, 복잡한 시스템 모니터링처럼 맥락이 중요함
  • 식사 직후 혈당이 올라가더라도 이미 인슐린을 맞았다면 즉시 걱정할 상황이 아닐 수 있음
  • Grafana는 주석을 지원하지만, 로그인해서 직접 주석을 남기는 방식은 부담이 큼
  • Telegram 봇을 만들어 메시지를 받을 때마다 웹훅으로 주석을 추가하도록 구성함
// encore:api public raw method=POST path=/webhook
func Webhook(w http.ResponseWriter, req *http.Request) {
    body, err := io.ReadAll(req.Body)
    if err != nil {
        http.Error(w, "could not read request body", http.StatusBadRequest)
        return
    }
    defer req.Body.Close()

    var update Update
    err = json.Unmarshal(body, &update)
    if err != nil {
        http.Error(w, "could not unmarshal JSON", http.StatusBadRequest)
        return
    }
}
  • 웹훅은 요청을 받고, 메시지를 검증하고, JSON을 언마셜한 뒤 처리 결과를 Telegram으로 돌려줌
  • 메시지 검증 후 정수를 파싱하고 주석 서비스를 호출함
  • 주석 서비스는 활동 내용에 따라 태그를 붙임
func getTags(ctx context.Context, activity string) []string {
    a := strings.ToLower(activity)
    switch a {
    case "walk", "run", "ran", "gym":
        return []string{"exercise"}
    case "eat", "ate":
        return []string{"food eaten"}
    case "inject":
        return []string{"inject"}
    default:
        return []string{"other"}
    }
}
  • Grafana에서는 태그별 색상을 지정하고 주석 쿼리를 설정할 수 있음
  • 식사, 운동, 인슐린 주입 같은 맥락이 그래프 위에 표시되면 본인과 대시보드를 보는 사람이 혈당 상황을 더 쉽게 해석할 수 있음

낮은 혈당을 인시던트로 전환하기

  • 혈당 가시성은 확보됐지만, 알림이 없으면 실시간 대응이 어려움
  • 간호사에게 받은 지침에 따라 혈당이 4 아래로 내려가면 처치를 시작해야 함
  • 5분마다 측정값을 확인하는 작은 cron을 작성하고, 사전 정의한 하한선보다 낮으면 인시던트를 트리거함
var _ = cron.NewJob(
    id: "monitor-blood",
    cron.JobConfig{
        Title: "monitor blood to check if there is reason to open an incident",
        Every: 5 * cron.Minute,
        Endpoint: BloodIncidentCron,
    },
)
if r < BloodLowerLimit {
    if err := triggerIncident(ctx); err != nil {
        return fmt.Errorf("failed to trigger an incident: %w", err)
    }
}
  • Telegram 봇에는 "i need help" 메시지로 수동 인시던트를 여는 코드도 추가함
  • 인시던트가 트리거되면 별도 마이크로서비스가 incident.io에 인시던트를 생성함
  • incident.io를 선택한 이유는 Cloudflare의 사내 인시던트 도구와 가장 비슷하고, 완전히 Go로 작성되어 있기 때문임
payload := Payload{
    IdempotencyKey: idemKey.String(),
    Mode: incidentModeStandard,
    Name: fmt.Sprintf("Matt's blood sugar is currently #%s", blood),
    SeverityID: incidentSeverityCritical,
    Summary: "Matt's blood sugar is low!",
    Visibility: "public",
}
  • 실제 인시던트 도구를 쓰면 에스컬레이션 정책을 설정하고, 인시던트 상태에서 보낸 시간 같은 리포트를 볼 수 있음
  • incident.io 워크플로는 Zapier와 비슷한 규칙을 구성하고 구독자에게 문자 알림을 보낼 수 있음
  • 예시 워크플로에서는 인시던트가 열릴 때 본인에게 문자를 보내고, 20분 동안 닫히지 않으면 파트너나 형제자매에게 자동 에스컬레이션함

리포트와 향후 개선

  • 인시던트 도구를 사용하면 혈당 저하 인시던트가 시간에 따라 얼마나 발생했는지 리포트로 볼 수 있음
  • 예시 그래프는 블로그용 데모 데이터이며, 실제 데이터는 다행히 더 지루한 수준임
  • 낮은 혈당 이벤트가 증가 추세라면 현재 치료 방식이 맞지 않는 신호일 수 있고, 의사와 상담할 근거가 됨
  • 현재 또는 계획 중인 개선 항목은 다음과 같음
    • 자동 종료: 지금은 데이터를 검토하기 위해 수동으로 인시던트를 닫지만, 몇 번 늦게 닫아 에스컬레이션된 적이 있어 15분 정도 안정적인 혈당이 유지되면 자동 종료할 수 있음
    • 데이터 대화: 수년치 혈당 데이터를 저장하고 있으며, LLM을 학습시켜 “왜 항상 오후 3시에 낮아지는가?” 같은 질문을 하고 Google Calendar 같은 다른 데이터도 함께 활용하고 싶어 함
    • 그래프 확장: 현재 그래프는 단순하지만, 당뇨 관리 성공을 판단하는 hba1c 같은 지표를 추가할 수 있음
    • 실패 방지 강화: 시스템이 중요하기 때문에 실패를 더 많이 예방할수록 좋음

코딩으로 만든 개인 건강 운영 시스템

  • 1형 진단 초기에는 질환이 삶을 어렵게 만들 것이라는 두려움이 있었음
  • 이 프로젝트는 1형 상태를 이해하고 관리·모니터링하는 데 도움이 됨
  • 혈당 관리를 분산 시스템처럼 다루며, 관측성, 알림, 인시던트, 에스컬레이션, 리포트를 붙인 개인 운영 시스템으로 만들었음
  • 코딩 능력은 1920년대까지만 해도 사망 선고였던 질환의 관리를 어느 정도 자동화할 수 있게 해줌

댓글과 토론

Hacker News 의견들
  • 16년 동안 당뇨와 살아왔고, 글쓴이의 분위기가 좋고 존경스럽다. 병 때문에 슬프거나 화가 날 때 이 글을 다시 읽고, 모든 걸 감당할 수 있다고 확신했던 지금을 떠올리면 좋겠음
    쉽지 않다. 정신적으로도 실무적으로도 그렇고, 내 경우엔 여러 해 동안 “즐겁게 통제 불능”에 가까웠다
    바로 어제도, 당뇨 인생 처음으로 36시간 목표 범위 유지를 보고 진짜 울었다. 36시간 동안 100% 초록색이었다. 어쩌면 이제 이해한 걸지도 모르겠다
    몇 년 뒤 60% 수준까지 삶의 질이 뛴 건 CGM에 연결된 마이크로 주입기, 다른 당뇨인들과의 경험 공유, NightScout VM 설치로 휴대폰·시계·데스크톱 위젯·알람시계·냉장고 어디서나 혈당과 추세를 보는 환경, 그리고 독일식 정밀함으로 탄수화물을 세고 식전 정확히 15분 전 볼루스를 넣은 덕분이었다. 이렇게 사는 건 정말 싫지만 효과는 있다
    버텨라. 흐트러지지 마라

  • 글쓰기 스타일이 정말 좋고 읽는 즐거움이 있었다. 우연히도 침투 테스트 일을 하는데, 동료 중 한 명이 1형 당뇨를 앓고 있음
    이 글의 개요와 그로 인한 복잡성을 보니, 매일 그런 어려움을 넘어서면서도 팀에서 가장 친절하고 다가가기 쉬운 사람 중 하나로 지내는 동료에게 더 공감하게 됐다

    • 당뇨가 있어도 정상적인 삶을 살 수 있고, 심지어 최상위 테니스 선수도 될 수 있음: https://en.wikipedia.org/wiki/Alexander_Zverev#Personal_life
      다만 이 글을 읽고 나니 그렇게 살기 위해 필요한 자기 절제 수준을 정말 실감하게 됨
    • “펜테스팅”이 당뇨 펜을 테스트하는 일이라면 일부는 펜과 종이로 하는 테스트일 수도 있겠다는 농담이 가능하긴 함
      물론 침투 테스트처럼 시스템을 깨고 들어가려는 일일 수도 있는데, 그 일을 하는 건지 궁금함
    • 좋은 고용주를 만난 듯함. 많은 회사는 아픈 직원을 채용하지 않거나 해고하기도 하고, 실제로 직장에서 의료 정보를 공유하지 말라는 조언이 일반적임
      최근에는 한 직원이 모두가 승진할 거라 예상했는데 승진하지 못한 사례를 읽었다. 죄라면 동료에게 차가 고장 나서 자주 고치느라 지쳤다고 짧게 말한 것뿐이었다
      승진하면 어느 정도 통근이 필요했는데, 매니저가 “승진시키려 했지만 차 상태가 안 좋다는 얘기를 들었다”고 말했다고 함
  • GopherConSG에서 Matt가 이 발표를 한 뒤, 우리가 자기 데이터를 소유하지 못하는 문제를 이야기했다. 데이터 제공자 쪽, 즉 혈당을 모니터링하는 회사가 져야 할 법적 책임이 많아서 사안이 꽤 복잡하다는 점이 인상적이었고, 큰 회사들이 개방을 주저하는 이유도 이해된다
    반면 이용자가 정당하게 자기 것이고 실제 행동에 쓸 수 있는 데이터에 접근하지 못하는 건 상당히 부당해 보인다

    • 이 부분은 늘 이해가 안 된다. BIC 펜을 사면서, 그 펜으로 만들어진 모든 엔트로피가 데이터 작성자가 아니라 도구 제작자의 소유라는 필수 라이선스 계약을 맺는 것과 비슷한가? 사람이 없으면 그 데이터도 존재하지 않았을 텐데
  • 어제 관련된 걸 하나 만들었다. 터미널 상태 아이콘으로 혈당 데이터를 보여주는 tmux 플러그인이다
    나도 1형 당뇨라 CGM으로 Dexcom을 쓰고 있고, 지금은 그 환경에 맞춰 동작하게 해뒀지만 다른 기기도 지원하고 기능도 더 늘리고 싶다. 관심 있으면 여기서 볼 수 있음
    https://github.com/Cian911/tmux-xdrip

    • Dexcom이 시계와 직접 동기화되면 좋겠다. 몇 년째 기다리고 있지만 아마 시계 배터리를 많이 소모할 듯함
    • tmux 안에서 사는 1형 당뇨인으로서 정말 멋지다. 최대한 빨리 써봐야겠다
  • Miao Miao 아이디어가 흥미롭다. 1형 당뇨인으로서 Freestyle Libre는 정말 인생을 바꿔준 놀라운 기기지만, 소프트웨어는 꽤 별로다
    1년도 더 전에 버그를 신고했는데 Abbott는 아직도 버그라고 인정하지 않았다. 온라인과 Google Play 앱 리뷰에 같은 제보가 많이 있는데도 그렇다
    관심 있거나 Abbott가 인정하도록 힘을 보탤 수 있는 사람을 위해 말하자면, LibreLink를 방해 금지 모드 예외 앱 목록에 넣어도 효과가 없고, 방해 금지 모드에 들어가면 즉시 “알람을 사용할 수 없다”는 경고가 뜨는 버그다. 업무 환경에서 방해 금지 모드를 켜면 혈당 알람까지 꺼져서 불편하다

  • 약간 관련된 얘기로, 6개월 전에 폐쇄성 수면무호흡증 진단을 받았고 잘 때 일정 압력으로 공기를 불어넣어 기도 폐쇄를 막아주는 CPAP 기계를 써야 한다. 1형 당뇨처럼 임무 필수 수준은 아니지만, 이 기계들이 예상보다 훨씬 많은 로그를 남긴다
    이 글을 읽으면서 수면무호흡 쪽에도 모니터링이나 더 심한 경우를 위해 비슷하게 해킹한 게 있는지 궁금해졌다
    이미 OSCAR는 알고 있고 정말 고맙게 쓰고 있지만, 그 밖에 뭐가 있는지도 궁금함
    1: https://www.sleepfiles.com/OSCAR/

    • 오히려 생명에 직결된다고 본다. CPAP는 내 삶을 바꿨고, 장기간 치료하지 않은 수면무호흡은 심장 등 여러 건강 문제를 일으킨다
      수면 검사에서 최악일 때는 숨을 멈추는 사건이 분당 48회쯤 기록됐다. 혀가 기도로 말려 들어가면 숨쉬기가 꽤 어려워진다
    • 기기를 지원한다면 https://home.sleephq.com/가 이 데이터를 잘 처리해준다
  • 흥미롭지만 이 영역의 오픈소스 소프트웨어인 Nightscout, xDrip 등을 살펴보지 않은 이유가 있는지 궁금함
    새 문제가 아니고, 이미 많은 사람들이 훨씬 기능이 풍부하고 잘 동작하는 해법으로 풀어둔 상태다

    • 살펴보긴 했다. 내가 하려던 일에는 처음부터 만드는 편이 더 빠를 것 같았고, 재미있는 학습 경험이기도 했다
  • Scott Hanselman은 수년 동안 1형 당뇨와 여러 기술 해킹에 대해 블로그와 YouTube에서 다뤄왔다. 예전에는 혈당이 터미널 프롬프트에 표시되도록 해킹하기도 했다: https://www.youtube.com/watch?v=_meKUIm9NwA

  • “이 기간에는 기기로 혈당을 측정할 수 없고, 혈당이 위험한 수준에 도달해도 알림을 받지 못한다. 이 오류는 가장 스트레스가 큰 순간에 나타나는 습관이 있었다”와 “살펴보니 메인 앱은 혈당 수치가 갑자기 변해 이상치처럼 보이는 데이터를 보내는 걸 싫어하는 듯했다”라는 대목은 생명을 위협하는 버그처럼 들린다

    • 대부분의 센서가 이런 식으로 동작한다. 센서가 항상 정확하다고 가정하고 있지만 실제로는 그렇지 않다. 예를 들어 자는 동안 센서 위로 몸을 굴리면 포도당 산화효소 스트립이 몸 안쪽으로 더 눌려 들어가 판독값이 달라질 수 있다
      엄밀히 말하면 CGM 데이터만으로 치료 결정을 내리면 안 된다
      Dexcom 센서의 경우 일부 앱은 원시값을 가져와 이 과정을 우회할 수 있다. Libre, 적어도 Libre 3에서는 특정 상황에서 그 값이 잘못된 판독이 아니라 혈당이 빠르게 변하는 중이라고 판단하면 데이터를 나중에 채워 넣는 것을 본 적이 있다
  • 기술 기반은 아니지만 당뇨 경보견도 있다
    잘 훈련된 개가 기술 기반 모니터링·알림 시스템을 보완해 보호자에게 경고하는 데 큰 성공을 거두고 있다는 걸 최근 배우고 있다