회사 점심봇 삽질기
회사 근처 구내식당의 식단표는 일주일마다 네이버지도와 인스타그램에 올라온다. 매번 직접 확인하는 게 번거로워서, 크롤링해서 팀 채팅방에 보내주는 봇을 만들었다. 처음에는 Selenium으로 이미지 URL을 긁고, PIL로 오늘 요일 컬럼만 크롭해서 Gmail로 발송하는 구조였다. Windows 작업 스케줄러로 돌렸다.
2주 쓰면서 세 가지 문제를 만났고, 각각 판단이 필요했다.
문제 1: 지난주 메뉴가 온다
2/20에 메일이 왔는데 지난주 메뉴였다. 처음엔 “식당에서 아직 안 올렸나 보다” 했는데, 확인해보니 이미 올라와 있었다. 크롤링 코드 문제였다.
matches = re.findall(pattern, page_source)
image_url = matches[0]
피드에는 과거 이미지가 섞여 있는데, matches[0]이 항상 최신이 아니었다. Selenium이 피드를 로딩하는 타이밍에 따라 DOM 순서가 달라진다.
판단: URL 경로에 날짜(YYYYMMDD)가 들어 있으니까, 전체 매치에서 날짜를 파싱해 최신을 선택하게 바꿨다. DOM 순서에 의존하지 않는 명시적 기준.
문제 2: PC가 꺼지면 안 돈다
Windows 작업 스케줄러의 근본 문제. 출근 전이나 재택하는 날에 크론이 안 돈다. 이건 로컬 스케줄러의 태생적 한계라 환경을 바꿔야 했다.
선택지:
- cron-job.org 같은 외부 스케줄러: 추가 의존성
- GitHub Actions cron: 코드랑 같이 관리, Secrets로 키 관리, 무료
GitHub Actions를 골랐다. 코드가 이미 GitHub에 있으니 자연스럽고, 별도 서비스 가입이 필요 없다.
문제 3: Gmail → Google Chat
Gmail로 보내니까 팀 채팅방에서 바로 안 보이고, 각자 메일함을 열어야 했다. Google Chat 웹훅으로 바꾸기로 했다.
여기서 예상 못한 제약을 만났다. Google Chat 웹훅은 로컬 파일 첨부가 안 된다. base64도 안 된다. 공개 HTTPS URL만 받는다.
크롭한 이미지를 어딘가에 올려야 했다. Imgur를 먼저 시도했는데 앱 등록 페이지가 고장나 있었다 (2025년부터 알려진 이슈). ImgBB로 대안을 잡았다. 무료, API가 단순(POST + base64 + key), 별도 OAuth 불필요.
GitHub Actions에서 만난 것들
스케줄 지연
cron을 12:00 KST로 잡았더니 GitHub Actions는 정각을 보장하지 않아 최대 20분 지연이 생길 수 있었다. 점심이 12:30인데 12시 넘어서 도착하면 의미가 없다.
발송을 11:00 KST로 앞당겼다. 지연이 생겨도 12:00 전에는 도착한다.
push 권한
크롤링 후 menu_info.json을 자동 커밋+push하는 워크플로우에서 403이 터졌다. GITHUB_TOKEN 기본값에 write 권한이 없었다.
permissions:
contents: write
한 줄 추가로 해결. 이건 GitHub Actions 처음 써보면 한 번은 만나는 것 같다.
공휴일 처리
cron에서 평일만 걸러도 공휴일은 못 걸른다. config.py에 2026년 평일 공휴일 12일(대체공휴일 포함)을 수작업으로 넣었다. 공공 API로 가져오는 것도 생각했지만, 1년에 12일인데 API 의존성을 추가하는 게 오버엔지니어링이라고 봤다.
최종 구조
GitHub Actions
├── 월 10:00 KST: 크롤링 → menu_info.json 갱신 → auto commit+push
└── 평일 11:00 KST: 이미지 다운로드 → 크롭 → ImgBB 업로드 → Google Chat 카드 발송
| 항목 | Before | After |
|---|---|---|
| 실행 환경 | Windows 작업 스케줄러 | GitHub Actions |
| 발송 채널 | Gmail | Google Chat 웹훅 |
| 이미지 호스팅 | 메일 첨부 | ImgBB |
| 날짜 선택 | 첫 번째 매치 | URL 날짜 파싱 후 최신 |
돌아보면
점심 메뉴 하나 보내는 데 생각보다 판단이 많이 필요했다. 크롤링 타이밍, 실행 환경, 발송 채널, 이미지 호스팅, 스케줄 시각, 공휴일 처리. 하나하나는 사소한데, 조합이 되면 복잡해진다.
가장 중요했던 판단은 “로컬에서 클라우드로 옮기자”였다. 그게 안정성의 근본 문제를 해결했고, 나머지는 그 위에서 풀 수 있는 문제들이었다.