
SOFTMOA TECHNOLOGY
회의실 예약 시간 중복 방지 시스템 구축 방법
소프트모아가 정리한 기술 기록입니다.
Contents
회의실 예약이 겹친다면 달력 표시보다 같은 방의 사용 시간을 판정하는 기준과 저장 절차를 먼저 정해야 합니다. 종료 시각과 다음 시작 시각이 같은 예약은 허용하고, 승인 대기가 시간을 차지하는지는 별도 정책으로 정합니다. 아래는 사내 회의실을 위한 가상 설계이며 실제 고객 구축 사례가 아닙니다.
예약 화면, 관리자 승인, 변경 이력을 함께 설계하는 소프트모아의 업무 시스템 관점에서, 조회 결과와 예약 확정이 어떻게 다른지 설명합니다.
11시 종료와 11시 시작은 겹치지 않게
예시에서는 한국시간 하루의 09:00~18:00를 운영 시간으로 두고 시작·종료를 15분 단위로 입력받습니다. 시작은 종료보다 빨라야 합니다. 구간은 시작을 포함하고 종료는 제외하므로 A회의실의 10:00~11:00와 11:00~11:30은 이어서 사용할 수 있습니다. 준비 시간이 필요한 방은 이와 다른 점유 기준을 정해야 합니다.
같은 방에서 기존 시작이 새 종료보다 빠르고, 기존 종료가 새 시작보다 늦으면 충돌입니다. 시작 시각만 같은지 확인하면 10:30~11:00의 부분 겹침이나 09:30~13:00처럼 기존 예약을 통째로 감싸는 요청을 놓칩니다.
- PENDING은 승인 대기, CONFIRMED는 확정으로 두고 두 상태 모두 시간을 점유하게 합니다. 이는 이 예시의 정책이지 모든 예약 서비스의 공통 규칙은 아닙니다.
- REJECTED는 반려, CANCELLED는 취소이며 점유에서 제외합니다. 삭제 대신 처리자·시각·사유를 남깁니다.
- 직원은 자기 예약을 신청·취소하고 시설 담당자는 승인·반려합니다. 대기 건을 언제 처리할지 정하지 않으면 사용하지 않는 방이 계속 막힐 수 있습니다.
신청 화면에서 승인 목록까지
직원은 등록된 회의실, 날짜, 시작·종료, 사용 목적을 입력합니다. 서버는 해당 방의 신청 권한과 입력값을 검사하고 충돌이 없으면 대기 건을 저장합니다. 화면에는 신청 번호와 '승인 대기'를 표시해야 하며, 이메일 발송만으로 확정이라고 안내하면 안 됩니다.
시설 담당자는 승인 목록에서 시간과 목적을 확인합니다. 승인하면 확정으로 바꾸고, 반려하면 사유와 함께 시간을 돌려줍니다. 직원의 '내 예약'과 방별 달력은 같은 상태를 보여 주어야 합니다. 다른 부서에는 점유 시간만 공개하고 회의 목적은 숨기는 식으로 조회 권한도 분리합니다.
시간 변경은 새 구간 검증과 기존 구간 해제를 한 번의 처리로 묶습니다. 새 시간이 충돌하면 원래 예약을 유지하고 실패 사유를 돌려줍니다. 원래 예약부터 취소한 뒤 새 요청을 넣으면 사용자가 기존 시간까지 잃을 수 있습니다.
기존 예약 4건과 요청 7건으로 경계 확인하기
다음은 저장 없이 판정만 하는 SQLite 조회입니다. 같은 날짜의 정상적인 기존 예약과 등록된 방 A·B만 사용합니다. a와 b는 자정부터 지난 정수 분으로, 600은 10:00, 660은 11:00입니다. 요청 7건은 각각 따로 시험하는 후보이며 서로 예약으로 등록되는 것은 아닙니다. WITH로 시험 데이터를 만들고 CASE와 EXISTS로 입력 오류·충돌을 구분합니다.[1][2]
WITH
bookings(id,room,a,b,state) AS (VALUES
('B1','A',600,660,'CONFIRMED'),
('B2','A',660,690,'CANCELLED'),
('B3','A',720,750,'PENDING'),
('B4','B',690,720,'CONFIRMED')
),
requests(id,room,a,b) AS (VALUES
('R1','A',630,660), ('R2','A',660,690),
('R3','A',720,735), ('R4','A',690,690),
('R5','A',690,720), ('R6','A',570,780),
('R7','A',NULL,660)
)
SELECT r.id,
CASE
WHEN typeof(r.a) != 'integer' OR typeof(r.b) != 'integer'
OR r.a < 540 OR r.b > 1080 OR r.a >= r.b
OR r.a % 15 != 0 OR r.b % 15 != 0
THEN 'INVALID'
WHEN EXISTS (
SELECT 1 FROM bookings b
WHERE b.room = r.room
AND b.state IN ('PENDING','CONFIRMED')
AND b.a < r.b AND b.b > r.a
) THEN 'CONFLICT'
ELSE 'AVAILABLE'
END AS result
FROM requests r ORDER BY r.id;
SQLite 3.54.0에서 위 조회를 실행한 결과입니다. 운영 DB를 읽거나 예약 데이터를 변경하지 않았습니다.
R1|CONFLICT
R2|AVAILABLE
R3|CONFLICT
R4|INVALID
R5|AVAILABLE
R6|CONFLICT
R7|INVALID
R1은 A방의 확정 건 B1과 겹칩니다. R2는 B1 종료와 시작이 같고 같은 시간의 B2는 취소되어 신청 가능합니다. R3은 대기 중인 B3에 막힙니다. R4는 시작과 종료가 같고 R7은 시작이 누락되어 입력 오류입니다. NULL을 먼저 거르지 않으면 시간 비교가 성립하지 않아 빈 시간처럼 오판할 수 있습니다.[1]
R5는 B방의 B4와 시간이 같아도 A방이므로 가능합니다. R6은 A방 B1과 B3를 모두 포함해 충돌합니다. 시험 데이터의 B3만 REJECTED로 바꿔 다시 실행하면 R3은 AVAILABLE이 되지만 R6은 B1 때문에 여전히 CONFLICT입니다. 비교할 기존 예약을 모두 제외한 시험에서도 R4·R7은 INVALID로 남았습니다.
신청 가능이라는 조회가 확정을 보장하지는 않습니다
이 SQL은 방의 존재·권한·기존 데이터의 품질을 검사하지 않고, 반복 예약이나 자정을 넘는 일정도 처리하지 않습니다. 실제 저장소에서는 날짜와 방을 함께 대조하고, 입력을 문자열로 이어 붙이지 말고 바인딩해야 합니다. 변경 조회에서는 자기 예약을 비교 대상에서 제외하되 다른 예약은 그대로 검사합니다.
두 직원이 동시에 조회하면 둘 다 AVAILABLE을 받을 수 있습니다. 충돌 검사 후 저장 사이의 경쟁까지 막아야 중복 방지입니다. PostgreSQL 15 공식 문서는 같은 회의실의 겹치는 시간 범위를 배제 제약으로 막는 예를 제공합니다.[3] 이는 운영 DB 설계의 참고이며 여기서 실행한 SQLite 조회가 동시 저장을 보장한다는 뜻은 아닙니다. 저장 충돌은 재선택 안내로 처리하고, 연속 클릭 재요청도 같은 신청으로 식별해야 합니다.
연동 캘린더와 알림은 저장 성공 후에 갱신합니다. 알림이 실패해도 확정 예약을 새로 만들지 말고 해당 신청의 알림만 재처리합니다. 도입 범위를 정할 때는 대기 점유 정책과 변경 실패 시 원래 시간을 유지하는지부터 합의하는 편이 좋습니다.
문법·설계 확인 자료: [1] SQLite 표현식, [2] SQLite WITH, [3] PostgreSQL 15 범위 제약.
소프트모아는 해당 시스템을 구축합니다. 문의하기
