인도에 꼭 필요했던 헬스케어 앱을 만들었습니다: 그리고 포기했었죠. 어떻게 완성했는지 소개합니다.
요약
인도의 ABDM 인프라를 활용하여 처방전을 디지털 건강 기록으로 변환하는 헬스케어 앱 CureNet의 개발 여정을 소개합니다. NVIDIA NIM Vision과 Bhashini ULCA를 활용해 문서 스캔, 데이터 구조화, 다국어 지원 기능을 구현했습니다.
핵심 포인트
- NVIDIA NIM Vision을 통한 처방전의 FHIR R4 데이터 변환
- ABDM 준수 인증 및 RSA-OAEP 암호화 적용
- Bhashini ULCA를 활용한 인도 22개 언어 지원
- 생체 인식 기반의 보안 헬스 로커 기능 구현
이 글은 GitHub Finish-Up-A-Thon Challenge를 위한 제출물입니다.
내가 만든 것
CureNet — 정부의 ABDM (Ayushman Bharat Digital Mission) 인프라를 통해 모든 인도 시민에게 의료 기록에 대한 실질적인 소유권을 부여하는 모바일 앱입니다.
핵심 컨셉은 간단합니다: 휴대전화 카메라로 어떤 처방전이든 스캔하면, CureNet이 이를 구조화된 FHIR R4 준수 디지털 건강 기록으로 변환하여 사용자의 ABHA (Ayushman Bharat Health Account)에 연결합니다. 수동 데이터 입력도, 병원 포털도 필요 없습니다. 그저 가리키고, 스캔하고, 소유하면 됩니다.
이 프로젝트는 대학교 학기 과제로 시작되었습니다. 우리에게 주어진 시간은 3주였습니다. 저는 ABDM 인증 흐름(authentication flow), 기본적인 AI 채팅, 그리고 기록 목록을 구축했지만, 시간이 부족했습니다. UI는 조잡했고, 스캐너는 문서의 절반 정도에서 작동하지 않았으며, "AI 어시스턴트"는 의학적 조언을 환각(hallucination)했습니다. 저는 제가 가진 것을 제출하고, 성적을 받은 뒤 다음 단계로 넘어갔습니다.
5개월 후, 저는 다시 저장소(repo)를 열었습니다.
코드는 // TODO 주석과 하드코딩된 데모 데이터로 가득한 공동묘지 같았습니다. 하지만 _아이디어_는 여전히 좋았습니다. 인도의 14억 인구는 휴대 가능한 건강 기록이 없습니다. 정부는 철로(ABDM)를 건설했지만, 일반 사람들이 실제로 탈 수 있는 기차를 만든 사람은 아무도 없었습니다. 그래서 저는 시작했던 일을 끝내기로 결심했습니다.
오늘날 CureNet이 하는 일
- 🔐 ABDM 준수 인증 (ABDM-Compliant Auth) — Aadhaar, 모바일, ABHA 번호 또는 ABHA 주소로 로그인합니다. 실제 RSA-OAEP 암호화를 사용하며, 실제 ABDM 샌드박스 (Sandbox) 연동을 지원합니다.
- 📸 AI 문서 스캐너 (AI Document Scanner) — 카메라를 처방전, 검사 결과 보고서 또는 퇴원 요약서에 가져다 대세요. NVIDIA NIM Vision이 문서를 분류하고 약물, 복용량, 검사 수치 및 진단명을 추출하여 이를 모두 FHIR R4 번들 (bundles)로 패키징합니다.
- 🤖 ABHAy AI — 사용자의 건강 맥락을 이해하는 의료 AI 어시스턴트입니다. 스캔된 기록을 읽고, 복용 중인 약물과 상태를 파악하며, 사용자의 언어(Bhashini ULCA를 통해 지정된 인도의 22개 언어 전체 지원)로 질문에 답변합니다.
- 🔒 헬스 로커 (Health Locker) — 가장 민감한 기록을 보관하기 위한 생체 인식 보호 금고입니다. 지문 또는 Face ID로 접속할 수 있습니다.
- 📊 건강 트렌드 (Health Trends) — 대화형 차트를 통한 바이오마커 (Biomarker) 추적 기능입니다. HbA1c, 혈압 또는 콜레스테롤 수치의 시간에 따른 변화 추이를 확인하세요.
- 🚨 긴급 디지털 패스 (Emergency Digital Pass) — 알레르기, 복용 약물, 혈액형 및 비상 연락처가 포함된 원터치 긴급 카드를 제공합니다. 잠금 화면용 이미지로 저장할 수 있습니다.
- 🔑 동의 기반 공유 (Consent-Based Sharing) — QR 코드를 통해 의사에게 제한된 시간과 범위 내에서 기록에 접근할 수 있는 권한을 부여합니다. 언제든지 권한을 취소할 수 있습니다.
- 🌐 다국어 지원 (Multilingual) — Bhashini ULCA를 통해 인도의 지정된 22개 언어 전체에 대해 전체 UI 및 AI 번역을 지원합니다.
기술 스택 (Tech Stack): Flutter/Dart · Node.js/Express · MongoDB · NVIDIA NIM (Llama 3.2 90B Vision) · FHIR R4 · ABDM Sandbox · Bhashini ULCA · Render
데모 (Demo)
🔗 GitHub: github.com/labishbardiya/CureNet
🔗 GitHub Release: https://github.com/labishbardiya/CureNet/releases/tag/v2.0.0
📱 APK 다운로드: CureNet APK 다운로드
🖥️ 라이브 데모: https://youtu.be/QU5zbPB3XJw
🌐 라이브 백엔드: curenet.onrender.com
재기 스토리 (The Comeback Story)
과거의 모습 (2026년 1월)
제가 2월에 CureNet을 중단했을 당시, "존재했던" 것들은 다음과 같습니다:
| 기능 | 상태 |
|---|---|
| ABDM 로그인 | ✅ 작동 중 (겨우) — 단일 인증 방식, 암호화 없음 |
| ... |
UI는 대학생 프로토타입(Prototype)처럼 보였는데, 실제로 대학생 프로토타입이었기 때문입니다. 제대로 된 아이콘 대신 텍스트 화살표(←)를 사용했습니다. 하드코딩된 이름들이 곳곳에서 유출되고 있었습니다. 프로덕션(Production) 코드에 print() 문이 남아 있었고, 지원 중단된(Deprecated) API 호출이 있었으며, 모든 파일에는 사용하지 않는 임포트(Import)가 10개씩 쌓여 있었습니다.
변화된 점 (2026년 5월~6월)
저는 CureNet을 밑바닥부터 다시 구축했습니다. 코드베이스는 같지만, 완전히 다른 앱이 되었습니다. 변화의 과정은 다음과 같습니다:
🧠 AI 아키텍처 (Architecture) 개편
- 기본적인 Groq completion에서 **골드 스탠다드 아키텍처 (Gold Standard Architecture)**로 마이그레이션(Migrate)했습니다: 시각적 OCR을 위한 NVIDIA NIM → 실시간 스트리밍을 위한 서버 전송 이벤트 (Server-Sent Events) → 속도 제한(Rate-limit) 탄력성을 위한 3개의 API 키 간 스마트 라우팅 (Smart Routing).
- 이제 ABHAy AI는 완전한 의료적 문맥(Context)을 갖추고 있습니다. 사용자가 업로드한 기록을 읽고, 복용 중인 약물을 알며, 사용자의 실제 건강 데이터에 기반하여 응답을 생성합니다. 복용하지 않는 약물에 대한 환각(Hallucination) 현상이 더 이상 발생하지 않습니다.
- 22개의 지정된 인도 언어 전체에 대해 Bhashini ULCA 파이프라인을 통한 실시간 번역 (Real-time translation) 기능을 추가했습니다. AI는 사용자가 질문한 언어로 응답합니다.
📸 문서 스캐너 재구축
- 고장 난 OCR 파이프라인을 NVIDIA NIM Llama 3.2 90B Vision으로 교체했습니다.
- 지능형 문서 분류 기능을 추가했습니다: 처방전, 검사 결과 보고서, 퇴원 요약서 등 각 문서 유형에 따라 다르게 파싱(Parsing)됩니다.
- 모든 스캔 결과는 국제 의료 데이터 표준인 **FHIR R4 번들 (FHIR R4 Bundle)**을 생성합니다. 단순한 텍스트 추출을 넘어 구조화된 임상 데이터를 제공합니다.
🏗️ 6가지 신규 기능
- 생체 인증(Biometric authentication)이 포함된 헬스 로커 (Health Locker)
- fl_chart를 활용한 대화형 바이오마커 추세 (Biomarker trends)
- 긴급 디지털 패스 (Emergency Digital Pass, 스크린샷 저장 가능)
- 시간 제한 액세스 권한 부여가 포함된 QR 기반 동의 공유
- 실시간 알림이 포함된 액세스 요청 모니터링
- 수정 가능한 의료 정보가 포함된 프로필
💅 엔터프라이즈급 완성도 (Enterprise-Grade Polish)
- 공유된 하단 네비게이션(bottom navigation)을 재사용 가능한
CureNetBottomNav위젯으로 추출 (7개 화면에 걸친 약 210줄의 중복 코드 제거) - 모든
print()를debugPrint()로 교체 - 모든 지원 중단(deprecated)된
withOpacity()호출을withValues(alpha:)로 마이그레이션 - 사용되지 않는 모든 임포트(import), 데드 메서드(dead method), 고립된 변수 제거
flutter analyze: 에러 0개, 경고 0개 (구조적 정보(structural infos) 2개만 남음)- 실제 사용자에게 유출되는 하드코딩된 데모 데이터 제로 — 모든 데모 콘텐츠는 신원 확인(identity checks)을 통해 제한됨
- 모든 곳에 적절한 Material 아이콘 적용 (텍스트 화살표 "←" 버튼 더 이상 사용 안 함)
☁️ 클라우드 배포 (Cloud Deployment)
- Nginx SSE 버퍼링 수정 사항을 적용하여 Render에 백엔드 배포
- 지속적 저장소(persistent storage)를 위해 MongoDB Atlas 사용
- 실제 RSA-OAEP 암호화를 적용한 ABDM Sandbox 통합
수치로 보는 결과
| 지표 | 이전 | 이후 |
|---|---|---|
| Dart 파일 수 | ~15 | 57 |
| ... |
GitHub Copilot 사용 경험
GitHub Copilot은 이 마무리 작업 전 과정에서 저의 상수 페어 프로그래머(pair programmer)였습니다. 구체적으로 어떻게 도움이 되었는지 소개합니다:
1. 보일러플레이트(Boilerplate) 가속화
Flutter는 장황합니다. 새로운 화면마다 StatefulWidget, State 클래스, initState, dispose, build 메서드, Scaffold, SafeArea 등이 필요합니다... Copilot은 이러한 구조적 패턴을 즉시 자동 완성해 주었습니다. CureNet의 28개 화면을 기준으로, 스캐폴딩(scaffolding) 작업에서만 수 시간을 절약할 수 있었습니다.
2. FHIR R4 번들 생성
FHIR 규격을 준수하는 JSON 구조를 수동으로 작성하는 것은 매우 고통스러운 일입니다. 엄격한 필드 요구 사항을 가진 깊게 중첩된 리소스(resources) 때문입니다. 제가 createPrescriptionBundle(을 입력하면, Copilot은 적절한 resourceType, entry 배열, Patient, Practitioner, MedicationRequest 리소스를 포함한 전체 번들(Bundle) 구조를 제안했습니다. 여전히 규격 준수 여부를 확인해야 했지만, 시작점은 90% 이상 정확했습니다.
3. Dart API 마이그레이션
Flutter의 API는 빠르게 진화합니다. Copilot은 제가 미처 알지 못했던 지원 중단(deprecated) 패턴을 포착해냈습니다. 예를 들어, 제가 withOpacity(를 입력했을 때 withValues(alpha: 0.5)를 제안하거나, Switch 위젯에서 activeColor 대신 activeThumbColor를 사용하도록 표시하고, 비동기 BuildContext 사용 시 mounted 가드(guard)를 권장했습니다. 마치 실시간 마이그레이션 가이드처럼 작동했습니다.
4. SharedPreferences 패턴
CureNet의 오프라인 우선(offline-first) 아키텍처는 기록 영속성을 위해 SharedPreferences에 크게 의존합니다. Copilot은 프로젝트의 DataMode.storageKey() 네임스페이스(namespacing) 패턴을 학습하여, 적절하게 네임스페이스가 지정된 키를 일관되게 제안했습니다. 제가 저장 로직을 작성하면, 올바른 JSON 인코딩(encoding) 패턴을 포함한 그에 상응하는 로드(load) 로직을 제안해 주었습니다.
5. 디버깅을 위한 Copilot Chat
Render에 배포한 후 SSE (Server-Sent Events) 스트리밍이 끊겼을 때, 저는 Nginx 설정을 Copilot Chat에 붙여넣고 "왜 30초 후에 SSE 연결이 끊어지나요?"라고 물었습니다. Copilot은 즉시 누락된 proxy_buffering off 지시어를 찾아냈습니다. 만약 직접 해결하려 했다면 Stack Overflow를 뒤지며 몇 시간은 허비했을 문제였습니다.
솔직한 소회
Copilot이 CureNet을 대신 써준 것은 아닙니다. 하지만 프로젝트를 완성하는 것을 어렵게 만드는 마찰(friction)을 제거해 주었습니다. 보일러플레이트(boilerplate), 타입 어노테이션(type annotations), JSON 파싱(parsing), 임포트(import) 관리와 같은 지루한 부분들은 백그라운드에서 처리되었고, 덕분에 저는 실제 어려운 문제들인 ABDM 암호화, FHIR 준수, 그리고 사용자의 복약 목록을 환각(hallucinate)하지 않는 AI 어시스턴트를 만드는 데 집중할 수 있었습니다.
이것이 바로 "완성"에 필요한 핵심입니다. 마법 지팡이가 아니라, 당신이 본업에 집중할 수 있도록 지루한 작업을 대신 처리해 주는 지치지 않는 파트너 말입니다.
CureNet은 피드백을 기다립니다. 인도 헬스텍(healthtech) 분야에서 개발 중이거나 ABDM과 관련된 작업을 하고 계신다면, 꼭 연락해 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기