헬스케어 스케줄링의 근본적인 문제점
헬스케어 애플리케이션 개발 시 가장 빈번하게 마주치는 난관은 예약 시스템 구축입니다. 특히 환자가 직접 진료 시간을 선택하는 ‘셀프 스케줄링’ 기능은 운영 효율성을 높이는 핵심 요소이지만, 이를 구현하기 위해서는 단순한 일정 표시 이상의 정교한 계산이 필요합니다. 대부분의 개발자는 기존의 전자 건강 기록(EHR) 시스템과 통합을 시도하는데, 여기서 큰 오해가 발생합니다. EHR API가 제공하는 ‘제공자 가용성(Provider Availability)’ 데이터는 실제 예약 가능한 ‘시간 슬롯’이 아니라, 의료진의 ‘주간 근무 시간표’인 경우가 많기 때문입니다.
기존 방식의 한계와 기술적 병목
전통적인 방식에서는 병원 직원이 전체 달력을 보며 빈 시간을 직접 찾아 환자에게 제안합니다. 하지만 시스템이 이 역할을 대신하려면, 특정 시간 범위 내에서 이미 예약된 시간을 제외하고 남은 빈 블록을 찾아 적절한 시작 시간을 계산해야 합니다. 예를 들어, 15분만 비어 있는 시간에 30분짜리 진료를 예약할 수 없으며, 오후 3시 17분과 같은 비정상적인 시작 시간을 피해야 합니다.
초기에는 이러한 로직을 Ruby 애플리케이션 코드에서 처리하려고 시도했습니다. 데이터베이스에서 예약 정보와 의료진 일정을 가져와 루프를 돌며 가용 시간을 계산하는 방식이었으나, 이는 데이터 양이 많아질수록 급격히 느려지는 성능 저하를 초래했습니다. 단순한 인덱싱이나 복잡한 알고리즘 논문을 적용하는 것만으로는 실질적인 해결책을 찾기 어려웠습니다.
PostgreSQL과 Materialized View를 활용한 최적화
thoughtbot 팀은 이 문제를 해결하기 위해 데이터베이스 계층으로 시선을 돌렸습니다. PostgreSQL은 타임스탬프와 범위(Range)를 쿼리하는 정교한 기능을 갖추고 있습니다. 이를 활용하여 향후 6개월 동안의 모든 의료진에 대한 예약 가능 슬롯을 미리 계산하고, Scenic Gem을 사용하여 ‘구체화된 뷰(Materialized View)’로 저장하는 방식을 채택했습니다.
이 방식의 핵심 장점은 다음과 같습니다: * 속도: Ruby에서 수많은 리스트를 순회하는 대신, 데이터베이스 수준에서 인덱싱된 뷰를 조회하므로 응답 속도가 비약적으로 향상됩니다. * 효율성: 예약 테이블에 의료진과 시간 범위에 대한 인덱스를 추가함으로써, 뷰를 갱신하는 속도 또한 매우 빠르게 유지할 수 있습니다. * 정확성: 복잡한 시간 계산 로직이 데이터베이스 엔진 내에서 처리되어 데이터의 정합성을 보장하기 용이합니다.
Michel Gem의 탄생과 활용
이러한 기술적 성과를 바탕으로 추출된 것이 바로 ‘Michel’ Gem입니다. Michel은 기존 애플리케이션의 예약(Booking), 의료진(Provider), 일정(Schedule) 클래스를 기반으로 구체화된 뷰 기반의 모델을 자동으로 생성해 주는 제너레이터를 제공합니다.
개발자는 새로운 예약이 생성되거나 의료진의 일정이 변경될 때마다 이 뷰를 새로고침(Refresh)하기만 하면 됩니다. 이를 통해 항상 최신 상태를 유지하면서도 매우 빠른 속도로 예약 가능한 시간 슬롯을 조회할 수 있는 강력한 인프라를 갖추게 됩니다. Michel은 헬스케어 분야의 특수한 요구사항을 충족하면서도 범용적인 예약 시스템의 성능 문제를 해결하는 훌륭한 도구입니다.