건강을 추측하지 마세요: Streamlit과 ECharts로 통합된 Quantified Self 대시보드 만들기

발행: (2026년 2월 9일 오전 10:00 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

Beck_Moulton

소개

수면이 혈당 수치에 어떤 영향을 미쳤는지 확인하기 위해 다섯 개의 서로 다른 앱을 오가느라 지치셨나요? Quantified Self의 세계에 오신 것을 환영합니다. 웨어러블이 넘쳐나는 이 시대에 진정한 도전은 데이터를 수집하는 것이 아니라 데이터 엔지니어링과 통합입니다.

이 튜토리얼에서는 Streamlit, ECharts, Pandas를 사용하여 통합 건강 대시보드를 구축합니다. 우리는 Oura Ring, Apple Watch, 그리고 연속 혈당 모니터(CGM)에서 수집한 시계열 데이터를 정렬함으로써 “이질적인 데이터 문제”에 접근할 것입니다. 이 가이드를 마치면 원시 건강 지표를 실행 가능한 인사이트로 전환하는 전문 수준의 Quantified Self 시각화 도구를 갖게 될 것입니다.

아키텍처: 원시 로그에서 인사이트까지

헬스 데이터를 다루는 것은 까다롭습니다. 각 기기가 서로 다른 샘플링 속도를 가지고 있기 때문입니다. 당신의 CGM은 5분마다 데이터를 전송할 수 있지만, Oura Ring은 하루에 한 번 “수면 점수”만 제공합니다.

graph TD
    A[Oura Ring API] -->|Daily Metrics| B[(SQL Database)]
    C[Apple Watch / HealthKit] -->|Heart Rate/Steps| B
    D[Continuous Glucose Monitor] -->|5‑min Glucose| B
    B --> E{Data Alignment Layer}
    E -->|Pandas Resampling| F[Unified DataFrame]
    F --> G[Streamlit Frontend]
    G --> H[ECharts Interactive Viz]

사전 요구 사항

다음 기술 스택이 설치되어 있는지 확인하세요:

  • Python 3.9+
  • Streamlit – UI 프레임워크
  • Pandas – 데이터 조작
  • SQLAlchemy – 데이터베이스 인터페이스
  • streamlit‑echarts – 인터랙티브 차트
pip install streamlit pandas sqlalchemy streamlit-echarts

Step 1: 데이터 레이어 (SQLAlchemy)

먼저 중앙 저장소에서 데이터를 가져옵니다. 이 예제에서는 건강 데이터를 SQLite(또는 PostgreSQL) 데이터베이스에 동기화했다고 가정합니다.

import pandas as pd
from sqlalchemy import create_engine

# Database connection
engine = create_engine('sqlite:///health_data.db')

def load_health_data():
    # Fetch metrics from different sources
    cgm_df = pd.read_sql("SELECT timestamp, glucose_value FROM cgm_logs", engine)
    activity_df = pd.read_sql(
        "SELECT date, sleep_score, readiness_score FROM oura_metrics",
        engine,
    )

    # Ensure datetime types
    cgm_df["timestamp"] = pd.to_datetime(cgm_df["timestamp"])
    activity_df["date"] = pd.to_datetime(activity_df["date"])

    return cgm_df, activity_df

Step 2: The Alignment Magic (Pandas)

여기가 Data Engineering이 이루어지는 부분입니다. resamplemerge_asof를 사용하여 5분 간격 CGM 데이터를 일일 Oura 점수와 정렬하여 연속적인 타임라인을 생성합니다.

def align_data(cgm, activity):
    # Resample CGM to hourly averages to reduce noise
    cgm_hourly = (
        cgm.set_index("timestamp")
        .resample("H")
        .mean()
        .reset_index()
    )

    # Merge daily activity data back into the hourly timeline
    # Left join keeps the high‑frequency timeline intact
    merged = pd.merge_asof(
        cgm_hourly.sort_values("timestamp"),
        activity.sort_values("date"),
        left_on="timestamp",
        right_on="date",
        direction="backward",
    )
    return merged

Step 3: ECharts를 사용한 시각화

표준 차트는 지루합니다. 건강 데이터에서는 인터랙티브가 필요합니다. ECharts를 사용하면 수면이 좋지 않았던 특정 밤을 확대하여 혈당이 정확히 어떻게 변했는지 확인할 수 있습니다.

from streamlit_echarts import st_echarts

def render_health_chart(df):
    options = {
        "title": {"text": "Glucose vs. Sleep Correlation"},
        "tooltip": {"trigger": "axis"},
        "legend": {"data": ["Glucose", "Sleep Score"]},
        "xAxis": {
            "type": "category",
            "data": df["timestamp"]
            .dt.strftime("%m-%d %H:%M")
            .tolist(),
        },
        "yAxis": [
            {"type": "value", "name": "Glucose (mg/dL)"},
            {"type": "value", "name": "Score", "max": 100},
        ],
        "series": [
            {
                "name": "Glucose",
                "type": "line",
                "data": df["glucose_value"].tolist(),
                "smooth": True,
                "itemStyle": {"color": "#ff4d4f"},
            },
            {
                "name": "Sleep Score",
                "type": "bar",
                "yAxisIndex": 1,
                "data": df["sleep_score"].tolist(),
                "itemStyle": {"color": "#1890ff", "opacity": 0.3},
            },
        ],
        "dataZoom": [{"type": "slider"}],  # Add zoom control
    }
    st_echarts(options=options, height="500px")

공식적인 확장 방법

로컬 대시보드는 좋은 시작이지만, 주말 프로젝트에서 프로덕션‑준비가 된 헬스‑테크 플랫폼으로 전환하려면 다음이 필요합니다:

  • 실시간 동기화
  • HIPAA‑준수 데이터 처리
  • 견고하고 확장 가능한 파이프라인

대규모 시계열 데이터에 대한 고급 아키텍처 패턴 및 프로덕션‑준비 예제를 보려면 **WellAlly Blog**의 기술 심층 분석을 확인하세요. 여기서는 생체 데이터 보안, 고성능 모델링 등을 다룹니다.

모두 합치기

아래는 모든 것을 연결하는 최소한의 Streamlit 앱입니다.

import streamlit as st

# Load and align data
cgm_df, activity_df = load_health_data()
merged_df = align_data(cgm_df, activity_df)

st.title("Quantified Self Health Dashboard")
st.subheader("Glucose vs. Sleep Score")

render_health_chart(merged_df)

앱을 실행하려면:

streamlit run your_app.py

확장된 예시

import streamlit as st

def main():
    st.set_page_config(page_title="Quantified Self Dashboard", layout="wide")
    st.title("📊 My Health Data Universe")

    st.sidebar.header("Filters")
    date_range = st.sidebar.date_input("Select Date Range")

    # Load and process
    cgm, activity = load_health_data()
    aligned_df = align_data(cgm, activity)

    # UI Layout
    col1, col2 = st.columns(2)
    with col1:
        st.metric("Avg Glucose", f"{aligned_df['glucose_value'].mean():.1f} mg/dL")
    with col2:
        st.metric("Avg Sleep Score", f"{aligned_df['sleep_score'].mean():.0f}")

    render_health_chart(aligned_df)

if __name__ == "__main__":
    main()

결론

데이터를 통합하면 개별 숫자만 보던 것이 패턴을 보게 됩니다. REM 수면이 2시간 미만인 밤에 혈당이 급증하나요? 이제 이를 입증할 데이터가 생겼습니다!

다음 단계는?

  • Scikit‑learn을 사용하여 Anomaly Detection을 추가하고 고혈당 이벤트를 표시합니다.
  • Apple Health XML 내보내기를 통합하여 더 세밀하게 분석합니다.
  • **WellAlly Blog**에서 헬스‑테크 엔지니어링에 대한 더 많은 영감을 얻으세요.

즐거운 해킹 되세요!

이 글이 도움이 되었나요? 가장 많이 추적하는 건강 지표를 댓글로 남겨 주세요! 👇

Back to Blog

관련 글

더 보기 »

Power BI의 스키마 및 데이터 모델링

소개 데이터 모델링은 이름에서 알 수 있듯이 정리되고 구조화된 데이터를 재구성하고 통찰력 있는 시각화를 만드는 것을 포함합니다. Power BI에서…