TRENDSHIFT 일간 #2 · 2026-06-10

NOOP 딥다이브
— 내 WHOOP 데이터를, 클라우드 없이 내 기기에서

NOOP은 피트니스 밴드 WHOOP(4.0·5.0)의 생체 데이터를 클라우드·계정·구독 없이 블루투스로 직접 읽어, 전부 내 기기의 로컬 SQLite에 저장하고 회복·스트레인·HRV·수면을 기기 안에서 직접 계산하는 오픈소스 동반 앱입니다. macOS(SwiftUI)·Android(Jetpack Compose) 풀 앱이 있고, iOS는 AltStore/SideStore 사이드로드 방식의 풀 앱(IPA)이 공개돼 있습니다(단 App Store 미출시). 핵심은 WHOOP의 BLE 통신 프로토콜을 역분석(reverse-engineering)한 WhoopProtocol과, 논문 기반 알고리즘으로 점수를 다시 계산하는 StrandAnalytics입니다. (NoopApp/noop · Swift 50.7% + Kotlin 45.4% (Python 3.7%·Shell 0.2%) · v4.2.11 · WHOOP 비공식·비제휴)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

무엇을 하는 프로젝트인가

WHOOP은 심박·HRV·수면을 재는 손목/팔 밴드입니다. 그런데 이 밴드는 화면이 없고, 데이터를 보려면 반드시 WHOOP의 공식 앱으로 로그인해 클라우드에 올리고 월 구독료를 내야 합니다. 즉 "내 몸에서 나온 데이터"인데도, 그걸 보려면 회사 서버와 구독을 거쳐야 하는 구조입니다.

NOOP은 그 구조를 뒤집습니다. 밴드와 블루투스로 직접 통신해서 심박·R-R 간격·SpO₂·피부온도·호흡·가속도 같은 원시 데이터를 빼내고, 전부 내 컴퓨터/폰의 로컬 SQLite 파일에 저장합니다. 회복(Recovery)·스트레인(Strain)·HRV·수면 점수도 서버가 아니라 기기 안에서 직접 계산합니다. 네트워크를 쓰는 기능은 (선택 사항인) AI 코치 하나뿐이고, 그것도 내 API 키로만 동작합니다.

한 장으로 이해하기

"내 차의 OBD 단자에 직접 진단기를 꽂는 것"과 같다

정비소(클라우드)에 차를 맡기지 않아도, OBD 포트에 직접 진단기를 꽂으면 엔진 데이터를 내가 바로 읽을 수 있죠. NOOP은 WHOOP 밴드의 블루투스 단자에 직접 꽂아 데이터를 읽는 진단기입니다.

다른 점은, WHOOP은 공식 단자 규격을 공개하지 않았다는 것. 그래서 NOOP은 통신 신호를 하나하나 뜯어보며 규격을 알아낸(역분석한) 결과를 코드로 구현했습니다. 잠금 해제나 결제 우회가 아니라, "내가 가진 기기와 그냥 대화하는" 상호운용(interoperability) 프로젝트입니다.

용어
BLE (Bluetooth Low Energy)
저전력 블루투스. 웨어러블·센서가 배터리를 아끼며 데이터를 주고받는 표준이다. 기기는 서비스특성(characteristic)이라는 UUID로 식별되는 "데이터 창구"를 열어 두고, 앱이 거기에 구독(notify)하면 값이 갱신될 때마다 밀어 준다. NOOP은 WHOOP가 어떤 UUID로 무엇을 보내는지를 알아내 그 창구에 직접 붙는다.
용어
역분석 (Reverse Engineering)
공개되지 않은 규격·동작을, 결과(신호·바이트 흐름)를 관찰해 거꾸로 알아내는 일. NOOP은 WHOOP의 펌웨어나 비밀 코드를 베끼지 않고, "이 바이트 묶음은 심박 1초치였다" 식으로 통신 포맷만 알아내 자체 구현했다. README가 강조하듯 독점 코드·로고·DRM 우회는 일절 포함하지 않는다.

2왜 주목받는가

트렌딩 이유 · 경쟁(공식 앱) 대비 장점

핵심 트렌딩 이유는 두 가지입니다. ① "내 데이터 주권"이라는 정서 — 하드웨어는 샀는데 데이터는 구독으로 인질 잡힌 상황에 대한 반발이 큽니다. ② 기술적 완성도 — 단순한 데이터 덤프 도구가 아니라, WHOOP 4.0과 5.0 두 세대의 프로토콜을 모두 다루고, 회복/스트레인/수면 점수를 논문 기반으로 직접 재계산하며, macOS·Android 풀 앱을 같은 로직으로 포팅한 진짜 제품이라는 점입니다.

공식 WHOOP 앱과의 비교

항목공식 WHOOP 앱NOOP
데이터 저장WHOOP 클라우드○ 내 기기 로컬 SQLite
계정 / 로그인필수○ 불필요 (서버 자체가 없음)
구독료월 정기 결제○ 무료 (기부는 100% 선택)
오프라인 동작제한적(동기화 필요)○ 완전 오프라인
점수 계산 위치서버○ 기기 안에서 (투명한 수식)
기존 기록 가져오기○ WHOOP CSV + Apple Health 임포트
플랫폼iOS / AndroidmacOS · Android · iOS(사이드로드, App Store 미출시)
알고리즘 공개✕ (독점)○ 파일마다 근거 논문 명시

핵심 차별점 셋

차별점 ①

"투명한 수식" — 점수가 어떻게 나왔는지 코드로 다 보인다

공식 앱의 회복 점수는 블랙박스입니다. NOOP은 회복·스트레인·HRV·수면을 출처가 명시된 공개 방법으로 다시 계산합니다 — HRV는 Task Force 1996의 RMSSD/SDNN, 스트레인은 Karvonen %HRREdwards/Banister TRIMP, 최대심박은 Tanaka 공식. 각 분석기 파일이 "무엇을 어떻게 계산하는지"를 스스로 문서화합니다. (독점 모델의 재현이 아니라 근사임을 명시.)

차별점 ②

밴드의 햅틱(진동) 모터를 "출력 장치"로 되살린다

밴드는 HRV를 측정할 뿐 아니라 진동 모터로 신호도 보낼 수 있습니다. NOOP은 이를 활용해 화면 없이 쓰는 기능을 만들었습니다 — Breathe(들숨 1번·날숨 2번 진동으로 호흡 페이싱하는 HRV 바이오피드백), Intervals(구간 전환마다 진동하는 무음 HIIT 타이머). 측정+피드백을 한 기기로 닫는 설계가 돋보입니다.

차별점 ③

"한 번 가져오면 영원히 내 것" — 과거 기록 임포트

이미 공식 앱이나 Apple Health에 수년치 데이터가 있다면, WHOOP CSV 내보내기와 Apple Health export.xml을 한 번 임포트하면 전부 내 기기로 옮겨집니다. 특히 1GB가 넘을 수 있는 export.xml스트리밍 SAX 파서로 처리해 메모리 폭발 없이 읽어 들이는 점이 실전적입니다.

한 줄 요약

NOOP은 "데이터 탈취 도구"가 아니라 "내가 산 센서를 내가 통제하는 기기에서 읽는 자유"를 코드로 구현한 사례다. 그래서 기술(BLE 역분석·on-device 분석)과 철학(데이터 주권)이 동시에 화제가 됐다.

3기술 스택 전체 지도

언어 · 패키지 · 빌드를 각각 뜯어본다

NOOP의 설계 철학은 "플랫폼에 의존하지 않는 순수 로직 패키지 + 얇은 UI 앱"입니다. 핵심 로직(프로토콜 디코드·저장·분석)은 플랫폼-순수(platform-pure) Swift 패키지로 짜여 테스트·CLI에서도 그대로 돌고, 같은 로직을 Android용으로 Kotlin에 다시 포팅했습니다. UI만 macOS는 SwiftUI, Android는 Jetpack Compose로 입힙니다.

언어 / 빌드

항목내용
언어Swift 50.7%(macOS·iOS·공용 로직) + Kotlin 45.4%(Android) + Python 3.7%·Shell 0.2% — 같은 알고리즘의 2개 포팅
macOS UISwiftUI (macOS 13+), 메뉴바 엑스트라 + 온보딩 마법사 포함
Android UIJetpack Compose (Android 8+ / minSdk 26)
프로젝트 생성XcodeGenproject.yml 한 장에서 Strand.xcodeproj 생성
패키지 관리Swift Package Manager (SPM)
코드사이닝샌드박스 + Hardened Runtime, 유니버설 바이너리(Apple Silicon+Intel)
라이선스비상업·비공식 상호운용 프로젝트 (WHOOP 비제휴)

외부 의존성 (단 2개)

놀랍도록 의존성이 적습니다. UI 라이브러리조차 직접 만들어 썼습니다(StrandDesign).

패키지역할
GRDB.swiftSQLite를 Swift답게 다루는 ORM/쿼리 라이브러리 — 로컬 저장의 전부
ZIPFoundationWHOOP CSV 내보내기 .zip 압축 해제

6개 Swift 패키지 (역할별)

패키지하는 일
WhoopProtocolBLE 프레임 파싱·CRC 검증·명령/이벤트/패킷 디코드 (역분석 핵심)
WhoopStoreGRDB/SQLite 영속화 — 마이그레이션·스트림·캐시
StrandAnalyticsHRV·회복·스트레인·수면·상관 분석 수학 (DB 없는 순수 함수)
StrandImportWHOOP CSV + Apple Health 임포터
StrandDesignSwiftUI 디자인 시스템 (팔레트·차트·컴포넌트, 외부 UI 의존 0)
NoopLocalAccess로컬 SQLite를 외부 도구·스크립트에서 직접 읽을 수 있게 해 주는 CLI 라이브러리(noop-local-access 실행 파일, macOS 전용, GRDB 기반)
설계 포인트
"플랫폼-순수" 패키지가 테스트를 살린다

WhoopProtocolCoreBluetooth를 임포트하지 않는다. 즉 실제 블루투스 하드웨어가 없어도, 저장해 둔 바이트 프레임만으로 디코드 로직을 단위 테스트할 수 있다. BLE 연결·본딩 같은 플랫폼 부분은 앱 레이어(Strand/BLE/)가 UUID 문자열을 CBUUID로 감싸 처리한다. "순수 로직 ↔ 플랫폼 어댑터"를 분리하는 교과서적 구조다.

용어
GRDB.swift
SQLite를 Swift에서 타입 안전하게 쓰게 해 주는 라이브러리. 레코드 매핑·쿼리 빌더·마이그레이션·변화 관찰(ValueObservation)을 제공한다. NOOP은 이걸로 모든 생체 데이터를 로컬 파일 하나에 담고, DB가 바뀌면 SwiftUI 화면이 자동 갱신되게 한다. 클라우드 DB가 아니라 "내 디스크의 파일"이라는 점이 핵심.
용어
XcodeGen
project.yml(YAML)에서 .xcodeproj생성해 주는 도구. .xcodeproj는 사람이 손으로 편집하기 어렵고 git 충돌이 잦은데, XcodeGen을 쓰면 프로젝트 설정을 읽기 쉬운 텍스트로 버전 관리하고 명령 한 줄(xcodegen generate)로 재생성할 수 있다.

4아키텍처 심화 분석

시스템 구조도 + 핵심 설계 패턴

NOOP의 진짜 배울 점은 "센서 → 디코드 → 저장 → 분석 → UI"의 단방향 파이프라인을 플랫폼 독립적으로 설계한 방식입니다. 모든 데이터는 한 방향으로 흐르고, 각 단계가 명확히 분리된 패키지입니다. 그리고 모든 화살표가 내 기기 안에서만 일어납니다.

전체 데이터 흐름도

[입력 3종] [디코드/파싱] [저장] WHOOP 밴드 ──BLE notify──▶ Strand/BLE + Collect ──▶ WhoopProtocol (프레임 디코드) │ WHOOP CSV ─┐ ▼ (.zip) ├──▶ StrandImport ───────────────────▶ WhoopStore Apple Health│ (CSV / SAX XML 파싱) (로컬 SQLite, GRDB) export.xml ─┘ │ ▼ StrandAnalytics (on-device) 회복·스트레인·HRV·수면·상관 │ ▼ Strand (SwiftUI) + StrandDesign Today·Live·Sleep·Trends·Automations... ★ 모든 화살표는 내 기기 밖으로 나가지 않는다 (서버·텔레메트리 0)

설계 패턴 ① — 두 세대 밴드를 하나의 enum으로

WHOOP 4.0과 5.0은 통신 규격이 다릅니다. NOOP은 이를 DeviceFamily 한 타입으로 추상화해, 같은 디코더가 세대별 차이만 분기하도록 했습니다.

public enum DeviceFamily: String, Sendable, CaseIterable {
    case whoop4   // CRC8(poly 0x07) 헤더 검사, 서비스 61080001-…
    case whoop5   // CRC16-Modbus 헤더 검사, "puffin" 패킷, 서비스 fd4b0001-…
}

디코딩은 스키마 주도(schema-driven)입니다. Resources/whoop_protocol.json이 프레임 구조를 데이터로 기술하고, 코드는 그 스키마대로 바이트를 해석합니다. CRC8·CRC16-Modbus·zlib CRC-32 구현, 프레임 프레이밍, 값 해석, 과거 스트림 재조립이 여기 들어 있습니다. 새 필드가 생기면 코드가 아니라 JSON을 고칩니다.

용어
CRC (순환 중복 검사)
전송된 바이트가 깨지지 않았는지 확인하는 체크섬. 보낸 쪽이 데이터로 짧은 검사값을 계산해 붙이고, 받은 쪽이 다시 계산해 일치하는지 본다. WHOOP 4는 CRC8, 5는 CRC16-Modbus를 쓰는데, NOOP은 두 세대를 구분하는 단서로도 이 CRC 방식 차이를 활용한다.

설계 패턴 ② — 버전이 매겨진 DB 마이그레이션

WhoopStore의 스키마는 v1부터 현재 v16(v16-paired-device-peripheral)까지 누적된 마이그레이터입니다. 앱 업데이트에 따라 계속 증가하며, 기존 로컬 DB가 단계적으로 최신 스키마로 올라갑니다. v1~v3의 디코드 스트림 테이블 예:

CREATE TABLE hrSample      (deviceId TEXT, ts INTEGER, bpm INTEGER, ...);
CREATE TABLE rrInterval    (deviceId TEXT, ts INTEGER, rrMs INTEGER, ...);
CREATE TABLE spo2Sample    (deviceId TEXT, ts INTEGER, red INTEGER, ir INTEGER, ...);
CREATE TABLE skinTempSample(deviceId TEXT, ts INTEGER, raw INTEGER, ...);
CREATE TABLE respSample    (deviceId TEXT, ts INTEGER, raw INTEGER, ...);

이후 마이그레이션은 서버 없이 기기가 계산한 지표 캐시(sleepSession, dailyMetric), 커서, 원시 프레임 아웃박스 등을 추가합니다.

설계 패턴 ③ — 투명한 on-device 분석기들

StrandAnalyticsDB도 모르는 순수 함수 모음입니다. 입력은 숫자 배열, 출력은 점수. 각 파일이 근거 논문을 명시합니다.

파일계산하는 것
HRVAnalyzerR-R 간격에서 RMSSD+SDNN (Task Force 1996), 범위·Malik 이소성 필터
RecoveryScorer0~100 회복 점수: HRV 중심 z-score + 개인 기준선 대비 로지스틱 합성
StrainScorer0~21 로그 스트레인: %HRR(Karvonen) + Edwards/Banister TRIMP
SleepStager심폐+중력 특징으로 수면/각성 + 근사 4단계 staging
CorrelationEnginePearson r, OLS 회귀, 시차(lag) 상관 — 두 지표 사이 관계
WorkoutDetector 외운동 감지·롤링 기준선·행동 인사이트·일일 오케스트레이터
왜 이렇게 나눴나

"순수 수학 함수"로 떼어 놓으면, 같은 RMSSD 계산을 Swift와 Kotlin에서 똑같이 옮길 수 있고, 고정된 입력으로 결과를 못 박아 테스트할 수 있다. UI·DB·블루투스에 얽힌 코드와 섞였다면 절대 못 할 일이다. NOOP이 두 플랫폼에서 "같은 점수"를 보장하는 비결이 이 분리다.

밴드를 "맥 컨트롤러"로 — Automations

Strand/Screens/AutomationsView.swift + System/MacActions.swift는 밴드의 물리 입력과 실시간 생체값을 Mac 동작으로 바꿉니다.

트리거동작
밴드 더블탭Mac 잠금 / 순간 표시 / 임의의 macOS 단축어 실행(shortcuts:// URL — 샌드박스 친화적)
착용/이탈 감지밴드를 벗으면 Mac 잠금, 다시 차면 단축어 실행 (※ 자동 잠금해제는 Apple Watch 전용이라 불가)
심박 존 / 휴식 스트레스햅틱 코칭 — 화면 안 봐도 진동으로 알림
스마트 알람밴드 펌웨어 알람을 직접 무장 — Mac이 자거나 앱이 꺼져도 기상 시각에 진동

5디렉토리 구조 해부

주요 폴더/파일의 역할

"순수 로직 패키지 + 얇은 앱 타깃"이라는 철학이 폴더 구조에 그대로 드러납니다. Strand/가 빌드하는 macOS 앱이고, 진짜 두뇌는 Packages/ 아래에 흩어져 있습니다.

noop/ ├─ Strand/ ← macOS SwiftUI 앱 (이걸 빌드하면 NOOP.app) │ ├─ App/RootView.swift 사이드바·전체 화면 구성 │ ├─ Screens/ Today·Live·Breathe·Intervals·Sleep·Trends... │ ├─ BLE/ Collect/ CoreBluetooth 연결·본딩·오프로드·실시간 │ ├─ Data/ MetricCatalog·알림 설정 저장 등 │ ├─ System/MacActions.swift 더블탭→Mac 동작·단축어 실행 │ ├─ MenuBar/ 메뉴바 라이브 심박 위젯 │ └─ Onboarding/ 첫 실행 마법사 ├─ Packages/ │ ├─ WhoopProtocol/ BLE 디코드 (CoreBluetooth 임포트 X) │ │ └─ Resources/whoop_protocol.json ← 스키마 주도 디코딩 │ ├─ WhoopStore/ GRDB SQLite (Database.swift = 마이그레이터 v16) │ ├─ StrandAnalytics/ HRV·회복·스트레인·수면·상관 수학 │ ├─ StrandImport/ WhoopExportImporter / AppleHealthImporter │ ├─ StrandDesign/ RecoveryRing·Hypnogram·Sparkline 등 차트 └─ NoopLocalAccess/ 로컈 SQLite CLI 라이브러리 (noop-local-access 실행 파일, macOS 전용) ├─ StrandTests/ 앱 단위 테스트 ├─ Tools/Backfill/ 디코드 데이터 백필 CLI 도구 ├─ android/ Jetpack Compose 풀 앱 (같은 로직 Kotlin 포팅) ├─ Fixtures/ 테스트용 샘플 WHOOP 내보내기 ├─ docs/ BUILD.md · PROTOCOL.md · 배너 등 └─ project.yml XcodeGen 정의 (Strand.xcodeproj 생성원)
읽기 좋은 순서

처음 본다면 READMEproject.yml(앱이 어떤 패키지를 묶는지) → WhoopProtocolDeviceFamilywhoop_protocol.json(데이터가 어떻게 생겼나) → WhoopStore/Database.swift(어디에 저장되나) → StrandAnalytics/HRVAnalyzer.swift(가장 단순한 분석기) → Strand/App/RootView.swift(UI가 어떻게 엮이나) 순서가 좋다.

6학습 포인트

이 레포에서 기술별로 배울 것 + 실습 아이디어

① BLE 통신과 바이너리 프로토콜 파싱

웨어러블·IoT의 기본기입니다. "서비스/특성 UUID → notify 구독 → 바이트 프레임 → 의미 있는 값"으로 이어지는 흐름과, CRC로 무결성을 확인하고 스키마로 필드를 해석하는 패턴을 한 레포에서 통째로 볼 수 있습니다.

실습: 웹 브라우저의 Web Bluetooth API로 가까운 BLE 기기를 스캔해 서비스·특성 UUID 목록을 출력하는 30줄짜리 페이지를 만들어 본다.

② "순수 로직 ↔ 플랫폼 어댑터" 분리 설계

WhoopProtocolCoreBluetooth를 임포트하지 않는다는 한 가지 결정이, 테스트 가능성·재사용성·멀티플랫폼 포팅을 전부 가능하게 했습니다. 의존성을 안쪽(순수)이 아니라 바깥쪽(앱)이 떠안게 하는 의존성 역전의 실전 예시입니다.

실습: 내가 만든 작은 앱에서 "네트워크/파일 IO에 의존하는 부분"과 "순수 계산"을 두 모듈로 쪼개, 순수 모듈만 단위 테스트해 본다.

③ 로컬 우선(local-first) 데이터 저장

클라우드 없이 SQLite 한 파일에 모든 것을 담고, DB 변화를 관찰해 UI를 갱신하며, 스키마를 버전 마이그레이션으로 진화시키는 방식. 오프라인 앱·프라이버시 앱 설계의 정석입니다.

실습: SQLite + 마이그레이션을 직접 써서, 표 스키마를 v1→v2로 올릴 때 기존 데이터가 보존되는 미니 앱을 만든다.

④ 생체신호 분석 수학을 코드로

RMSSD(연속 R-R 차이의 제곱평균제곱근), z-score 기준선, TRIMP 같은 공식이 어떻게 코드가 되는지 볼 수 있습니다. "논문 한 줄 → 함수 한 개"로 옮기는 연습으로 최고의 교본입니다.

실습: R-R 간격 배열을 받아 RMSSD를 계산하는 함수를 직접 짜고, NOOP의 HRVAnalyzer 결과와 같은 입력으로 비교해 본다.

⑤ 1GB+ XML을 메모리 폭발 없이 — 스트리밍 파싱

Apple Health export.xml은 통째로 메모리에 올리면 죽습니다. AppleHealthImporterSAX(이벤트 기반) 파서로 한 요소씩 흘려보내며 처리합니다. 대용량 데이터 처리의 핵심 기술입니다.

실습: 큰 XML/CSV를 한 줄/한 요소씩 스트리밍으로 읽어 합계만 내는 스크립트를 짜서, 전체 로드 방식과 메모리 사용량을 비교한다.

7시스템 요구사항

무엇이 있어야 돌아가나
항목요구사항
macOS 앱macOS 13+, 블루투스 있는 Mac. 빌드 시 Xcode 15+ (Swift 5.9) + XcodeGen
Android 앱Android 8+ (minSdk 26). APK 사이드로드(\"알 수 없는 앱 설치\" 허용)
iOSiOS 17+. AltStore/SideStore로 IPA 사이드로드. App Store 미출시, 본인 Apple ID로 서명. (패키지 플랫폼 지원은 iOS 16+)
실시간 페어링본인 소유의 WHOOP 밴드(4.0 또는 5.0)
밴드 없이 체험WHOOP CSV / Apple Health 임포트로 탐색 가능 (데모 APK는 샘플 데이터 내장)
네트워크불필요 — 유일한 예외는 선택적 AI 코치(본인 API 키)
권한(entitlements)app-sandbox · 블루투스 · 사용자 선택 파일 읽기쓰기. Hardened Runtime ON
중요 · 법적/안전 고지

NOOP은 WHOOP과 무관한 비공식·비제휴 상호운용 프로젝트다. \"WHOOP\"은 대화 상대인 하드웨어를 가리키기 위해서만 쓰였고, 독점 코드·펌웨어·로고를 포함하지 않으며 로그인/페이월/DRM을 우회하지 않는다. 또한 의료기기가 아니다 — 심박·HRV·회복·수면·SpO₂ 등 모든 값은 공개 방법으로 계산한 근사치이며 임상 검증되지 않았다. 진단·치료·건강 의사결정에 쓰지 말 것. 본인이 가진 밴드가 있어야 실시간 데이터를 받을 수 있다.

8직접 해볼 수 있는 실습 과제

난이도별 5개 — 직접 손을 움직여야 남는다
난이도 ★☆☆

1. 밴드 없이 전체 화면 둘러보기

릴리스에서 NOOP-demo.apk(샘플 데이터 내장)를 설치하거나, macOS 앱을 빌드한 뒤 Fixtures/의 샘플 WHOOP 내보내기를 Data Sources로 임포트한다. Today·Sleep·Trends 화면이 어떤 지표를 보여 주는지, 어디서 그 값이 왔는지 데이터 출처 푸터를 읽는다.

난이도 ★☆☆

2. project.yml 읽고 의존성 그래프 그리기

project.yml을 열어 Strand 앱 타깃이 어떤 패키지 5개에 의존하는지, 각 패키지는 또 무엇에 의존하는지 화살표로 그려 본다. \"앱은 무겁고 패키지는 가볍다\"는 의존성 방향을 눈으로 확인한다.

난이도 ★★☆

3. WhoopProtocol 패키지만 단독 빌드·테스트

Xcode 프로젝트 없이 cd Packages/WhoopProtocol && swift build && swift test를 돌려 본다. CoreBluetooth 없이도 디코드 테스트가 통과하는 걸 확인하고, whoop_protocol.json 스키마와 디코더 코드가 어떻게 짝지어지는지 따라간다.

난이도 ★★★

4. RMSSD를 두 언어로 똑같이 구현

StrandAnalytics/HRVAnalyzer.swift의 RMSSD 계산을 읽고, 같은 알고리즘을 파이썬(또는 본인 언어)으로 다시 짠다. 동일한 R-R 배열을 넣어 결과가 일치하는지 검증한다 — \"순수 함수가 왜 멀티플랫폼에 강한가\"를 체감하는 과제.

난이도 ★★★

5. 나만의 Automation 추가

System/MacActions.swiftshortcuts:// 호출 패턴을 본떠, \"밴드를 더블탭하면 특정 macOS 단축어 실행\"에 새 동작을 하나 추가한다. 샌드박스 안에서 외부 동작을 안전하게 트리거하는 방법을 익힌다.

9관련 기술 심화 학습 로드맵

주차별 4주 계획 — 웨어러블 + 로컬 우선 앱
주차주제할 일
1주차BLE / 바이너리 프로토콜GATT(서비스·특성·notify) 개념, Web Bluetooth로 스캔 실습, CRC 체크섬 직접 계산
2주차로컬 우선 데이터 저장SQLite + GRDB(또는 Room) 익히기, 버전 마이그레이션, DB 변화 관찰로 UI 갱신
3주차생체신호 분석 수학HRV(RMSSD/SDNN), %HRR·TRIMP, z-score 기준선, 상관/회귀를 코드로 옮기기
4주차멀티플랫폼 + 대용량 처리순수 로직을 Swift↔Kotlin 포팅, SAX 스트리밍 파서로 대용량 XML 처리, 샌드박스 권한
학습 팁

이 레포는 \"웨어러블 데이터 앱을 어떻게 처음부터 끝까지 만드나\"의 완결된 예제다. 센서에서 바이트를 읽는 가장 낮은 층부터, 점수를 계산하는 수학, 화면에 그리는 UI까지 한 줄로 꿰여 있다. 한 층씩 \"이 층의 입력과 출력은 무엇인가\"를 적어 나가면, 4주 뒤엔 다른 센서(스마트워치·체중계 등)로도 같은 골격을 옮길 수 있다.

10핵심 키워드 사전

이 프로젝트에 등장하는 용어 빠른 정리
용어
HRV · RMSSD / SDNN
HRV(심박 변이도)는 심장 박동 간격이 얼마나 들쭉날쭉한가로, 자율신경(회복) 상태의 지표다. RMSSD는 연속한 R-R 간격 차이의 제곱평균제곱근, SDNN은 R-R 간격의 표준편차. NOOP은 Task Force 1996 정의를 따르고 Malik 규칙으로 비정상 박동(이소성)을 걸러 낸다.
용어
Recovery / Strain
회복(Recovery)은 \"오늘 몸이 얼마나 준비됐나\"를 0~100으로, 스트레인(Strain)은 \"오늘 얼마나 부하를 줬나\"를 0~21 로그 스케일로 나타낸다. NOOP은 회복을 HRV 중심 z-score 로지스틱으로, 스트레인을 %HRR(Karvonen)과 TRIMP로 개인 기준선 대비 계산한다.
용어
R-R 간격 (R-R Interval)
심전도에서 한 박동의 R파와 다음 R파 사이 시간(ms). 심박수의 \"원재료\"이며 HRV 계산의 입력이다. NOOP은 밴드에서 이 값을 직접 받아 rrInterval 테이블에 저장하고, Breathe 화면에서 호흡에 따라 변하는 RMSSD를 실시간으로 보여 준다.
용어
GATT 서비스 / 특성 (Service / Characteristic)
BLE 기기가 데이터를 노출하는 단위. 기기는 \"서비스\"(예: fd4b0001-…) 아래에 여러 \"특성\"을 두고, 앱이 특성에 notify를 켜면 값이 바뀔 때마다 받는다. NOOP은 WHOOP 4/5의 서비스 UUID를 단서로 세대를 구분하고 올바른 디코더를 고른다.
용어
SAX 파서 (스트리밍 XML 파싱)
XML을 통째로 메모리에 트리로 올리는 DOM 방식과 달리, \"요소 시작/끝\" 같은 이벤트를 만날 때마다 콜백으로 처리하고 흘려보내는 방식. 1GB가 넘는 Apple Health export.xml도 메모리 폭발 없이 읽을 수 있다. NOOP의 AppleHealthImporter가 이 방식을 쓴다.
용어
샌드박스 / Hardened Runtime
샌드박스는 앱이 선언한 권한(블루투스·선택 파일 등) 밖으로 손대지 못하게 가두는 격리. Hardened Runtime은 코드 주입(DYLD_INSERT_LIBRARIES) 같은 공격 통로를 막는 보안 강화 모드. NOOP은 둘 다 켜서, \"내 데이터만 다루고 밖으로 안 샌다\"는 약속을 OS 수준으로 뒷받침한다.

11참고 링크

더 깊이 파고들 곳