Skip to content

Repository files navigation

 __      __            _    ____  _ _ _    _               _
 \ \    / /_ _ _ _ __ | |  |  _ \(_) | |  / _|___ ___ _ __| |_
  \ \/\/ / _` | '_/ _` |  | |_) | | | | | (__/ _ / -_) '_ | / /
   \_/\_/\__,_|_| \__,_|  |  __/|_|_|_|  \___\___\___| .__|_\_\
                          |_|        💊             |_|
        ward-pillcheck — 병동 지참약 식별 PWA

ward-pillcheck · 병동 지참약 식별 PWA

병동 간호사가 환자가 가져온 약(지참약) 을 현장에서 빠르게 식별·정리하는 모바일 PWA. 약 이름을 몰라도 색 + 모양 + 각인 + 마크로 거꾸로 찾고, 주사제·수기 약도 더해 환자별 복약 리스트를 만들어 정렬·금기점검·인계 복사까지. 데이터는 기기에만.

🔗 : https://yuangunn.github.io/ward-pillcheck/

About

병동에서 입원 환자의 지참약(가져온 약) 을 식별하고 복약 리스트로 정리해 인계까지 돕는 모바일 PWA입니다. 지참약은 보통 이름표 없이 낱알만 오므로, 식약처 공공데이터를 활용해 색·모양·각인·마크(그림) 같은 겉모습으로 거꾸로 찾는 데 초점을 맞췄습니다.

  • 🔍 식별 방법: ① 실물검색(색·모양·각인·마크 갤러리) ② 이름검색 ③ 주사제(이름) ④ 직접입력(수기)
  • 🗂 데이터: 식약처 낱알식별·허가정보·e약은요·DUR. 낱알·주사제·마크는 기기에 번들로 내려받아 오프라인·즉시 검색(매일 자동 갱신)
  • 🛡 안전: 환자 리스트에 대해 DUR 금기·중복 점검(병용금기·임부·노인·연령·효능군중복) + 성분 중복(복합제를 성분 단위로 분해해 같은 활성성분을 여러 약에서 찾음) + 분할·분쇄 주의(서방·장용 제형은 부수면 위험) — 모두 완전 오프라인
  • 🔒 개인정보: 환자는 병상번호 권장(이름도 입력 가능), 주민번호·등록번호 입력란 없음, 모든 기록은 기기 localStorage 에만 — 외부 서버 전송 없음. 인계는 원내 Teams(병원 도메인)로만
  • 📱 UX: 모바일 세로 우선, 한 손 조작. 홈(환자) → 환자(복약 리스트) → 검색 흐름, Toss 스타일 디자인, 라이트/다크(기기 설정 자동), 설치형 PWA(오프라인 셸)
  • ⚖️ 주의: 처방·복약지도 자동화가 아닌 식별 보조 도구이며 최종 확인은 의료진 책임

기능

기능 설명
실물 검색 색 스와치 + 모양 칩(색·모양 모두 여러 개 선택 — 타원형/장방형처럼 분류가 애매해도 한 번에) + 앞면 각인으로 거꾸로 찾기(기본 탭). 비슷한 계열 색 한 번에: 헷갈리기 쉬운 색은 계열로 묶어, 한 색을 처음 누르면 계열 전체가 함께 선택(분홍→주황·빨강, 연두↔초록↔청록, 파랑↔남색, 자주↔보라) — 켜기는 계열 통째로, 끄기는 개별로 미세조정. 각인은 실제 인쇄 각인만(마크는 별도). 거꾸로 집은 알약 보정: 각인을 180° 뒤집어도 매칭(예: 각인 SIHHIS로 검색해도 일치). 혼동문자 보정(3글자+): 마모·오독으로 헷갈리는 글자(0↔O↔D · 1↔I↔L · 5↔S · 8↔B · 2↔Z · 6↔G)도 매칭(예: 0LTDLT 검색) — 정확 일치를 앞에 두고 그 뒤에 표시
마크로 찾기(골라서) 약에 새겨진 그림(마크)을 이미지 갤러리에서 골라 검색(번들 마크 이미지 431종)
마크 글자로 찾기 마크에 새겨진 글자/모양 이름을 타이핑(예: G, MF, 삼각형). 식약처가 같은 마크를 «ㄷㄱ,G»처럼 자모+라틴 둘 다 적어둔 경우 G로도 찾아짐(대소문자 무시·시작일치)
마크로 찾기(그려서) 캔버스에 마크를 그리면 번들 마크와 온디바이스 형상 유사도로 닮은 후보 제시(서버·API 0). 그린 게 글자/숫자면 OCR로 인식(베타·Tesseract 동적 로드) → 마크 글자 검색에 연결(예: G 그리기→«ㄷㄱ,G» 찾기). OCR 모델(~5.8MB)은 자체호스팅·초기 프리캐시 → 한 번 받으면 오프라인/인트라넷에서도 인식
이름 검색 품목명으로 검색
외용·주사제 검색 흡입제(벤토린)·좌약(둘코락스)·연고·점안 등 외용약과 주사제(인슐린)를 이름으로 검색(허가정보 번들). 낱알 모양 없는 약 대응
직접 입력 목록에 없는 약·주사제를 수기로(이름 + 용량 단위 T/U/mL/회분/포 + 용법·시점)
결과 카드 품목명·업체·색/모양/각인(앞·뒤, 검색어 강조)·분할선·제형. 각인이 180° 뒤집어 일치하면 ↕ 거꾸로 보면 일치, 혼동문자로만 일치하면 ≈ 비슷한 각인 배지. 약 글리프 탭 → 확대(실사진 원본 링크)
상세 정보 실물사진 + 외형요약(성분·분류·구분·제형·색·모양·각인·분할선) + 효능·용법·주의·상호작용·이상반응·보관(e약은요→허가정보 폴백, 출처 표시) + 사진 원본 링크. 성분은 한글 주성분(영문 INN 병기) — 한글은 허가상세 MAIN_ITEM_INGR 를 번들
환자 리스트 추가 정제수(0.5 단위)·용법·투약시점. 색/모양/각인 자동채움(수정 가능)
투약시점 多 용법에 맞춰 시점 칸 자동 — QD=1, BID=2, TID=3, QID=4 + 직접입력
환자 관리 익명 라벨 추가/전환, 제목 옆 > 또는 ⋯ 로 이름변경·삭제
정렬 기본순 / 용법순 / 시점순 (환자별 저장)
인계(원내 Teams 전용) 복약 리스트를 한 줄 포맷 텍스트로 원내 Teams로만 전송(병원 도메인/.ac.kr 계정만 허용). 외부 복사·시스템 공유는 제공 안 함. 공유 상세설정에서 성분명(한글 괄호)·영문 품목명·겉모습 표시를 각각 토글(기기에 저장). 영문 품목명은 허가정보 **품목영문명(ITEM_ENG_NAME)**을 이름 뒤 대괄호로 병기(예: 모티리톤정 [Motilitone Tab.]) — 직접입력·영문명 미등록 약은 생략
금기·중복 점검(DUR) 리스트에 대해 병용금기·임부·노인·연령 금기·효능군 중복 자동 점검
성분 중복 점검 복합제를 개별 성분으로 분해(예: 아모잘탄=암로디핀+로사르탄)해, 같은 활성성분이 여러 약에 겹치면 경고(중복투약 위험). 염·수화물·제형 접미어를 떼어 매칭, 워커 없이 완전 오프라인
분할·분쇄 주의 서방·장용 제형(서방정·장용정 등)은 쪼개거나 갈면 약효 급변·위장 자극 위험 → 안전점검에서 분할·분쇄 주의 경고. 제형명 기반, 완전 오프라인
DB 정보·업데이트 홈에서 약품 데이터 건수·최근 업데이트일 표시 + 수동 업데이트
의약품 검색(독립) 홈 상단 [지참약 식별 / 의약품 검색] 탭. 환자와 무관하게 경구약·주사제·외용약을 이름으로 찾아 상세만 열람(약 누르면 상세 시트, 사진 탭 시 확대)
약 상세 보기 환자 리스트의 약 그림 탭 또는 의약품 검색 결과 탭 → 상세 시트. 사진 탭하면 인앱 확대
다크 모드 헤더 🌙/☀️ 토글(제목 탭과 같은 줄 정렬), 기기 설정 자동 감지
자동 업데이트 새 배포 감지 시 서비스워커가 자동 적용·새로고침(설치형 PWA도 수동 강제새로고침 불필요)

저장 항목 한 줄 표시 포맷:

아스피린장용정100mg 1T QD 아침식후 (흰/원형/Bayer)
메트포르민500mg 1T BID 아침식후,저녁식후 (흰/원형/MF500)
란투스주솔로스타펜 10U BID 아침식전,저녁식전

기술 스택 / 구조

Vite + React + TypeScript · vite-plugin-pwa · @dnd-kit · 상태는 Context + useReducer

  • localStorage. 데이터 접근은 추상화 계층(src/api) 뒤에 두고, 검색은 기기 번들 데이터셋(IndexedDB/정적파일), 상세·이미지는 Cloudflare Worker 프록시로 분리.
src/
  api/        types · workerClient · bundledClient · mockClient · index(팩토리)
              dataset(번들 낱알/마크 IndexedDB) · dur(금기점검) · permit(주사제·허가)
              marksim(그려서 마크 찾기 — 온디바이스 형상 유사도)
  domain/     models · format(한 줄 포맷) · sort(정렬)
  constants/  frequency(용법·시점칸) · timing · appearance(색/모양)
  state/      store(Context+reducer) · persist(localStorage) · useDataset
  design/     theme(토큰·라이트/다크) · Icon(+PillGlyph/MarkGlyph) · ui · screens · sheets
worker/       Cloudflare Worker(인증키 보관 + CORS + 이미지 프록시)
scripts/      build-dataset.mjs(낱알·주사제·마크 번들 생성)

로컬 개발 / 테스트

npm install
npm run dev        # http://localhost:5173 (VITE_API_BASE 없으면 데모/목 모드)
npm run build      # 타입체크 + 프로덕션 빌드
npm test           # Vitest 단위/통합 (60+ 케이스)
npm run e2e        # Playwright 실브라우저 E2E (최초 1회 npx playwright install chromium)

VITE_API_BASE 가 비어 있으면 목(mock) 모드 — 인증키 없이 샘플 데이터로 UI/흐름 확인. CI(.github/workflows/ci.yml): PR/푸시마다 typecheck + Vitest + 빌드 + E2E.

배포

1) data.go.kr 인증키 (5종 API, 하나의 키)

공공데이터포털에서 아래를 활용신청(개발계정·자동승인) 후, 마이페이지의 일반 인증키(Decoding) 를 사용합니다(코드/저장소엔 절대 넣지 않음).

데이터 ID 용도
의약품 낱알식별 정보 15057639 색·모양·각인·마크 검색
의약품개요정보(e약은요) 15075057 효능/용법/주의 상세
의약품 제품 허가정보 15095677 주사제 이름검색 · 상세 폴백
의약품안전사용서비스(DUR) 품목정보 15059486 금기·중복 점검
(선택) DUR 성분정보 15056780 성분 기준 보조

엔드포인트 버전 suffix 는 갱신될 수 있어 Worker 의 *_ENDPOINT 환경변수로 덮어쓸 수 있습니다.

2) Cloudflare Worker (인증키 보관 + 프록시)

cd worker
npm install
npx wrangler login
npx wrangler secret put SERVICE_KEY   # data.go.kr Decoding 키
npm run deploy

출력된 https://<name>.<subdomain>.workers.dev 가 프론트의 VITE_API_BASE 값입니다. 운영 시 wrangler.tomlALLOW_ORIGIN 을 Pages 도메인으로 제한 권장.

Worker 엔드포인트

경로 식약처 API / 역할
GET /api/pills 낱알식별 getMdcinGrnIdntfcInfoList03
GET /api/detail?itemSeq= e약은요 getDrbEasyDrugList
GET /api/permit?itemSeq=&item_name= 제품 허가정보 — 효능/용법/주의 폴백 + 주성분(허가목록 ITEM_SEQ 일치 매칭)
GET /api/drugsearch?item_name=&inj=1 허가정보 이름검색(주사제)
GET /api/dur?itemSeq= DUR 품목정보(병용금기·임부·노인·연령·효능군중복)
GET /api/img?u= nedrug 이미지 프록시(헤더 정리 + CORS)

3) GitHub Pages

main push 시 .github/workflows/deploy.yml 가 데이터셋 빌드 → 빌드 → 배포.

  1. Settings → Pages → Source: GitHub Actions
  2. Settings → … → Variables: VITE_API_BASE = Worker URL
  3. Settings → … → Secrets: SERVICE_KEY = data.go.kr Decoding 키(데이터셋 빌드용)
  4. base 경로는 저장소명 기준 /ward-pillcheck/ (다르면 VITE_BASE)

번들 데이터셋 (오프라인 검색)

식약처 API는 색/모양/각인 검색 파라미터를 서버에서 무시하는 경우가 있어, 전체 데이터를 빌드 때 받아 기기에서 직접 검색합니다. scripts/build-dataset.mjs 가 생성:

파일 내용 규모(예시)
pills.json 낱알식별 전체(색/모양/각인/마크코드/분할선…) + 주성분(허가정보 결합) ~25,000건
injections.json 허가정보에서 추린 외용약·주사제(흡입제/좌약/연고/점안/주사) ~수천건
marks.json + marks/*.gif 고유 마크 코드 + 마크 이미지 번들(SW 프리캐시) ~431종, ~수MB
details.json.gz 전 품목 허가사항(효능·효과/용법·용량/주의) — 전문약 포함. gzip 수십 MB(gz)
dur.json.gz DUR 전체 룰셋(병용금기·임부·노인·연령·효능군중복) — 로컬 매칭 gzip
  • 검색 번들은 첫 실행 시 IndexedDB 에 캐시(메타 바뀌면 자동 갱신).
  • details/dur 는 용량이 커서 설정에서 받을 때만 다운로드(gzip → DecompressionStream 해제 → IndexedDB). 단건 조회로 자동 다운로드하지 않음.
  • 매일 cron(0 18 * * * UTC) 으로 재생성·재배포.
  • 허가목록은 totalCount 기준 전수 페이징(누락 방지).

오프라인 / 병동 인트라넷

상세(효능/용법/주의)·성분·DUR을 허가사항 문서(전수) 로 번들화해, 워커(공용 인터넷) 없이도 검색·상세·금기점검이 됩니다. 상세 소스는 e약은요(일부 전문약 누락) 대신 허가정보 허가사항.

  • 설정(⚙️) → "전체 의약품 데이터 받기": 검색+상세+DUR을 기기에 적재 → 이후 폐쇄망/오프라인 동작.
  • getDetail: 받아둔 허가사항(전수) 우선 → 온라인이면 e약은요로 상호작용/부작용/보관 보강.
  • 실물사진(선택): 본 사진은 SW(pill-photos, CacheFirst)로 자동 보관. 설정에서 전부 미리 받기(대용량 ~수 GB, 중단 가능)도 가능 — 식약처에서 폰으로 직접(레포/Pages 호스팅 없음).
    • 콜드 로딩: 처음 보는 약 사진은 nedrug→Worker 경유가 느려(콜드 ~20초, 캐시 후 <1초) 그동안 상세 시트에 로딩 스피너 표시(사진 '없음'이 아니라 '받는 중'). 한 번 보면 엣지 캐시(7일)로 즉시.
  • 사용 흐름: 인터넷 되는 곳에서 1회 받아두면 병동 인트라넷/오프라인에서 그대로 사용.

비목표

  • 처방·복약 지도 자동화 아님 — 식별 보조 도구, 최종 확인은 의료진 책임
  • 환자 식별정보(실명·등록번호 등) 수집/전송 없음

About

병동 지참약 식별 PWA — 색·모양·각인·마크로 거꾸로 찾고 환자별 복약 리스트·DUR 점검·오프라인

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages