![]()
"매주 월요일 주간 점검" 같은 반복 일은 머리로 기억하면 꼭 한 번씩 빠집니다.
그래서 기억 서버에 일정을 맡겼는데, 맡기는 과정에서 "도는 척하는 작업" 이라는 함정을 두 번 만났습니다.
이번 포스팅에서는 Claude Code 와 일하는 1인 운영에서 예약 일정을 서버가 쥐게 한 구조, 그리고 알림이 울리자마자 사라지던 문제와 유령 lease 를 정리한 방법을 풀어 보도록 하겠습니다.
서버는 알려 줄 뿐, 대신 일하지 않습니다
처음에 선택지를 셋 놓고 봤습니다.
1. 일정을 서버가 쥐고, 세션을 열면 오늘 할 일로 넣어 준다.
2. 백업 같은 정기 유지 작업은 서버가 직접 실행한다.
3. 사람 없이 서버에서 Claude 를 돌려 일까지 시킨다.
1번과 2번만 골랐습니다. 3번은 처음 설계 때부터 빼 둔 무인 실행이라 이번에도 넣지 않았습니다. 서버는 "오늘 이걸 하세요" 까지만 말하고, 실제 일은 제가 세션을 열어서 합니다.
반복 규칙은 cron 대신 읽히는 말로

반복 규칙을 cron 문자열로 두지 않았습니다. 대시보드에서 사람이 읽고 고칠 것이라, 0 9 * * 1 보다 weekly:MON 이 낫다고 봤습니다.
daily 매일
weekly:MON,THU 매주 월·목
monthly:1 매월 1일 (1~28 만 허용)
every:3 3일마다
매월 규칙은 1~28일만 받습니다. 29~31일은 없는 달이 있어서 그달을 건너뛰게 됩니다. 그래서 monthly:31 은 이유를 말하고 거부합니다.
일정은 두 종류입니다. task 는 만기가 되면 백로그에 일감 한 건을 만들고, remind 는 일감을 만들지 않고 세션 컨텍스트에 알림으로만 보입니다. "기억만 하면 되지 일감은 아니다" 를 표현할 자리가 필요했습니다.
두 번 돌아도 한 번만 생기게
만기 처리는 서버의 별도 프로세스(jobs)가 1분마다 합니다. 프로세스는 다시 뜰 수도 있고, 같은 날을 두 번 훑을 수도 있습니다.
그래서 만든 일감에 출처 표시를 답니다.
source = f"sch:{일정ID}:{만기일}" # 유니크 —— 같은 날 두 번 돌아도 한 건
출처가 유니크라서 같은 일정의 같은 날 몫은 두 번 생기지 않습니다. 재기동해서 다시 훑어도 안전합니다.
대시보드의 "지금" 버튼은 일감을 직접 만들지 않습니다. 만기일을 오늘로 당길 뿐이고, 만드는 일은 jobs 한 곳에서만 합니다. 화면이 같은 일을 한 벌 더 가지면 규칙이 두 갈래로 갈라지기 때문입니다.
함정 하나: 알림이 울리자마자 사라졌습니다
remind 일정이 만기가 됐는데 세션 컨텍스트에 보이지 않았습니다.
원인은 순서였습니다. jobs 가 만기를 처리하면서 다음 만기일을 바로 뒤로 밉니다. 그런데 컨텍스트를 만드는 쪽은 "다음 만기일이 오늘 이전인 것" 만 찾고 있었습니다.
처리하자마자 다음 만기가 미래로 가 버리니, 오늘 울린 알림을 컨텍스트가 찾을 수 없었던 것입니다.
그래서 컨텍스트 쪽에서 "마지막으로 울린 날이 오늘인 것" 도 함께 보도록 고쳤습니다. 실제로 돌려 보고 나서야 찾은 문제였습니다.
⚠️ 상태를 처리하는 쪽과 그 상태를 보여 주는 쪽이 따로 있으면, 처리하는 순간 보여 줄 근거가 사라질 수 있습니다. "언제 처리됐는지" 를 남겨 두세요.
함정 둘: 아무것도 안 도는데 "실행 중"

일감에는 실행 중(RUNNING) 상태가 있고, 일을 잡은 쪽이 lease(점유 기간)를 겁니다.
그런데 일을 잡은 쪽이 중간에 죽으면, 그 일감은 RUNNING 으로 영영 남습니다. 아무도 돌지 않는데 돌고 있다고 표시되고, 다른 쪽은 그 일을 잡지 못합니다.
그래서 jobs 가 1분마다 유효기간이 지난 lease 를 풀어 줍니다.
UPDATE tasks SET lease_owner=NULL, lease_until=NULL, status='READY'
WHERE status='RUNNING' AND lease_until IS NOT NULL
AND lease_until < now() - interval '6 hours'
점유 기간이 끝나고도 6시간이 지난 RUNNING 은 READY 로 되돌리고, 몇 건을 풀었는지 로그에 남깁니다.
제가 가장 쓰게 느낀 대목은 이것입니다. "잡았다" 를 기록하는 장치를 둘 때는, "잡은 쪽이 사라지면" 을 처리하는 장치도 반드시 함께 있어야 한다는 점입니다. 둘 중 하나만 있으면 상태 표시가 거짓말을 하기 시작합니다.
예약 일정을 붙일 때 제가 쓰는 기준
1. 서버는 알려 주는 데까지만 하고, 실제 일은 사람이 세션에서 합니다.
2. 반복 규칙은 사람이 읽을 수 있는 말로 쓰고, 애매한 날짜(29~31일)는 거부합니다.
3. 만기 처리는 두 번 돌아도 한 번만 생기게 출처를 유니크로 둡니다.
4. 만드는 코드는 한 곳에만 둡니다. 화면 버튼은 만기를 당길 뿐입니다.
5. 처리한 뒤에도 "오늘 처리됐다" 를 보여 줄 근거를 남깁니다.
6. 점유(lease)를 거는 곳이 있으면, 점유한 쪽이 사라졌을 때 풀어 주는 일도 함께 만듭니다.
예약 작업에서 무서운 건 실패보다 "도는 척" 이었습니다. 알림이 울렸는데 안 보이고, 아무도 안 도는데 실행 중이라고 나왔습니다.
혹시 운영 중인 작업 목록에 며칠째 "실행 중" 으로 멈춰 있는 항목이 있다면, 그게 정말 돌고 있는지 한번 확인해 보시는 건 어떨까요?
그러면 오늘도 모두 스테이블 하세요.
만든 것들
'Coding, Testing, Challenge' 카테고리의 다른 글
| Claude Code Mods 쓰기 전 확인할 것, 2.1.287보다 낮으면 아무 일도 일어나지 않는다 (0) | 2026.10.11 |
|---|---|
| Claude Code Mods란? 스피너에 숫자 하나 띄워 보고 알게 된 기존 기능과의 차이 (0) | 2026.10.11 |
| Git Bash에서 내 명령어가 안 잡히던 이유, Windows PATHEXT와 파이썬 진입점 두 가지 함정 (0) | 2026.10.11 |
| PostgreSQL 시간이 9시간 빨랐다, timestamptz UTC 표시 버그를 저장값은 두고 고친 기록 (0) | 2026.10.11 |
| AI 세션 기록 분류기가 업무 대화를 개인 쪽으로 보냈다, 분류는 동기화여야 하는 이유 (0) | 2026.10.09 |