본문 바로가기

개발자 최종 업데이트 2026-09-12 · 약 7분

cron 표현식 읽는 법 — 일과 요일을 같이 쓰면 벌어지는 일

다섯 필드의 의미와 자주 쓰는 표현식을 정리하고, 일·요일을 동시에 지정하면 AND가 아니라 OR로 동작하는 함정과 시간대 문제를 짚습니다.

0 3 * * 1 같은 문자열을 보면 무슨 뜻인지 한참 헤아리게 됩니다. 그런데 배치 작업이 매일 돌아야 하는지 매주 돌아야 하는지는 서비스 운영에 직접 영향을 주는 문제라, 감으로 짐작하고 배포하면 곤란해집니다. 이 글에서는 cron 표현식을 읽는 법과 오래 쓴 사람도 걸리는 함정 하나를 정리합니다.

다섯 자리의 의미

표준 crontab의 표현식은 공백으로 구분된 다섯 개의 필드로 이루어집니다.

순서 필드 허용 범위
10–59
20–23
3일 (day of month)1–31
41–12
5요일 (day of week)0–7 (0과 7 모두 일요일)

읽는 순서는 작은 단위에서 큰 단위로입니다. 시계를 읽듯 "몇 분, 몇 시, 며칠, 몇 월, 무슨 요일"로 짚어 나가면 됩니다.

각 자리에 쓸 수 있는 표기

  • * — 모든 값. "매 분", "매 시"처럼 제한을 두지 않습니다.
  • 5 — 그 값일 때만.
  • 1-5 — 범위. 요일 자리에 쓰면 월요일부터 금요일까지입니다.
  • 1,15 — 목록. 일 자리에 쓰면 1일과 15일입니다.
  • */10 — 간격. 분 자리에 쓰면 0·10·20·30·40·50분입니다.

자주 쓰는 표현식

표현식 의미
*/5 * * * *5분마다
0 * * * *매시 정각
0 3 * * *매일 새벽 3시
0 3 * * 1매주 월요일 새벽 3시
0 9 1 * *매월 1일 오전 9시
30 18 * * 1-5평일 오후 6시 30분
0 0 1 1 *매년 1월 1일 자정

함정: 일과 요일을 함께 쓰면 OR이 된다

이것이 cron에서 가장 많이 사고가 나는 지점입니다. 다섯 필드는 모두 AND로 묶일 것 같지만, 일(3번)과 요일(5번) 두 필드만은 예외입니다. 둘 다 *가 아닌 값으로 지정하면 둘 중 하나만 맞아도 실행됩니다.

0 0 1 * 1을 "매월 1일이면서 월요일인 날"로 읽으면 틀립니다. 실제로는 "매월 1일 또는 매주 월요일"이라 한 달에 다섯 번 안팎으로 돌아갑니다. 한 달에 한 번 돌 줄 알았던 작업이 매주 실행되는 셈입니다.

"매월 1일이면서 월요일"을 정말로 표현하려면 cron만으로는 불가능합니다. 스크립트 안에서 요일을 한 번 더 확인하고 아니면 그냥 종료하는 방식으로 처리해야 합니다. Cron 표현식 번역기는 이런 식을 "~일 또는 ~요일에"로 풀어 주므로, 풀이를 읽어 보면 의도와 다른지 바로 확인할 수 있습니다.

*/n 은 나눠떨어지지 않으면 끝에서 끊긴다

*/7을 분 자리에 넣으면 7분마다 균등하게 돌 것 같지만 그렇지 않습니다. 각 시각의 0분부터 세기 때문에 0·7·14·21·28·35·42·49·56분에 실행되고, 그다음은 56분에서 7분 뒤인 63분이 아니라 다음 시각의 0분입니다. 즉 매시 경계에서 간격이 4분으로 줄어듭니다.

60을 나누어떨어지게 하는 값(2·3·4·5·6·10·12·15·20·30)을 쓰면 이런 불균등이 생기지 않습니다. 정확히 N분 간격이 필요한 작업이라면 cron이 아니라 작업 큐나 스케줄러 쪽이 적합할 수 있습니다.

시간대를 반드시 확인한다

cron은 실행되는 서버의 시간대를 따릅니다. 클라우드 서버는 기본값이 UTC인 경우가 많아, 한국 시간으로 새벽 3시에 돌리려고 0 3 * * *를 넣었다가 실제로는 정오에 돌아가는 일이 흔합니다. 서버의 시간대를 확인하거나, 스케줄러가 지원한다면 표현식에 시간대를 명시하세요.

서머타임을 쓰는 지역이라면 더 조심해야 합니다. 시계가 한 시간 건너뛰는 날에는 그 시각의 작업이 실행되지 않고, 되돌아가는 날에는 두 번 실행될 수 있습니다. 한국은 서머타임이 없어 이 문제에서 자유롭지만, 해외 리전에 배포한다면 고려해야 합니다.

방언에 따라 다른 것들

표준 crontab은 다섯 필드이지만, 구현체마다 확장이 있습니다.

  • 초 필드(6필드): 스프링 스케줄러나 Quartz는 맨 앞에 초를 추가한 여섯 필드를 씁니다. 다섯 필드용 표현식을 그대로 넣으면 한 칸씩 밀려 전혀 다른 시각에 돌아갑니다.
  • 별칭: @daily, @hourly, @reboot 같은 표현을 지원하는 구현체도 있습니다. 읽기는 편하지만 환경을 옮길 때 호환되지 않을 수 있습니다.
  • 요일 이름: MON, SUN 같은 영문 약어를 받는 곳도 있습니다.

쓰려는 환경의 문서를 먼저 확인하고, 이식성이 필요하다면 표준 다섯 필드와 숫자만 쓰는 편이 안전합니다.

배포 전 체크리스트

  • 다음 실행 시각 몇 개를 뽑아 의도와 맞는지 눈으로 확인했는가
  • 서버 시간대가 UTC인지 로컬인지 확인했는가
  • 일과 요일을 동시에 지정하지 않았는가 (지정했다면 OR임을 알고 있는가)
  • 작업이 예정보다 오래 걸려 다음 실행과 겹칠 경우를 대비했는가
  • 실패했을 때 알림이 오도록 해 두었는가

특히 네 번째 항목은 놓치기 쉽습니다. cron은 앞선 실행이 끝났는지 확인하지 않고 시각이 되면 새로 띄우므로, 5분마다 도는 작업이 7분씩 걸리면 계속 쌓입니다. 잠금 파일이나 중복 실행 방지 장치를 함께 두세요.

마무리

cron 표현식은 다섯 칸의 의미만 알면 읽기 어렵지 않습니다. 문제가 되는 것은 일과 요일이 OR로 묶인다는 예외와 서버 시간대 두 가지이며, 실제 사고도 대부분 여기서 납니다. 표현식을 한국어 풀이와 다음 실행 시각으로 확인하려면 Cron 표현식 번역기를, 로그에 찍힌 시각을 대조하려면 Unix 타임스탬프 변환기를 함께 쓰면 편합니다.

이 글과 관련된 도구