crontab 실행 안됨, 등록 목록만 봐서는 왜 모르나?
결론부터 적습니다. crontab 실행 안됨 문제는 등록 목록에서 보이지 않습니다. crontab -l 은 “이 시각에 이 명령을 돌려라”라는 예약 내용을 보여 줄 뿐, 그 명령이 어젯밤 실제로 시작했는지, 시작했다가 중간에 죽었는지, 아예 시작조차 못 했는지는 알려 주지 않습니다. 목록에 줄이 있다는 사실은 의도의 증거이지 실행의 증거가 아닙니다.
저희가 이 차이를 데이터로 확인한 날은 2026년 10월 8일입니다. 등록된 크론은 168개였고, 그중 사람이 하던 일을 대신하는 작업 70개의 최근 30일 기록을 하나씩 셌습니다. 실행 흔적이 한 번도 없는 작업이 16개였습니다. 로그를 남기기는 하지만 형식이 제각각이라 횟수를 셀 수 없는 작업도 17개였습니다. 70개 가운데 거의 절반이 “돌았다”고 말할 근거를 갖지 못한 셈입니다.

이 숫자가 아프게 읽히는 이유는 하나입니다. 0회 16개는 어디서도 오류를 내지 않았습니다. 크론은 실패해도 알림을 보내지 않고, 시작을 못 하면 로그조차 남기지 않습니다. 사람이 “요즘 이 결과물이 안 보이네”라고 알아차리기 전까지 목록은 계속 초록불처럼 보입니다.
168개를 한 통에 넣고 세면 왜 틀리나?
처음부터 70개만 센 데는 이유가 있습니다. 등록된 크론 168개는 성격이 넷으로 갈립니다. 사람이 하던 일을 대신하는 업무대체 70개, 사람이 원래 하지 않던 일을 지켜보는 감시 45개, 판단을 앞당기는 보고 35개, 인프라 자체를 돌보는 위생 17개입니다. 아직 판정하지 않은 미분류가 1개 남았습니다.

“168개를 자동화했다”고 한 줄로 쓰면 숫자가 부풀고, 누가 한 번만 되물어도 무너집니다. 감시 작업은 사람이 원래 하지 않던 일이라 “사람 시간을 몇 시간 줄였나”라는 질문 자체가 맞지 않습니다. 위생 작업은 업무가 아니라서 셈에서 뺐습니다. 그래서 실행 횟수를 사람 시간으로 바꿀 수 있는 작업은 업무대체 70개뿐이고, 그중 사람이 하면 몇 분이 걸리는지 기준선까지 붙은 작업은 16개입니다.
이 분류 과정은 자동화 성과 측정, 크론 168개 중 업무를 대신한 건 70개에 따로 적었습니다. 이 글은 그 70개를 실제로 세었을 때 무엇이 나왔는지에서 출발합니다.
실행 0회 16개는 어떤 작업이었나?
기록은 0회 작업 가운데 10개의 이름을 셈법과 함께 남겨 두었습니다. 16개 중 어느 10개를 골랐는지는 기록에 없으므로, 아래 표는 전체가 아니라 이름이 남은 일부입니다. 셈법 칸의 “전용 로그”는 그 작업만 쓰는 로그 파일을 읽었다는 뜻이고, “로그 없음”은 읽을 로그 파일 자체를 찾지 못했다는 뜻입니다.

표를 다시 나눠 보면 성격이 다른 두 묶음이 섞여 있습니다. 전용 로그가 있는데 30일 동안 한 줄도 늘지 않은 작업이 3개입니다. 이쪽은 “로그를 쓰는 길은 살아 있는데 작업이 시작하지 않았다”고 읽을 수 있어 비교적 명확한 0회입니다. 나머지 7개는 로그 파일이 아예 없습니다. 이쪽은 0회일 수도 있고, 돌기는 도는데 흔적을 남기지 않는 작업일 수도 있습니다.
cron_kin_pipeline 이라는 이름은 네 줄에 걸쳐 나옵니다. 이름 뒤의 ~2·~3·~4는 같은 스크립트가 시각을 달리해 여러 번 등록돼 있다는 표시로 읽었습니다. 네 줄 모두 로그가 없으므로, 이 파이프라인은 하루 몇 번씩 예약돼 있으면서도 30일 동안 돌았다는 증거를 하나도 남기지 않았습니다. 같은 스크립트가 네 번 등록돼 있다는 사실이 “그래도 한 번은 돌겠지”라는 안심을 주기 쉬운데, 근거 없는 안심이 네 겹 쌓였을 뿐입니다.
hero_monthly_harvest 한 건은 판정을 미뤄 둡니다. 이름처럼 한 달에 한 번 도는 작업이라면, 최근 30일이라는 창이 실행일을 비껴가는 것만으로 0회가 나올 수 있습니다. 실행 주기를 아직 확인하지 않았기 때문에 이 한 건을 고장으로 세지 않았습니다.
처음 세운 가설은 무엇이었고 어디서 틀렸나?
첫 가설은 단순했습니다. 등록돼 있으면 돈다. crontab -l 에 줄이 있고 문법이 맞으면 리눅스가 알아서 실행해 준다고 믿었고, 그래서 자동화 목록을 만들 때도 등록 목록을 그대로 옮겨 적었습니다. 이 가설은 0회 16개로 깨졌습니다. 등록 목록에는 168개가 모두 멀쩡히 남아 있었습니다.
두 번째 가설은 그 반대편에 섰습니다. 등록 목록 대신 실행 흔적을 세면 정확하다. 로그 줄이든 결과물 기록이든 실제로 남은 흔적만 세면 “돌았나”라는 질문에 바로 답할 수 있다고 봤습니다. 실제로 이 계측기는 crontab -l 이 아니라 로그와 기록 파일에 남은 실행 흔적을 셉니다.
그런데 이 두 번째 가설도 절반만 맞았습니다. 흔적을 세는 방식이 하나가 아니었고, 그중 한 방식이 실행 횟수를 거꾸로 부풀리고 있었습니다. 0회 문제와 정반대 방향의 오류가 같은 표 안에 함께 있었습니다.
하루 한 번 도는 크론이 30일에 99회로 잡힌 이유는?
같은 계측에서 이름이 남은 업무대체 작업 다섯 개를 나란히 놓아 봤습니다. 다섯 개 모두 하루에 한 번 도는 일정입니다. 기술블로그 발행은 새벽 2시 20분, 리포트 발행은 오전 8시 20분, 채용 공고 수집 두 개는 오전 7시 7분과 7시 45분, 리드 이메일 수집은 새벽 2시에 돕니다. 하루 한 번이면 30일 동안 아무리 많아도 30회입니다.

결과는 셈법에 따라 깔끔하게 갈렸습니다. 로그 줄을 센 세 작업(공용 로그 grep 둘, 전용 로그 하나)은 정확히 30회였습니다. 매일 한 번씩 빠짐없이 돌았다는 뜻이고, 주기와도 맞습니다. 반면 산출 마커를 센 두 작업은 99회와 72회였습니다. 하루 한 번 도는 작업이 30일에 99번 돌 수는 없습니다.
산출 마커는 작업이 결과물을 만들 때마다 찍는 표시입니다. 한 번 실행에서 결과물을 여러 개 만들거나, 같은 결과물을 고칠 때마다 표시를 다시 찍으면 마커 수가 실행 수보다 커집니다(추정: 99와 72 모두 상한을 넘는 모양에서 나온 판단이고, 실행 한 번당 마커가 몇 개 찍히는지는 아직 세지 않았습니다). 즉 산출 마커는 “얼마나 만들었나”에는 쓸 만하지만 “몇 번 돌았나”의 답으로 쓰면 틀립니다.
0회 문제와 함께 보면 그림이 선명해집니다. 흔적이 없으면 0으로 떨어지고, 흔적이 겹치면 상한을 뚫고 올라갑니다. 두 오류 모두 “흔적 하나 = 실행 한 번”이라는 전제에서 나옵니다.
부풀린 실행 횟수가 대체 시간을 얼마나 키웠나?
실행 횟수가 틀리면 그 뒤의 숫자도 같이 틀립니다. 이 계측은 작업마다 “대체 시간 = 최근 30일 실행 횟수 × 사람이 하면 걸리는 1회 시간”으로 계산합니다. 기술블로그 발행은 사람이 쓰면 1편에 120분으로 잡혀 있고, 리포트 발행은 90분입니다. 둘 다 사람이 직접 재지 않고 작업 과정을 나눠 계산한 추정치입니다.

기록된 대체 시간은 기술블로그 발행이 198.0시간(99회 × 120분), 리포트 발행이 108.0시간(72회 × 90분)입니다. 같은 1회 시간을 두고 실행 횟수만 주기 상한인 30회로 묶으면 각각 60시간과 45시간이 됩니다. 이 두 값은 기록에 있는 값이 아니라 글을 쓰며 다시 계산한 값입니다. 반대로 로그를 센 세 작업의 대체 시간(10.0시간 두 건, 20.0시간)은 실행 횟수가 주기와 맞아 손댈 이유가 없습니다.
눈여겨볼 점은 크기의 순서입니다. 기록에서 가장 큰 두 숫자가 정확히 부풀린 셈법에서 나왔습니다. 자동화 효과를 보고할 때 맨 위에 올라가는 숫자가 가장 약한 근거 위에 서 있었다는 뜻입니다. 누군가 “하루 한 번 도는데 어떻게 99번이냐”고 물으면 그 자리에서 무너질 숫자였습니다.
로그가 없는 작업과 0회인 작업은 같은 신호인가?
같은 신호가 아닙니다. 0회는 “볼 곳을 봤는데 아무것도 없었다”이고, 로그 없음은 “볼 곳이 없었다”입니다. 앞의 것은 고장을 가리키고, 뒤의 것은 계측이 비어 있다는 사실을 가리킵니다. 둘을 한 칸에 넣으면 고장 난 작업과 멀쩡한데 말이 없는 작업을 구분할 수 없습니다.
셀 수 없는 17개도 같은 이유로 따로 둡니다. 로그는 남기지만 형식이 달라 횟수를 뽑아낼 수 없는 작업들입니다. 이 17개를 0으로 처리하면 실행 0회가 33개로 불어나고, 30으로 처리하면 아무 근거 없이 모두 정상으로 칩니다. 어느 쪽도 사실이 아니므로 “모름”이라는 세 번째 칸을 둡니다.
- 실행 0회: 읽을 로그가 있고, 30일 동안 실행 흔적이 한 줄도 없다. 고장으로 보고 원인을 찾는다
- 로그 없음: 읽을 로그 파일 자체가 없다. 고장인지 모르므로, 먼저 작업이 흔적을 남기도록 고친다
- 셀 수 없음: 로그는 있지만 형식이 달라 횟수를 못 뽑는다. 0도 정상도 아닌 “모름”으로 남긴다
- 상한 초과: 주기로 계산한 최대 횟수보다 크다. 실행이 아니라 다른 것을 세고 있다는 신호다
이 네 칸을 나누고 나면 “0회 16개”라는 숫자도 다시 읽힙니다. 이름이 남은 10개 가운데 확실히 0회라고 말할 수 있는 작업은 전용 로그를 가진 3개뿐이고, 나머지 7개는 “로그 없음” 칸에 더 가깝습니다. 고칠 순서도 여기서 정해집니다. 흔적을 남기지 않는 작업은 원인을 찾을 수조차 없으니, 먼저 흔적을 남기게 만들어야 합니다.
사람이 보지 않는 자동화에서 무엇을 증거로 삼나?
크론으로 돌리는 업무는 애초에 사람이 보지 않으려고 만든 것입니다. 그래서 사람이 한 번씩 결과를 들여다보며 확인하는 방식은 규모가 커질수록 무너집니다. 남는 증거는 작업이 스스로 남긴 흔적뿐이고, 그 흔적이 없으면 “돌았다”는 말은 누구도 확인하지 않은 주장이 됩니다.
«통과»를 정답의 증거로 읽지 않는다. 통과는 «검사한 것 중 걸린 게 없다»일 뿐이다.
이 문장은 원래 화면 품질 검사를 두고 쓴 규칙인데, 크론 목록에도 그대로 들어맞습니다. 등록 목록에 168개가 있다는 사실은 “등록한 것 중 지워진 게 없다”일 뿐입니다. 실행 흔적이 30회로 맞는다는 사실도 “흔적을 남기는 작업 중 빠진 날이 없다”일 뿐이고, 흔적을 남기지 않는 작업에 대해서는 아무 말도 하지 않습니다.
그래서 이 계측기는 crontab -l 을 세지 않고 로그와 기록 파일에 남은 실행 흔적을 셉니다. 그리고 흔적의 종류(전용 로그, 공용 로그 grep, 산출 마커)를 숫자 옆에 항상 같이 적습니다. 99회라는 숫자를 보는 사람이 그 옆의 “산출 마커”를 보고 의심할 수 있게 하려는 장치입니다.
crontab 실행 안됨을 직접 점검할 때 볼 네 가지
서버에 크론을 걸어 둔 분이라면, 아니면 외주로 받은 자동화가 정말 도는지 궁금한 분이라면, 아래 네 가지를 차례로 보시면 됩니다. 특별한 도구 없이 로그 파일과 달력만 있으면 됩니다.
- 작업마다 실행 흔적이 남는 곳이 있는가: 로그 파일 경로를 하나씩 적어 보고, 적을 곳이 없는 작업부터 흔적을 남기게 고친다
- 최근 30일 실행 횟수를 주기와 견줘 봤는가: 하루 한 번이면 30회가 상한이다. 0회도, 상한 초과도 둘 다 이상 신호다
- 무엇을 세었는지 숫자 옆에 적었는가: 로그 줄인지 결과물 수인지에 따라 같은 작업이 30회도 되고 99회도 된다
- 셀 수 없는 작업을 0이나 정상으로 처리하지 않았는가: “모름” 칸을 따로 둔다
남는 한계도 적어 둡니다. 0회 16개 각각이 왜 돌지 않았는지는 아직 하나씩 확인하지 않았습니다. 산출 마커가 실행 한 번에 몇 개씩 찍히는지도 세지 않았고, 이름에 monthly가 붙은 작업은 주기를 확인하기 전까지 판정을 미뤘습니다. 이 글의 숫자는 “무엇이 돌지 않았나”의 목록이지 “왜 돌지 않았나”의 답이 아닙니다.
같은 계측의 이전 측정과 세는 방식의 차이는 크론 실행 확인, 등록 목록이 아니라 실행 흔적을 세야 하는 이유에 정리했습니다. 회사 업무를 자동화했는데 실제로 도는지 확인할 방법이 없다면 상담으로 남겨 주세요.
글쓴이: SGK 스튜디오, 중소기업 홈페이지 제작·AI 챗봇·업무 자동화를 만드는 작은 개발 스튜디오