cron 표현식은 컴퓨터에 “이 일정에 이것을 실행해”라고 말하는 가장 간결한 방법이다. 다섯 개의 필드, 모두 숫자와 특수 문자, 문법을 배우면 모호함이 없다. 문제: 실제로 필요한 일반적인 패턴이 약 15개이고, 나머지 시간은 crontab -e 프롬프트에서 일요일이 0인지 7인지 기억하려고 쳐다보고 있다.
이 글은 5년 전에 가지고 싶었던 치트시트이다. 모든 일반적인 패턴을 복붙 가능한 표현식, 설명, 그리고 사람들을 당기는 Gotcha와 함께 설명한다.
다섯 개의 필드
* * * * *
│ │ │ │ │
│ │ │ │ └── 요일 (0-6, 일요일=0)
│ │ │ └──── 월 (1-12)
│ │ └────── 일 (1-31)
│ └──────── 시 (0-23)
└────────── 분 (0-59)
각 필드는 다음을 가질 수 있다:
- 특정 값:
5 - 와일드카드:
*(모든 값) - 범위:
1-5 - 스텝:
*/15(매 15) - 목록:
1,15,30 - 스텝이 포함된 범위:
1-30/5(1과 30 사이 매 5)
이것이 전부이다. 나머지는 이 기본 요소들을 조합하는 것이다.
패턴
매분
* * * * *
사용 사례: 항상 실행되고 있어야 하고 스케줄러가 재생성하는 작업. 원하는 것은 거의 아니다.
매 N분
*/5 * * * * # 매 5분
*/15 * * * * # 매 15분
*/30 * * * * # 매 30분
*/5는 “0부터 시작하여 매 5분”을 의미한다. 즉, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55.
Gotcha: */5의 첫 실행은 분 0이고, crontab을 저장한 시점이 아니다. 12:03에 저장하면 첫 실행은 12:08이 아니라 12:05이다.
매시간 정각
0 * * * *
Gotcha: 이것은 “매시간 분 0”이고, 1:00, 2:00, 3:00 등이다. “매시간 30분_past”를 원하면 30 * * * *를 사용한다.
매일 자정
0 0 * * *
Gotcha: 서버의 시간대이다. cron은 시스템 시간대를 사용한다. 서버가 UTC이고 미국 동부 자정을 원하면 변환이 필요하다. 가장 깔끔한 수정: 시스템 시간대를 원하는 것으로 구성하거나, CRON_TZ 환경 변수를 설정한다(대부분의 최신 cron 구현체가 지원).
매일 특정 시간
0 9 * * * # 오전 9:00
30 14 * * * # 오후 2:30
0 0 * * * # 자정
15 3 * * * # 오전 3:15
평일 오전 9시
0 9 * * 1-5
1-5는 요일이고, 월요일부터 금요일까지이다.
Gotcha: 0과 7은 모두 일요일을 의미한다. POSIX 사양은 둘 다 허용한다. 대부분의 구현체는 실수로 7을 사용해도 허용한다. 그것을 의존하지 마라.
매주 월요일
0 0 * * 1
매주 월요일 자정.
주 2회 (월요일과 목요일)
0 9 * * 1,4
월요일과 목요일 오전 9시.
매월 1일
0 0 1 * *
1일 자정.
매월 마지막 날
0 0 L * *
L은 Vixie cron / Quartz 확장이다. 표준 cron에는 없다. 표준 cron을 사용하는 경우 “마지막 날” 근사치에 의존해야 한다. 0 0 28-31 * *처럼 (28일, 29일, 30일, 31일에 실행되고, 2월에는 28일에 실행된다).
매 분기
0 0 1 */3 *
1월, 4월, 7월, 10월 1일 자정.
매주 일요일 정오
0 12 * * 0
매 6시간
0 */6 * * *
0:00, 6:00, 12:00, 18:00.
평일 오전 9시~오후 5시 매분
* 9-17 * * 1-5
월요일부터 금요일까지 9:00:00부터 17:59:59까지 매분 실행되는 작업. 아마 너무 공격적이지만 — 가능하다.
매월 1일 오전 2시 30분
30 2 1 * *
매월 마지막 금요일 오후 11시
0 23 * * 5#5
5#5는 “매월 5번째 금요일”이다(Quartz 문법). #과 L은 비표준 확장이고 — 표준 cron에는 “매월 마지막 금요일” 표현식이 없다. 표준 cron에서 이것이 필요하면, 날짜를 확인하는 래퍼 스크립트가 필요하다.
매 30초
표준 cron은 1분 이하로 갈 수 없다. 분 이하 스케줄링의 경우:
- systemd 타이머:
OnUnitActiveSec=30s - Kubernetes CronJob: 불가능(최소 1분). 루프 내
sleep 30이 있는 일반 Job을 사용한다. - Node.js 앱: 프로세스 내
setInterval(fn, 30_000).
일광 절약 시간
cron은 일광 절약 시간(DST)을 구현에 따라 작업을 두 번 실행(봄 앞당기기)하거나 건너뛰기(가을 뒤로)로 처리한다. 시간에 민감한 작업(청구 실행, 보고서)인 경우 DST 놀라움을 피하기 위해 1-3 AM 윈도우 바깥으로 충분히 스케줄한다.
Quartz 스케줄러는 DST 플래그로 이를 처리한다. 표준 cron은 그렇지 않다.
1분 해상도 제한
cron은 본질적으로 1분 해상도 도구이다. 분 이하 스케줄링이 필요하면 답은 “다른 도구를 사용하라”이다. 특히 Kubernetes의 경우:
apiVersion: batch/v1
kind: CronJob
metadata:
name: every-30-seconds
spec:
schedule: "* * * * *"
startingDeadlineSeconds: 10
jobTemplate:
spec:
template:
spec:
containers:
- name: worker
image: myworker:latest
command: ["/bin/sh", "-c", "for i in 1 2; do do_work; sleep 30; done"]
이것은 분당 두 개의 하위 작업을 발행하여 30초마다 작업을 실행한다. 거칠지만 작동한다.
Quarkus, EventBridge, Kubernetes CronJob은?
다른 스케줄러는 약간 다른 cron 문법을 사용한다. 다섯 필드 형태가 가장 일반적이다. 몇 가지 다른 것들:
- Quartz: 여섯 필드(초 포함), “특정 값 없음”을 위한
?와일드카드. - AWS EventBridge: 여섯 필드,
L과#확장 지원. - Kubernetes CronJob: 다섯 필드,
L이나#지원 없음. - Spring
@Scheduled: 여섯 필드(초 포함),?와L지원. - GitHub Actions: 다섯 필드, 확장 없음.
표준과 일치하지 않는 도구를 사용하는 경우 문서를 확인한다. 이 치트시트의 대부분의 패턴은 직접 전환된다. 비표준 확장(L, #, W)은 그렇지 않다.
문법을 암기하지 않고 cron 표현식 생성
문법을 암기하고 싶지 않다면, DevSpeedTools의 cron 생성기에서 다섯 개의 필드를 클릭하고 다음 다섯 번의 예약된 실행을 보여준다. “0 9 * * 1-5가 정말 평일 오전 9시를 의미하는지” 확인하는 가장 빠른 방법이다 — 답은 예이지만, 다음 실행 날짜를 보면 패턴이 구체화된다.
일회성 표현식의 경우, crontab.guru가 표준 레퍼런스이다. cron 생성기는 같은 작업을 하고 완전한 클라이언트 사이드이므로, 붙여넣는 표현식은 절대 브라우저를 떠나지 않는다.
요약
Cron 표현식은 몇 가지 규칙을 가진 작은 DSL이다. 다섯 개의 필드, * (임의), - (범위), / (스텝), , (목록)를 배우면, 필요할 95%의 스케줄을 표현할 수 있다. 나머지 5%는 스케줄러별 확장이나 다른 도구가 필요하다. 의심이 나면, 다음 실행을 보여주는 도구로 표현식을 생성하고 미리보기하라 — 멘탈 디버깅보다 훨씬 빠르다.