예약은 생각보다 섬세한 일이다. 일정이 촘촘할수록 한 번의 놓침이 하루 전체를 흔든다. 알림 봇은 그 빈틈을 메워 준다. 사람이 해야 할 반복 확인을 대신해 주고, 기회가 열리는 순간을 놓치지 않도록 곁에서 토스해 준다. 여기서는 예약 알림 봇을 직접 구축해 실전에서 쓰는 법, 그리고 운영 과정에서 생기는 문제와 개선 포인트까지 한 번에 정리해 보겠다. 앱 하나를 설치하고 끝내는 가벼운 얘기가 아니다. 실제로 현장에서 부딪히는 예외 상황, 사이트별 차이, 메시지 채널의 한계 등 기술적 디테일을 풀어낼 것이다.
알림 봇이 필요한 이유, 그리고 어떤 문제를 풀어주는가
예약은 수요와 공급의 시계가 엇갈릴 때 진가가 드러난다. 인기 타임 슬롯은 열리자마자 사라지고, 취소표는 예고 없이 등장했다가 증발한다. 눈으로 계속 새로고침하며 대기할 수 없다면 자동화가 답이다. 알림 봇은 크게 세 가지 가치를 만든다. 첫째, 예약 가능 상태 변화를 실시간에 가깝게 포착한다. 둘째, 채널을 통합해 한곳으로 알림을 모아준다. 셋째, 반복 루틴을 안전하게 자동 수행한다.
다만 만능은 아니다. 일부 오피사이트는 동적 렌더링을 적극적으로 쓰고, 공격적인 봇 차단을 적용한다. 페이지 구조가 빈번하게 바뀌면 크롤러가 자주 깨진다. 알림 딜레이가 생기는 순간 경쟁률 높은 슬롯은 여지없이 빼앗긴다. 이 글은 그 간극을 최소화하고, 고장 났을 때 빠르게 복구하는 방법에 무게를 둔다.
알림 봇의 전체 구조를 한 번에 그려보기
알림 봇을 하나의 파이프라인으로 보면 명확해진다. 소스에서 데이터를 끌어오고, 정제하고, 부산오피 이전 상태와 비교해 변화만 감지한다. 그 결과를 메시지로 만들어 전송한다. 흐름은 단순하지만 각 단계에서 선택해야 할 도구와 설계가 성패를 가른다.
데이터 수집은 두 갈래다. 첫째, 공식 API가 있다면 그것을 최우선으로 사용한다. 호출 간격과 인증 방식을 지키기만 하면 안정적이고 빠르다. 둘째, API가 없으면 웹 스크래핑으로 가야 한다. 정적 HTML이라면 requests + 파서면 충분하고, 동적 렌더링이라면 헤드리스 브라우저가 필요하다. 어느 쪽이든 예의가 필요하다. 과도한 요청은 차단을 부른다.
정제 단계에서는 예약 슬롯을 표준 스키마로 맞춘다. 날짜, 시작 시간, 종료 시간, 카테고리 같은 필드는 공통 필드로, 가격이나 지역과 같은 확장 필드는 딕셔너리로 들고 간다. 이 통일 작업이 되어 있어야 여러 오피사이트를 묶어 한 화면에 보여줄 수 있다.
상태 비교는 메모리 캐시든, 키-값 저장소든 무엇이든 좋다. 핵심은 슬롯의 식별자다. 보통 날짜와 시작 시간, 지점 ID를 키로 삼고, 가용 여부와 업데이트 타임스탬프를 값으로 둔다. 이전 스냅샷과 새 스냅샷을 diff 해서 새로 열린 슬롯만 알림으로 내보낸다.
전송 단계는 팀의 습관을 따른다. 텔레그램, 디스코드, 카카오톡 비즈 메시지, 이메일, 푸시 서버까지 선택지는 넓다. 채널이 여럿이면 라우팅 규칙을 둔다. 중요도 높은 슬롯은 푸시, 그 외는 묶어서 텔레그램 요약 메시지로 보내는 식으로.
오피사이트 스크래핑, 어디까지 가능하고 어디서 막히는가
현장에서 가장 많이 부딪히는 난관이 스크래핑 안정성이다. 오피사이트마다 DOM 구조가 자주 바뀌고, 예약 버튼의 텍스트가 데이터에 따라 유동적이다. 또 클라우드 기반 WAF가 비정상 패턴을 잡아 차단하기도 한다. 몇 가지 원칙을 세워두면 사고를 줄일 수 있다.
첫째, CSS 셀렉터를 과도하게 구체화하지 않는다. class 이름이 해시처럼 바뀌는 사이트에서는 인접 관계나 특성 기반 셀렉터를 쓰는 편이 오래 간다. 예를 들어 div[class*="slot"]처럼 부분 일치나 role 속성을 적극 활용한다.
둘째, 텍스트 기반 판별은 다국어와 공백, 제어 문자에 취약하다. 가능하면 버튼의 data-attribute나 aria-label을 우선한다. 텍스트를 쓴다면 정규화 과정을 두어 공백과 줄바꿈, 특수 문자를 제거한다.
셋째, 렌더링 대기를 숫자 지연에만 맡기지 않는다. 요소가 나타날 조건을 명시하고, 해당 조건이 충족될 때까지 기다리는 로직을 쓴다. 예를 들어 예약 테이블의 행 수가 늘어날 때까지 대기한다.
넷째, 요청 빈도는 분당 N회로 제한하고, 분산 스케줄러로 타임슬라이스를 나눈다. 새벽 시간대에만 빈도를 높이고 평상시에는 간격을 벌린다. 차단은 한 번 당하면 길게 간다.
다섯째, 에러는 삼키지 말고 관찰 가능하게 만든다. 실패한 URL, HTTP 코드, 캡차 발생 여부, 렌더링 소요 시간, 파싱 실패율을 지표로 쌓아두면 어디가 병목인지 바로 보인다.
텔레그램으로 빠르게 시작하는 최소 구성
가장 빠르게 눈으로 성과를 볼 수 있는 경로는 텔레그램 봇이다. 누구나 만들 수 있고, 메시지 전송 API가 단순하다. BotFather로 토큰을 만들고, 채널이나 그룹에 봇을 초대하면 준비 끝이다. 메시지는 간결해야 한다. 슬롯을 하나하나 쏟아내면 피로도가 쌓이니, 새로 열린 슬롯만 보내고, 비슷한 슬롯은 1분 단위로 묶어서 요약한다.
메시지 포맷은 사용자가 바로 판단할 수 있게 최소 정보를 담는다. 날짜, 시간, 지점, 링크, 유효성 기준 시간. 링크는 그대로 눌러 접근할 수 있어야 하지만, 로그인 세션이 필요한 사이트라면 딥링크가 깨질 수 있다. 그때는 페이지 최상단 링크와 동선 안내를 함께 넣는 편이 낫다. 예를 들어, 앱 - 예약 - 지점 - 날짜 순으로 이동하라는 짧은 설명을 덧붙인다.

텔레그램의 메시지 속도 제한도 고려한다. 초당 메시지 수를 넘기면 쿨다운이 걸린다. 배치 전송으로 한 메시지에 여러 슬롯을 담고, 하이라이트를 위에 올린다. 더 보기 같은 확장 버튼을 붙이고 싶으면 인라인 키보드를 쓰면 되지만, 링크 외의 상호작용은 과하지 않게 유지하는 것이 좋다.
스케줄러와 빈도, 어디에 기준을 둘 것인가
예약 알림의 생명은 타이밍이다. 너무 자주 긁으면 차단되고, 너무 느리면 경쟁에서 진다. 경험상 안정적인 범위는 분당 1회에서 4회 사이다. 경쟁이 치열한 이벤트성 예약일 때만 10초 간격까지 단기적으로 내려간다. 이때는 백오프를 필수로 둔다. 에러가 연속으로 발생하면 간격을 늘리고, 정상 회복 후 서서히 원래 빈도로 되돌린다.
시계는 하나로 맞춘다. 서버의 시간대(Time Zone)와 오피사이트가 표기하는 시간대가 다를 때 혼선이 빈번하다. UTC 기준으로 저장하고, 사용자에게 노출할 때만 로컬 시간대 변환을 적용하면 사고를 줄일 수 있다. 날짜 경계도 조심해야 한다. 자정 근처 예약은 어제와 오늘 사이에 걸치는데, 단순 문자열 비교로 정렬하면 순서가 엉키기 쉽다.
크론 기반 스케줄러는 간단하고 신뢰성이 높지만, 사이트별로 다른 주기를 갖고 싶다면 태스크 큐를 쓰는 편이 관리가 수월하다. 우선순위를 동적으로 조절할 수 있어 긴급 슬롯의 폴링 빈도를 순간적으로 높일 수도 있다.
상태 저장과 중복 알림 방지
같은 슬롯을 동일 사용자에게 반복 전송하면 금세 알림을 꺼버린다. 본질은 중복 방지다. 슬롯을 고유 키로 변환해 최근 N시간 내 전송 이력에 넣어두고, 동일 키가 감지되면 스킵한다. 단, 상태가 바뀌었을 때는 다시 알려야 한다. 대표적으로 대기에서 가용으로, 혹은 가격 변동 같은 변화다. 그래서 이력 저장 시 슬롯 키와 함께 해시를 보관한다. 날짜, 시간, 지점 ID에 상태 요약을 합쳐 해시로 만들고, 이전 해시와 다르면 새 메시지를 보낸다.
이력의 TTL은 예약 주기와 사용 패턴에 맞춘다. 일반적으로 24시간이면 충분하지만, 주 단위 예약을 다룬다면 72시간까지 늘려도 된다. 저장은 레디스 같은 인메모리 저장소가 간편하다. TTL을 걸어 만료를 자동화하고, 메모리 사용량이 치솟지 않게 키 수를 제한한다.
실패를 가정한 설계, 실제로는 여기서 갈린다
사이트가 응답을 늦출 때 봇이 함께 느려지면 안 된다. 타임아웃을 짧게 잡고, 실패는 빠르게 포기한 뒤 다음 라운드에서 재시도한다. 재시도는 지수 백오프를 쓰되, 매번 같은 타이밍에 겹치지 않도록 난수 지연을 섞는다. 그리고 실패의 종류를 구분한다. 4xx는 요청이 잘못되었거나 차단이므로 패턴을 바꾸거나 간격을 늘려야 한다. 5xx는 서버 문제라 다음 라운드에서 정상화될 확률이 높다.
캡차는 기계적 우회보다 감지와 후퇴가 우선이다. 감지되면 해당 도메인의 폴링 빈도를 대폭 낮추고, 사용자가 수동 확인할 수 있는 링크를 제공한다. 봇이 무리하게 우회하면 계정 자체가 정지될 수 있다. 보수적으로 움직이는 것이 장기적으로 이득이다.
로그는 사람의 언어로 쌓는다. 단순한 에러 코드 나열이 아니라, 어떤 URL에서 어떤 셀렉터가 실패했고, 마지막으로 성공한 시점이 언제였는지까지 한 줄로 읽히게 만든다. 문제를 재현하려면 컨텍스트가 필요하다.
여러 오피사이트를 한 봇에서 다룰 때의 통합 전략
사이트마다 구조와 규칙이 다르다. 통합하려면 추상화 계층이 필요하다. 데이터를 표준 스키마로 맞추는 어댑터 패턴을 쓰면 좋다. 사이트별 어댑터는 다음 역할을 맡는다. 인증과 세션 관리, 데이터 페치, 슬롯 파싱, 차단 대응 정책. 공통 코어는 스케줄링, 상태 비교, 전송, 로깅을 담당한다.
알림의 우선순위는 사람의 관심사에 맞춰야 한다. 같은 시간대에 여러 지점에서 슬롯이 열리면 사용자가 선호하는 지역과 가격대, 시간대를 기준으로 랭킹을 매긴다. 랭킹은 단순 점수화로도 충분하다. 예를 들어, 선호 지역 +3점, 선호 시간대 +2점, 가격 상한선 이내 +2점, 이동 시간 30분 이내 +1점. 점수 상위부터 메시지 상단에 배치하면 된다.
요약 메시지를 하루에 한 번 따로 보내는 것도 유용하다. 오늘 열린 전체 슬롯의 스냅샷과 성공률, 평군 응답 시간, 에러 비율을 세 줄로 정리하면 사용자는 봇의 건강 상태를 체감한다. 이 피드백이 있어야 신뢰가 쌓인다.
개인정보와 보안, 당연한 얘기지만 구체적으로 관리해야 한다
계정 쿠키나 토큰을 파일로 남기는 순간 위험이 시작된다. 비밀 값은 환경 변수로 주입하고, 서버에선 비밀 관리 서비스를 사용한다. 로그에도 토큰이나 세션 식별자가 노출되지 않도록 마스킹을 적용한다. 링크에 세션 파라미터가 붙을 때는 제거해 공유한다.
사용자 맞춤 알림을 지원하려면 전화번호나 메신저 ID를 저장해야 하는데, 저장 최소화가 원칙이다. 봇이 동작하는 데 꼭 필요한 항목만 보관하고, 90일 무활동 시 자동 삭제 같은 정책을 둔다. 암호화는 전송과 저장 모두에 적용하고, 액세스 로그를 남겨 내부 접근까지 추적 가능하게 만든다.
실제 운영에서 겪는 흔한 문제와 해결법
가장 흔한 이슈는 알림이 늦게 오는 현상이다. 원인은 세 가지로 압축된다. 첫째, 사이트 응답 지연. 둘째, 스케줄러 병목. 셋째, 메시지 채널 제한. 각 단계의 처리 시간을 측정해 어디서 늘어지는지 확인한다. 병목이 스크래핑이면 도메인별 워커를 늘리고, 메시지 단계면 배치 크기를 조절한다.
또 다른 이슈는 거짓 양성, 즉 실제로는 예약 불가인데 알림이 나가는 경우다. 파서가 숫자나 색상, 클래스 이름에 의존할 때 빈번하다. 검증 단계를 하나 추가해 버튼 클릭 가능 여부나 서버 응답으로 교차 확인한다. 비용은 조금 늘지만 신뢰도가 달라진다.
알림이 너무 많아 피로가 쌓이는 것도 문제다. 임계값을 둬 비슷한 슬롯이 짧은 시간 안에 연속으로 생기면 묶어 요약한다. 알림 제목에 중요한 차이를 바로 드러내면 사용자는 메시지를 열어보지 않고도 판단할 수 있다. 예를 들어, [서울역] 18:00 - 19:00 신규 2건 이런 형식이 읽기가 빠르다.
템포와 디테일, 메시지 디자인의 작은 차이가 만드는 성과
메시지는 길수록 읽히지 않는다. 첫 줄에 핵심을, 둘째 줄에 컨텍스트를, 셋째 줄에 행동을 둔다. 핵심은 지점과 시간, 컨텍스트는 가격이나 옵션, 행동은 이동 링크나 앱 열기 안내다. 세 줄 규칙을 지키면 어떤 채널에서도 보기 좋다.
시간 표기 역시 통일한다. 18:00처럼 24시간제를 쓰고, 날짜는 YYYY-MM-DD로 표기한다. 모바일에서 공간이 부족하니 요일은 이모지 대신 약어로 붙인다. 예: 2025-03-21 Fri 18:00.
실전에서 CTR을 올리는 가장 간단한 방법은 링크와 버튼의 간격을 두는 것이다. 터치 실수가 줄어든다. 텔레그램의 인라인 키보드는 2열보다 1열이 오작동이 적다. 사소해 보이지만 장기적으로 불만을 줄인다.
모니터링과 알림의 알림, 메타 관리
봇이 멈추면 사용자도 모른다. 그래서 봇 자체를 감시하는 알림이 필요하다. 데이터 수집이 일정 시간 이상 지연되면 헬스 체크 알림을 보낸다. 지표는 간단하게 시작한다. 최근 15분 내 스캔 성공 횟수, 평균 처리 시간, 전송 큐 길이, 에러율. 임계값을 넘어가면 운영자 채널에 초록색이 아니라 노란색 알림부터 보낸다. 사람은 한 번에 빨간 경보만 받으면 금방 무뎌진다.
배포 자동화도 중요하다. 코드를 수정할 때마다 수집기 전체를 재시작하면 놓치는 슬롯이 생긴다. 롤링 재시작으로 워커를 절반씩 교체하고, 준비 상태 프로브가 통과된 뒤 트래픽을 넘긴다. 간단한 컨테이너 오케스트레이션만으로도 체감 안정성이 오른다.
이미 있는 도구를 붙여서 가성비 높이기
직접 모든 것을 만들 필요는 없다. RSS 변환기, 사이트 변경 감지 서비스, Uptime 모니터, 텔레그램 메시지 큐 라이브러리 같은 완성품을 조합하면 초기 구축 시간이 크게 줄어든다. 특히 변경 감지 서비스는 DOM 특정 영역이 바뀔 때만 알림을 주므로 스크래핑 빈도를 낮출 수 있다. 다만 유료 요금제의 호출 제한을 넘지 않도록 계획을 세워야 한다.
크롤링은 파이썬 생태계가 풍부하다. Requests, httpx, Playwright, Selenium, BeautifulSoup, lxml, parsel, 그리고 데이터 정제에 pandas까지. 반대로 Node.js로 가면 Playwright와 Cheerio 조합이 가볍다. 팀의 역량과 운영 인력의 익숙함이 선택 기준이 된다.
테스트 전략, 실환경을 닮게 만들수록 산다
스테이징 환경을 따로 둘 수 없다면, 최소한 사이트의 일부 경로를 캡처해 고정 응답으로 재생하는 장치를 만든다. 이렇게 하면 파서가 깨지는지 흘러간 시간 없이 확인할 수 있다. 날짜와 시간이 포함된 응답은 템플릿 변수를 써서 테스트 실행 시점에 동적으로 치환한다. 덕분에 테스트가 날짜 변경에 민감하게 깨지지 않는다.
부하 테스트도 가볍게나마 해본다. 동일 도메인에 동시 요청을 5, 10, 20으로 늘리며 차단 임계점을 파악한다. 이 수치는 운영에서 안전한 선을 잡는 데 유용하다. 관찰된 한계 아래에서 여유를 두고 운용하면 장기 안정성이 높아진다.
사용자 경험, 미묘하지만 성패를 가르는 차이
알림을 받는 사람이 설정을 바꾸기 쉽게 만들어야 한다. 지역, 시간대, 최대 이동 거리, 가격 상한을 빠르게 수정할 수 있어야 한다. 텔레그램에서는 명령어 대신 간단한 설문형 버튼으로 상태를 갱신하는 편이 진입 장벽이 낮다. 사용자가 변경한 설정은 즉시 적용되었다는 메시지를 회신한다. 신뢰는 이런 작은 피드백에서 쌓인다.
무음 시간대 기능은 꼭 넣는다. 야간에는 알림을 메시지로만 쌓고 푸시는 끈다. 아침이 되면 요약을 한 번 보내고, 그 이후 새로 생기는 것만 푸시한다. 사람이 살아가는 리듬과 맞춰야 오래 간다.
유지보수 비용 줄이는 습관
파서를 짤 때 셀렉터와 키를 코드에서 하드코딩하지 말고, 설정 파일로 꺼내 놓는다. 사이트가 바뀌었을 때 운영자는 코드가 아니라 설정만 바꿔서 복구할 수 있다. 셀렉터에 버전 주석을 남기고, 변경 이력을 적어두면 다음 번 수정이 빨라진다.
사이트별 어댑터를 모듈로 나눠 독립적으로 배포할 수 있게 설계하면 좋아진다. 한 사이트가 크게 바뀌었을 때 전체 시스템을 건드리지 않고도 빠르게 대응할 수 있다. 이런 분리는 기능 완료 속도뿐 아니라 사고 전파를 줄여준다.
그리고 무엇보다도, 문서화를 미루지 않는다. 설치, 환경 변수, 스케줄링 규칙, 실패 대응, 알림 포맷, 장애 매뉴얼까지 짧아도 좋으니 한 곳에 모아둔다. 새 팀원이 들어왔을 때 학습 비용이 줄어든다.
실제 적용 예시, 작은부터 시작해 크게 키우기
처음에는 한 오피사이트의 특정 지점만 대상으로 삼는다. 하루에 6시간 동안만 폴링하고, 텔레그램으로만 알림을 보낸다. 일주일 정도 데이터를 모으면서 슬롯이 열리는 패턴을 본다. 특정 요일 저녁에 취소표가 몰리는지, 점심 이후 업데이트가 많은지. 그 패턴을 기준으로 스케줄을 미세 조정한다. 필요할 때만 빈도를 올리는 방식은 차단 위험을 낮춘다.
다음 단계에서는 지점을 늘리고, 지역 필터와 무음 시간대를 도입한다. 알림이 증가하면 요약 배치를 도입한다. 이때부터 중복 방지와 상태 해시가 중요해진다. 셋째 단계에서 다른 오피사이트를 추가한다. 스키마 정합성을 검증하면서 어댑터 품질을 올린다. 링크가 깨지는 사례가 발견되면 즉시 파서에 회피 로직을 추가한다. 이런 점진적 확장이 실패를 로컬에 가둔다.
마지막으로 로그와 지표를 대시보드에 모은다. 당일 성공 건수, 실패율, 평균 지연, 사용자 클릭률. 숫자는 냉정하다. 어디를 손봐야 할지 똑바로 가리킨다.
빠르게 만들 수 있는 기본 구현 체크리스트
- 데이터 수집 경로 선택: 공식 API 우선, 없으면 정적 스크래핑, 동적이면 헤드리스 브라우저 준비 표준 스키마 정의: 날짜, 시간, 지점 ID, 상태, 링크, 확장 메타 상태 저장과 중복 방지: 슬롯 키 + 상태 해시, TTL 24시간 기준 전송 채널 연결: 텔레그램 봇 토큰, 채널 라우팅, 배치 요약 모니터링: 수집 성공 횟수, 평균 처리 시간, 에러율, 헬스 체크 알림
이 다섯 가지만 갖춰도 일단 돌아간다. 이후에는 실전에서 드러나는 문제를 하나씩 줄이면 된다.
윤리와 규칙, 선을 지키는 자동화
알림 봇은 편리하지만 웹사이트 운영 정책과 법률의 테두리 안에서 움직여야 한다. 서비스 약관을 확인하고, 요청 빈도와 수집 범위를 보수적으로 설정한다. 상업적 이용이 금지된 데이터를 대량으로 수집하거나 공유하면 문제가 될 수 있다. 봇이 사이트에 과부하를 주는 순간 모두에게 손해가 된다. 정중함과 절제가 결국 자신의 시스템을 지켜준다.
마무리, 실전에서 꾸준히 이기는 방법
알림 봇은 한번 만들어 두면 알아서 일한다. 그러나 진짜 성능은 지속적인 손질에서 나온다. 사이트가 바뀌면 누구보다 빨리 고치고, 사용자 피드백을 반영해 알림을 다듬는다. 숫자로 관리하고, 실패를 투명하게 드러내고, 야심보다 리스크를 먼저 줄인다. 그러면 예약을 놓치는 일이 눈에 띄게 줄고, 팀의 하루가 매끄러워진다. 오랫동안 현장에서 써 본 사람으로서 확실히 말할 수 있다. 견고함은 화려함보다 멀리 간다.