[운영 확장 2] 예약 우선순위는 누구의 규칙인가

환자 A는 패키지 상품의 두 번째 방문을 준비하고 있습니다. 첫 방문은 다음 주 화요일 오전 CONFIRMED로 확정되어 있습니다. 그런데 A는 최근 예약 두 건을 지키지 않았고, 이번에는 다음 날 비는 인기 시간대를 요청했습니다. 상담팀은 반복 노쇼 환자에게 같은 날 자동 제안을 멈추자고 합니다. 병원 운영팀은 VIP 고객에게 빈시간을 먼저 제안하자고 합니다.
patient.isVip = true나 noShowCount = 2를 예약 테이블에 넣으면 당장 동작하는 것처럼 보입니다. 하지만 곧 네 가지 질문이 생깁니다.
- 노쇼가 환자 책임인지, 휴진·장비 고장 같은 병원 사유인지 누가 판정하는가?
- VIP 기준은 예약 서비스가 계산하는가, 고객 상담·CRM과 병원 운영이 제공하는가?
- 제한은 새 요청에만 적용하는가, 이미 확정한 예약까지 다시 배치하는가?
- 판단에 필요한 정보를 읽지 못하면 자동 제한하는가, 직원 검토로 보내는가?
이 글의 결론은 간단합니다.
예약 서비스는 노쇼 페널티나 VIP 등급을 만들지 않습니다. 정책 소유자가 정한 기준을 받아 신규 제안·보류(hold)·대기 목록 제안에 적용하고, 이미
CONFIRMED인 예약은 바꾸지 않습니다.
환자 A에게 실제로 일어난 이벤트를 사실대로 나눈다
섹션 제목: “환자 A에게 실제로 일어난 이벤트를 사실대로 나눈다”환자 A를 “신뢰도 42점”으로 요약하면 그 점수가 어디서 나왔는지 알 수 없습니다. 예약 서비스가 필요한 것은 점수가 아니라, 어떤 일이 일어났고 그 책임이 누구에게 있는지 구분한 이벤트입니다.
| 이벤트 | 원천 | 책임 | 예약 서비스의 처리 |
|---|---|---|---|
| 예약을 지키지 않음 | 예약·내원 기록 | PATIENT 또는 UNKNOWN | 환자 책임으로 확인된 이벤트만 판단에 포함 |
| 늦은 취소 | 예약·상담 기록 | PATIENT | 정책의 늦은 취소 시간 창과 임계치를 적용 |
| 휴진·장비 고장 | 병원 운영 | CLINIC 또는 OPERATIONAL_EXCEPTION | 환자 제한 근거에서 제외하고 운영 재배정으로 전달 |
| 직원이 사유를 정정 | 직원 작업 | DATA_CORRECTION | 원래 이벤트는 보존하고 보정 이력과 버전을 추가 |
| 출처를 확인할 수 없음 | 외부 연동 | UNKNOWN | 자동 제한 대신 STALE 또는 UNAVAILABLE 반환 |
현재 모델의 이벤트 종류는 NO_SHOW와 CANCELLED이고, 출처는 APPOINTMENT, CLINIC_OPERATION, STAFF_OVERRIDE, IMPORT로 나눕니다. 환자 책임이 확인된 이벤트만 노쇼·늦은 취소 임계치에 사용할 수 있습니다. 병원 휴진으로 취소된 예약을 환자에게 다시 불이익으로 적용하는 일은 예약 서비스가 결정할 문제가 아닙니다.
VIP는 예약 서비스의 고객 프로필 정보가 아니다
섹션 제목: “VIP는 예약 서비스의 고객 프로필 정보가 아니다”VIP 고객에게 빈시간을 먼저 제안할 수는 있습니다. 그러나 VIP 기준과 유효 기간은 병원마다 다릅니다. 누적 구매액일 수도 있고, 특정 진료과 계약이나 기간이 정해진 캠페인일 수도 있습니다. 예약 서비스가 이 기준을 고객 프로필 정보로 복사하면 CRM과 예약 서비스가 서로 다른 등급을 갖게 됩니다.
예약 서비스에는 판단에 필요한 최소 입력만 전달합니다.
| 입력 | 의미 | 예약 서비스가 남기는 것 |
|---|---|---|
불투명한 memberId | CRM이 정한 고객 식별 범위 | 이름·전화번호 없이 판단 key에 사용 |
| 적용할 정책과 정책 버전 | 기간, 임계치, 제한 방식, 규칙 버전 | 제안과 함께 정책 버전과 digest를 기록 |
| VIP·우선순위 신호 | 후보 순서를 조정할 수 있는 입력 | 등급 정의나 점수는 저장하지 않음 |
| 직원 예외(override) | 일시적인 허용 또는 제한 해제 | actor, 사유, 만료, correlation과 함께 기록 |
따라서 예약 서비스의 책임은 고객 등급을 다시 계산하는 일이 아닙니다. 같은 병원 범위와 같은 정책 버전으로 새 요청을 판단하고, 나중에 그 판단을 재현할 수 있도록 정책 digest와 만료 시각을 남기는 일입니다.
판단 기준을 고정하고 예약 신뢰도 판단기는 작게 만든다
섹션 제목: “판단 기준을 고정하고 예약 신뢰도 판단기는 작게 만든다”환자 A가 인기 시간대를 요청하면 예약 신뢰도 판단기(BookingReliabilityEvaluator)가 동작합니다. 판단기는 무제한 이력이나 자유 입력 상담 메모를 읽지 않고, 이번 요청에 적용할 정책 버전과 제한된 이력만 입력으로 받습니다.
- 판단 key를
(tenantId, clinicId, memberId)로 제한합니다. - 적용할 정책에서
lookbackDays, 늦은 취소 시간 창, 노쇼·늦은 취소 임계치,coolingOffHours,restrictionMode를 읽습니다. - 제한된 이벤트만 읽고
eventId와sourceVersion으로 중복을 제거합니다. PATIENT책임 이벤트의 개수와 근거 ID만 계산합니다. 개인정보와 점수는 결과에 넣지 않습니다.verdict,reasonCode,triggerId,policyDigest,historyDigest, 만료 시각을 반환합니다.
판단 결과는 다음처럼 제한된 계약입니다.
| verdict | 의미 | 새 요청에 대한 효과 |
|---|---|---|
ELIGIBLE | 현재 정책상 제한 없음 | 제안·보류(hold)·확정 흐름을 계속 진행 |
REQUIRES_STAFF_APPROVAL | 직원 확인이 필요함 | 자동 확정 대신 직원 검토 |
RESTRICTED | 자동 경로를 제한함 | EXCLUDE_AUTOMATIC_SAME_DAY_OFFERS 또는 REQUIRE_STAFF_APPROVAL 적용 |
OVERRIDDEN | 직원 예외가 유효함 | 예외 만료 전 새 요청 허용 |
STALE | 정책이나 이력이 최신이 아님 | 다시 읽거나 직원 검토로 보내고 제한을 확정하지 않음 |
UNAVAILABLE | 판단 저장소를 읽을 수 없음 | 판단 불가를 알리고 자동 제한하지 않음 |
POLICY_DISABLED는 기능을 끈 정책의 명시적인 결과입니다. 운영 모드 OFF, SHADOW, ENFORCE와도 구분합니다. SHADOW에서는 결과를 기록하되 예약 경로를 막지 않을 수 있고, ENFORCE에서만 새 경로에 허용·검토·제한을 실제로 적용합니다.
이미 확정한 예약은 다시 심사하지 않는다
섹션 제목: “이미 확정한 예약은 다시 심사하지 않는다”환자 A의 화요일 오전 예약은 이미 CONFIRMED입니다. A의 이력이 나중에 바뀌었다고 예약 서비스가 그 예약을 자동 취소하거나 다른 환자에게 넘기면, 새 요청에 대한 정책 판단과 이미 확정한 예약을 한데 섞게 됩니다.
| 대상 | 판단을 적용하는 시점 | 기존 확정 예약 보호 |
|---|---|---|
새 PROPOSED | 제안을 만들기 전 | 해당 없음 |
새 HELD | 보류(hold) 생성 전과 만료 전 재검증 | 해당 없음 |
새 CONFIRMED | 확정 트랜잭션 안에서 digest·만료 기록 | 기존 예약은 이동하지 않음 |
기존 CONFIRMED | 자동 재배치 대상이 아님 | 취소·이동하지 않음 |
| 대기 목록 후보·제안 | 빈시간을 제안하기 전에 읽기 전용 판단을 사용 | 다른 확정 예약을 대체하지 않음 |
A에게 필요한 조치는 영구 금지가 아닙니다. 같은 날 자동 제안을 잠시 제외하거나, 보증금·재확인·직원 상담을 요구할 수 있습니다. VIP 정책과 함께 후보 순서를 조정할 수도 있습니다. 범위와 만료가 없는 영구 벌점은 설명할 수도, 되돌릴 수도 없습니다.

CONFIRMED 예약을 취소하거나 이동하는 단계는 없습니다.서비스 책임은 입력과 결과의 소유자로 나눈다
섹션 제목: “서비스 책임은 입력과 결과의 소유자로 나눈다”정책을 예약 서비스 안에 숨기지 않으려면, 누가 입력을 만들고 누가 결과를 사용하는지 먼저 나눠야 합니다.

| 서비스 | 소유하는 정보 | 예약 서비스와의 경계 |
|---|---|---|
| 고객 상담·CRM | 고객 프로필 정보, 상담 이력, VIP·우선순위 판단 | 원래 등급 정의 대신 불투명한 고객 범위와 정책 입력만 전달 |
| 병원 운영 | 적용 정책, 제한 방식, 직원 예외와 만료 | 기준과 예외를 결정하고 추가만 가능한 작업 이력(append-only)을 남김 |
| 예약 서비스 | BookingReliabilityDecision, 제안·보류(hold)·확정 상태, 다이제스트(digest)와 적용 이력 | 입력을 판단해 새 경로에 적용하고 기존 CONFIRMED를 보호 |
| 대기 목록 서비스 | 후보 순서, 제안, 만료, 응답 | 예약 서비스 판단을 읽기 전용으로 사용하고 제안 생명주기를 소유 |
| 임상·자원 서비스 | 내원·노쇼·취소 원천 사실, 의료진·장비·공간 상태 | 사실과 자원 결과를 발행하지만 고객 등급은 결정하지 않음 |
“정책을 적용한다”와 “정책을 결정한다”는 다릅니다. 예약 서비스는 RESTRICTED 결과를 받아 새 HELD 생성을 막을 수 있지만, 고객이 왜 VIP인지 또는 노쇼가 환자 책임인지 자체적으로 판정하지 않습니다. DecisionRecord는 이 책임 경계를 넘는 최소 계약입니다.
대기 목록은 빈시간을 제안하지만 다른 확정 예약을 대체하지 않는다
섹션 제목: “대기 목록은 빈시간을 제안하지만 다른 확정 예약을 대체하지 않는다”취소로 빈시간이 생기면 대기 목록 서비스가 후보를 정렬합니다. 예약 신뢰도 판단과 VIP 우선순위 신호를 함께 사용할 수 있지만, 적용 위치는 새 제안의 순서입니다.
- 실제 자원과 방문 조건을 먼저 확인합니다.
- 후보마다 예약 서비스의 판단을 읽기 전용으로 조회합니다.
RESTRICTED환자에게는 같은 날 자동 제안을 보내지 않거나 직원 검토로 보냅니다.- VIP 신호는 정책 범위 안에서 같은 조건의 후보 순서를 조정합니다.
- 제안의 만료·수락·거절·재시도는 대기 목록 서비스가 관리합니다.
- 한 후보의 거절 때문에 다른 환자의
CONFIRMED예약을 바꾸지 않습니다.
“VIP가 먼저 제안을 받았다”와 “VIP가 이미 예약을 확정했다”는 전혀 다른 일입니다. 공정성은 모든 상황에 하나의 순서를 강제하는 것이 아니라, 이번 순서의 기준·범위·예외·만료를 설명할 수 있게 만드는 일입니다.
직원 검토와 예외는 삭제가 아니라 이력이다
섹션 제목: “직원 검토와 예외는 삭제가 아니라 이력이다”환자 A가 응급 상황으로 노쇼했다면 상담원이 다음 요청을 허용할 수 있습니다. noShowCount를 줄이거나 이벤트를 삭제하면 이후 모든 판단의 근거가 바뀝니다. 대신 다음 내용을 추가만 가능한 방식(append-only)으로 남깁니다.
- 누가(
actor) 예외를 적용했는가 - 어떤 사유(
reasonCode)였는가 - 언제까지 유효한가(
expiresAt) - 어떤 요청과 정책 digest에 연결되는가(
correlationId,decisionDigest) - 원래 이벤트와 보정 이벤트의 버전은 무엇인가
예외가 만료되면 예약 신뢰도 판단기는 적용할 정책과 이력을 다시 읽습니다. 직원의 한 번의 허용이 고객 등급을 바꾸지 않으며, 상담사의 자유 입력이 예약 서비스의 고객 프로필 정보로 복사되지도 않습니다.
현재 구현, 승인된 설계, 운영 준비를 구분한다
섹션 제목: “현재 구현, 승인된 설계, 운영 준비를 구분한다”| 구분 | 이 글에서 확인한 범위 |
|---|---|
| 현재 구현 | BookingReliabilityEvaluator, BookingReliabilityDecision, BookingEligibilityGate, 정책·이벤트 조회, 중복 제거, 판정(verdict)·사유·다이제스트(digest)·만료 반환 |
| 승인된 설계 | 신규 PROPOSED·HELD·대기 목록 제안 적용, 기존 CONFIRMED 보호, 직원 예외 적용/해제 이력, OFF·SHADOW·ENFORCE 모드 |
| 운영 준비 | 병원별 정책 값, CRM VIP 기준, 환자 책임 판정 증빙, 직원 권한·만료, 설명·이의제기 채널 |
| 로드맵 | 직원 검토 결과 확인, 대기 목록 제안 동의 화면, 병원별 공정성 지표와 정책 비교 |
현재 코드에 판단기와 자격 확인 단계(gate)가 있다는 사실만으로 모든 병원이 ENFORCE 상태가 되지는 않습니다. 병원 허용 목록(allowlist), shadow 관찰, 오래된·판단 불가(unavailable) 상태 복구 훈련, OFF 또는 SHADOW 되돌리기(rollback) 절차를 별도로 확인해야 합니다.
핵심은 벌점이 아니라 책임과 범위다
섹션 제목: “핵심은 벌점이 아니라 책임과 범위다”환자 A의 다음 요청을 제한할 수 있고 VIP에게 빈시간을 먼저 제안할 수도 있습니다. 그래도 다음과 같이 한 문장으로 설명할 수 있어야 합니다.
“이번 판단에는 이 병원에 적용된 정책과 환자 책임 이벤트, 직원 예외가 사용되었습니다. 결과는 새 같은 날 제안에만 적용했고, 이미 확정한 예약의 일시는 바꾸지 않았으며, 판단을 읽지 못했을 때는 자동 제한하지 않았습니다.”
이 설명을 만들 수 없다면 예약 우선순위는 숨겨진 조건문입니다. 상품 BOM이 방문 계획을 만들고 예약 서비스가 고객 희망 내원 날짜를 예약 확정으로 바꾸듯, 예약 신뢰도 정책도 책임 있는 이벤트와 명시적인 범위를 가진 결정으로 바꿔야 합니다.
다음 글
섹션 제목: “다음 글”이 글은 예약 우선순위의 기준과 책임 경계를 분리했습니다. 다음 글에서는 같은 예약 요청이 병원마다 다른 결과가 되는 이유를 테넌트 기본 정책, 병원별 재정의, 유효 정책 기준 정보의 관점에서 살펴봅니다.
시각 자료 더 보기
섹션 제목: “시각 자료 더 보기”근거 자료
섹션 제목: “근거 자료”- clinic-appointment 저장소
- 예약 신뢰도 API 계약
- 대기 목록 제안 API 계약
- 예약 신뢰도 설계
- 대기 목록 핵심 설계
- BookingReliabilityEvaluator
- BookingEligibilityGate
- 프로필 변경 뒤 공정한 재평가 lesson
댓글
GitHub 계정으로 의견을 남기거나 reaction을 남길 수 있습니다.