의료택시 2.0 · 내부 학습자료

🧑‍🏫 코드 원리 학습 · 발표 방어 가이드

이 서비스의 모든 코드가 무슨 원리로 도는지 쉽게 설명합니다. 심사위원 질문에 직접 답할 수 있게 "무엇을 / 왜 / 어떻게 + 실제 코드 + 비유" 순으로 정리했습니다.

0. 전체 그림 — 파일이 각각 뭘 하나

파일역할핵심 기술
원주_의료택시_관제시스템.html상담원·관리자 웹앱(예약·추천·배차·통계)SPA·localStorage·SVG지도
원주_의료택시_기사앱.html기사 모바일앱(배차수신·운행·귀가)상태기계·BroadcastChannel
ai_dispatch/ (백엔드)실서비스용 서버(진짜 OR-Tools·DB·ML)FastAPI·PostgreSQL·OR-Tools·sklearn
원주_통합_대시보드.html공공데이터 지도 분석GeoJSON·choropleth
원주_의료기관_카카오지도.html509곳 주소→좌표 변환카카오 services
💡 핵심 구분: 브라우저 앱(HTML)은 시연·운영 프론트. 진짜 대규모 연산(OR-Tools·머신러닝·중앙DB)은 백엔드(ai_dispatch)가 담당. 브라우저는 파이썬을 못 돌리기 때문.

1. 지도는 어떻게 그려지나

무엇: 원주 행정경계와 병원·차량을 화면에 그린다. 왜: 외부 지도 API(구글·카카오)는 키·과금·도메인 제한이 있어서, 키 없이 되도록 직접 그린다.

원리: 위경도(지구) → 화면 픽셀(평면) 변환

지구는 둥근데 화면은 평평하다. 그래서 등거리 투영으로 좌표를 픽셀로 바꾼다. 위도에 따라 경도 1도의 실제 폭이 달라지므로 cos(위도)로 보정한다.

// 위경도 → 화면 좌표 (PX 함수)
function PX(lon, lat){
  const kx = Math.cos(중앙위도 * π/180);   // 경도 폭 보정
  const s  = min(가로/경도범위, 세로/위도범위); // 화면에 맞춘 배율
  return [ 왼쪽여백 + (lon-최소경도)*kx*s,
           아래여백 - (lat-최소위도)*s ];    // y는 위아래 뒤집기
}
🍊 비유: 귤껍질(둥근 지구)을 눌러 평평하게 펴서 종이(화면)에 붙이는 것. 위쪽으로 갈수록 경도 간격이 좁아지니 cos로 눌러준다.

행정경계는 GeoJSON(각 읍면동의 꼭짓점 좌표 목록)을 받아 PX로 변환해 다각형(<path>)으로 그린다.

2. AI 병원 추천 — 점수 계산

무엇: 진료과·거주지만 넣으면 갈 병원을 추천. 어떻게: 후보 병원마다 4가지를 점수화해 합산, 높은 순 Top5.

// 추천점수 = 거리 45% + 소요 20% + 혼잡도 20% + 예약가능 15%
score = 45*(1 - 거리/최대거리)     // 가까울수록 ↑
      + 20*(1 - 예상소요/60)
      + 20*(1 - 병원혼잡도/100)     // 덜 붐빌수록 ↑
      + 15*(예약가능 ? 1 : 0.2);
💡 이건 딥러닝이 아니라 "가중치 규칙(점수표)"이다. 장점: 왜 이 병원인지 설명 가능(거리 몇 점, 혼잡 몇 점). 심사위원이 "왜 추천했냐" 물으면 점수 내역을 그대로 보여주면 된다.

혼잡도는 어떻게 구하나 (중요)

병원이 실시간 대기수를 공개하지 않으므로 가진 데이터로 근사한다. 그리고 혼잡도는 두 종류다:

종류계산
병원 혼잡도그 병원이 붐빔(예약 몰림)우리 콜센터가 그 병원에 보낸 예약 수 + 건보 통계 밀도
도로 교통혼잡도길 막힘(정체)카카오모빌리티 실시간 교통(백엔드)

3. AI 자동배차 — OR-Tools 원리

무엇: 하루 예약 전체를 택시들에 나눠, 빈차 주행(공차)을 최소로. 왜 어렵나: 손님 15명만 돼도 배차 조합이 1조 가지가 넘는다(사람이 못 푼다).

💡 어려운 계산은 우리가 안 짠다. Google OR-Tools(구글 최적화 엔진)가 푼다. 우리는 문제를 "번역"만 한다: ①지점 좌표 ②시간창(예약시각) ③정원=1(합승 금지) ④비용=거리.
# 백엔드 optimizer.py — 진짜 OR-Tools
routing.SetArcCostEvaluatorOfAllVehicles(거리콜백)  # 비용=거리
routing.AddDimension(시간콜백, ...)                 # 시간창 제약
routing.AddPickupAndDelivery(픽업, 하차)            # 집→병원 짝
routing.AddDimensionWithVehicleCapacity(..., 1)     # 정원 1
params.local_search = GUIDED_LOCAL_SEARCH           # 최적 탐색

총거리 최소 = 공차 최소인 이유: 총거리 = 실차 + 공차. 실차(손님 태운 거리)는 무조건 필요해 고정 → OR-Tools가 줄일 수 있는 건 공차뿐.

🛡️ 안전장치(폴백): OR-Tools가 시간초과여도 그리디("가장 가까운 택시에 붙이기")로 항상 답이 나오게 했다. 브라우저 데모는 이 그리디로 시연.

4. 회송 체이닝 — 공차를 없애는 핵심

A를 병원에 내려준 택시가 빈 차로 돌아가지 않고, 그 병원에서 진료 끝난 B를 태워 귀가시킨다. "누군가의 등원 종료 = 다른 이의 귀가 시작"으로 이어 붙여 빈 주행 2번을 0으로.

🔗 합승 아님: A가 내린 B를 태운다(정원 1). 낯선 사람과 같이 안 탄다. 택시의 빈 시간·거리만 이어 붙이는 것.

5. 학습 — 수요예측·소요시간 (진짜 학습)

무엇: 쓸수록 똑똑해진다. 어떻게: 운영 데이터를 localStorage(브라우저) 또는 DB(서버)에 실제로 저장하고, 새 데이터가 올 때마다 평균을 조금씩 갱신(온라인 학습)한다.

// 실제 학습 코드 (관제앱)
function learnDemand(지역, 시각){  // 지역×시간대 수요 카운트
  LEARN.demand[지역+'@'+시(hour)] += 1; }
function learnDuration(진료과, 분){ // 진료과별 소요 평균
  m.n++; m.mean += (분 - m.mean)/m.n;  // 온라인 평균 갱신
  LEARN.dur[진료과] = m; }
localStorage.setItem('medtaxi_learn', JSON.stringify(LEARN)); // 저장
💡 온라인 평균: 새 값이 오면 평균 += (새값-평균)/개수로 조금씩 이동. 데이터가 쌓일수록 실제값에 수렴 = "학습". 실서비스에선 이걸 scikit-learn(GradientBoosting)으로 요일·날씨·연령까지 넣어 정교화.
수요예측소요시간
입력요일·시간·계절·날씨·지역진료과·시간대
정답실제 예약 건수실제 진료 소요(분)
용도택시 사전배치회송 택시 미리 준비

⚠️ 귀가시각은 정확 예측 안 한다: 진료시간은 검사·대기로 변수가 커서 평균 경향만 배운다. 실제 귀가 배차는 "진료완료" 버튼(호출형, 카카오택시처럼) → 가장 가까운 차량. 예측이 틀려도 안전.

6. 두 앱 연동 — 상담원 배차가 기사폰에 뜨는 원리

무엇: 상담원이 배차하면 기사앱에 즉시 알림. 어떻게: BroadcastChannel — 같은 주소(도메인)의 여러 탭이 서로 메시지를 주고받는 브라우저 기능. 서버 없이 실시간.

// 관제앱: 배차 확정 시 방송
BC.postMessage({t:'dispatch', 승객, 픽업, 병원, 기사});
// 기사앱: 수신 → 배차 알림 팝업(30초 카운트다운)
BC.onmessage = e => { if(e.data.t==='dispatch') 배차알림표시(e.data); };
// 기사 수락 → 관제로 되돌려 상태 갱신
BC.postMessage({t:'accepted', id});
📡 비유: 같은 건물(도메인) 안의 무전기 채널. 상담원이 "배차!"라고 말하면 같은 채널을 켠 기사폰이 듣는다. 조건: 같은 주소(예: 12-4gd.pages.dev)에서 두 탭을 열어야 통신.

7. 저장(운영화) — 더미가 아닌 이유

예약·배차·완료·학습을 매번 localStorage에 저장 → 새로고침해도 유지·누적. 대시보드 "누적 학습 데이터 N건"이 그 증거.

💡 한계 정직히: localStorage는 이 브라우저 1대 안에서만 공유. 여러 상담원·기사가 다른 기기에서 같은 데이터를 쓰려면 중앙 서버+DB가 필요(=백엔드 ai_dispatch 배포). 그게 실증사업 단계.

8. 심사위원 예상 질문 & 답변 (치트시트)

Q. 이게 왜 AI인가요?
A. 두 가지입니다. 배차는 조합최적화(Google OR-Tools) — 인공지능 교과서의 탐색·최적화 분야. 수요·소요시간은 머신러닝으로 데이터가 쌓일수록 학습합니다. 딥러닝·챗봇은 이 문제에 과잉이라 안 씁니다.
Q. 지금 진짜 학습하나요?
A. 운영 데이터가 쌓이면 학습하는 구조이고, 브라우저 앱도 localStorage에 실제 저장·누적하며 평균을 갱신합니다. 학습 원리는 별도 학습데모에서 오차가 줄어드는 곡선으로 증명했습니다.
Q. 기존 100원 택시와 뭐가 다른가요?
A. 그건 장보기 등 만능형입니다. 우리는 병원 통원 특화 + 데이터로 취약지 선정 + OR-Tools 최적배차 + 회송 체이닝으로 공차를 줄이는 AI 운영 플랫폼입니다.
Q. 진료 끝나는 시각을 어떻게 예측하나요?
A. 정확히 예측 안 합니다. 검사·대기로 변수가 커서요. 진료완료 버튼을 누르면 가장 가까운 차량이 오는 호출형(카카오택시식)이라 예측 오차와 무관합니다.
Q. 병원 혼잡도를 어떻게 아나요?
A. 병원 실시간 대기는 비공개라, 우리 배차가 몰린 정도 + 건보 통계 밀도로 근사합니다. 도로 정체는 별개로 카카오모빌리티 실시간 교통을 씁니다.
Q. 데이터는 믿을 수 있나요?
A. 병상·인구·공차율은 공공기관 실측(별첨 검증리포트에 출처·기준일 전수 공개), 예산·절감률은 가정으로 명시했습니다. 없는 데이터는 만들지 않았습니다.
📌 한 문장 요약: "공공데이터로 취약지를 찾고(분석), 규칙기반 점수로 병원을 추천하고(설명가능 AI), OR-Tools로 공차 최소 배차하며(최적화 AI), 회송 체이닝으로 빈차를 회수하고, 운영 데이터로 수요를 학습(머신러닝)하는 — 기존 택시·콜센터로 바로 시작하는 AI 의료이동 운영 플랫폼."