
Clinical Research · Preventive Care
HAIM HAIM
뇌졸중 위험도 예측 · 맞춤 건강관리 연구용 앱 + 연구자 관리 콘솔
- 클라이언트
- 강원대학교 × HB Connect
- 기간
- 2026.07 — 2026.09 · 약 6.5주
- 역할
- 앱 · 서버 · 관리자 웹 전권 단독 개발
- 팀 구성
- 개발자 1인(본인) + 기획자 + 연구진
- 플랫폼
- Android · Web(Admin)
- 상태
- v1.0.1 Play 스토어 빌드 완료, 연구 참여자 배포
약 4만 줄
총 코드
44%
테스트 비중 · 앱 기준
282
커밋
6.5주
개발 기간
18
SQL 마이그레이션
Overview
프로젝트 개요
HAIM은 60대 이상 연구 참여자를 대상으로 뇌졸중 위험도를 예측하고 12주간 맞춤 건강관리를 지원하는 연구용 헬스케어 앱입니다. 연구진이 PPT · 엑셀로 정리한 위험도 알고리즘(ver.2)을 TypeScript로 이식해 검증했고, '나의건강기록' FHIR 건강검진 파일을 임포트해 위험요인을 산출합니다.
혈압 · 혈당 · 체중 자가 측정, Health Connect 운동 기록, 온심 서버 연동 운동 처방, 30화 건강교육 카드뉴스와 퀴즈, 목표 실천율 트래킹을 제공하며, 연구자용 관리자 웹에서 가명화된 데이터셋 · 통계 · 접근 로그를 관리합니다. 6.5주 만에 앱 · 서버 · 관리자 웹 · 배포 파이프라인 · 백업까지 완성했습니다.
My Role
담당 업무
- 01Flutter 앱 설계 · 구현 (72 파일 1.6만 줄) + 테스트 43 파일 7천 줄 (프로덕션 대비 약 44%)
- 02NestJS 서버(125 파일 1.6만 줄) — 위험도 엔진, FHIR 파서, 역할 기반 접근 제어, 필드 암호화
- 03React + Vite 관리자 웹 — 참여자 관리, CSV, 통계 대시보드, 접근 로그, 설문 등록 · 마감
- 04Supabase(PostgreSQL) 마이그레이션 18개 · Firestore 이중 저장소 설계
- 05Android Health Connect 네이티브 채널(Kotlin) 직접 구현
- 06Docker → AWS ECR/ECS CD, Firebase Hosting, S3 백업(pg_dump + Firestore 증분) 파이프라인
Features
핵심 기능
뇌졸중 위험도 스코어링 엔진
설문 + 건강검진 원자료 → 기간별(P1~P5) 위험요인 A~H 판정 → 조합별 발생 확률. log-LR 확률 테이블(남 812KB · 여 527KB)을 JSON으로 내장.
FHIR 건강검진 임포트
'나의건강기록' FHIR JSON을 사용자가 직접 선택 → 파싱 → 회차별 수치 그래프로 검토 후 제출. LOINC PHR · MyHealthWay 파서.
건강기록 · 추이 그래프
혈압 · 혈당 · 체중 · 허리둘레 시계열 입력, 4주 · 12주 선 그래프(기준선 포함).
Health Connect 운동 기록
플러그인 없이 네이티브 채널로 기기 측정 운동 세션을 읽어 기록, {source}-{시작시각} 키로 중복 제거.
온심 연동 운동 처방
앱 → HAIM 서버 → 온심 서버로 맞춤 운동 처방 발급 · 재발급. 외부 장애 시에도 앱은 정상 동작.
건강교육 · 목표 실천율
「아몰랑의 건강생활」 30화 카드뉴스 회차별 개방, 열람 기록 · 퀴즈 응답, 84일 목표 실천율, 만족도 · 사용성 설문, FCM 푸시.
연구자 관리 콘솔
참여자 목록 · 상세 · CSV, 가명화 연구 데이터셋, 통계 대시보드, 접근 감사 로그, 관리자 권한 이력, 역할 3단계.
Architecture
설계 원칙
재식별 방지 이중 저장소
Supabase에는 연구코드 · 내부 uuid만(이름 없음), Firestore의 이름 · 생년월일은 서버가 AES-256-GCM으로 암호화(키는 AWS SSM). 두 저장소를 잇는 매핑은 서버만 보유.
API 계약 3지점 동기화
앱 submission.dart ↔ 서버 contract.ts ↔ DB check 제약을 '하나 바꾸면 셋 다' 규칙으로 문서화.
닫힘 기본값 권한 가드
ROLE_ACCESS 단일 표로 역할 접근을 관리하고, @Roles가 없는 엔드포인트는 전원 차단.
법적 요구사항의 코드화
관리자 권한 변경 이력을 DB 트리거로 적재(개인정보 안전성 확보조치 기준 제5조, 3년 보관)하고 서버 키의 update · delete 권한을 회수해 위변조 불가. 개인정보처리방침은 웹 링크(법 제30조).
60대+ 접근성 전제
전역 텍스트 배율 kTextScale = 1.3 단일 진입점, 큰 터치 영역, 다크모드 미제공 등 타깃 사용자 전제를 코드 규칙으로.
Challenges
기술 과제와 해결
연구 알고리즘의 검증 가능한 이식
Problem
위험도 모델이 PPT와 엑셀로만 존재해 앱 · 서버에서 동일하게 계산된다는 보장이 없었습니다.
Solution
알고리즘 예시 20행 전부 일치, 남 · 여 확률 소수점 9자리 일치, 파이썬 참조 구현과 무작위 300건 교차검증(1e-12 이내)을 테스트로 고정하고, 모듈 수정 시 샘플 CSV 재생성 스크립트까지 파이프라인화했습니다.
결과 · 연구진이 결과를 신뢰할 수 있는 근거 확보
미공개 연구 모델 보호
Problem
순수 계산기 엔드포인트라도 응답을 반복 관찰하면 확률 테이블을 복원할 수 있습니다.
Solution
계산기 엔드포인트에도 인증을 강제해 비인가 호출을 차단했습니다.
Health Connect 플러그인이 삼키는 오류
Problem
health 플러그인이 세션마다 거리 · 열량 · 걸음수를 강제로 함께 읽고, 권한이 없으면 오류를 삼킨 채 0건을 반환했습니다.
Solution
네이티브 채널을 직접 구현해 필요한 데이터만 읽고 오류를 그대로 전파하도록 교체했으며, 불필요한 BLE 의존성도 함께 제거했습니다.
데이터 품질 가드
Problem
부분 동기화가 기존 기록을 덮어쓰거나, 소급 기억으로 과거 기록이 입력될 위험이 있었습니다.
Solution
운동 기록은 총 운동시간이 커질 때만 반영(무시돼도 200 + applied:false), 일일 기록은 오늘 · 어제만 허용, 검진 기록이 없는 기간도 반드시 '미검진'으로 기록해 위험도 산출 입력을 보장했습니다.
Firebase 플러그인 동반 버전 상승
Problem
firebase_messaging 추가로 core가 올라가자 auth가 customAuthDomain 심볼을 찾지 못해 APK 빌드가 깨졌습니다. flutter test로는 잡히지 않는 문제였습니다.
Solution
호환 버전 조합을 고정하고 pubspec 주석으로 원인과 재발 조건을 문서화했습니다.
한글 IME · Android 12 스플래시
Problem
이름 검증이 자모 조합 중에 실패하고, 스플래시 에셋이 Android 12+ 규격에서 세로로 늘어났습니다.
Solution
검증을 '다음' 클릭 시점으로 옮기고, 규격별 스플래시 이미지를 생성하는 스크립트를 작성했습니다.
Tech Stack
기술 스택
App
- Flutter 3.44 · Dart 3.12
- InheritedWidget 기반 DI + ValueNotifier (외부 상태 라이브러리 없음)
- Firebase Auth · Messaging
- file_selector (FHIR JSON)
- clock (테스트 시간 조작)
- Kotlin Health Connect 채널
Server
- NestJS 11 · TypeScript 5.7
- Supabase (PostgreSQL) · Firestore
- AES-256-GCM 필드 암호화 (AWS SSM)
- Jest · Supertest 스펙 32개 + e2e
- Docker · AWS ECR/ECS · GitHub Actions
Admin · Ops
- React 18 · Vite 6 · react-router 6
- Firebase Hosting
- S3 백업 (pg_dump + Firestore 증분 48h)
- lint · build · test · e2e 커밋 전 필수
Outcomes
성과
- ✓6.5주 만에 앱 · 서버 · 관리자 웹 · CD · 백업 파이프라인을 완성해 Play 스토어 빌드(1.0.1) 납품
- ✓연구 알고리즘을 1e-12 정밀도로 교차검증한 스코어링 엔진 제공
- ✓개인정보보호법 · 안전성 확보조치 기준을 DB 트리거와 권한 설계로 구현
Next Project
온심 onsim