본문 바로가기

로딩 중...


크론 표현식 빌더

0 */6 * * *. 이게 여섯 시간마다인지, 매시 6분인지, 배포 직전에 매번 한 번 더 확인하게 되는 표현식입니다. 별표 다섯 개가 나란히 붙어 있으면 어느 자리가 분이고 어느 자리가 요일인지 눈으로 세다가 결국 손이 멈추죠. 크론 표현식 빌더는 표현식을 붙여넣으면 곧바로 사람 말로 풀어 주고, 앞으로 실제 몇 시에 도는지 다음 실행 시각까지 보여 주는 도구입니다.

스케줄러에 걸어둔 배치가 새벽 4시가 아니라 오전 4시 5분에 돈다는 걸 배포 다음 날 로그를 보고서야 알았던 적이 있습니다. 자리 하나를 잘못 세면 하루가 통째로 어긋나니까요. 그 확인 왕복이 싫어서, 표현식을 넣으면 다음 실행 시각이 바로 뜨는 걸 제가 쓰려고 만들었습니다.

사용 방법

주기 선택과 다음 실행 시각 미리보기

  1. 위쪽 탭에서 환경을 고릅니다. 일반 crontab이면 Unix/Linux(5필드), 스프링·Quartz면 Quartz/Spring(초 포함 6필드), AWS EventBridge면 AWS EventBridge(연도 포함 6필드)입니다. 표현식을 붙여넣으면 필드 개수를 보고 환경을 자동으로 맞춰 주기도 합니다.

    크론 표현식 빌더-guide-1

  2. 가운데 큰 입력창에 표현식을 직접 치거나 붙여넣습니다. 입력이 유효하면 아래에 평일 오전 9시에 실행 같은 설명과 다음 실행 시각이 바로 나오고, 자리를 잘못 쓰면 칸이 빨갛게 변하며 어느 필드가 틀렸는지 알려 줍니다.

    크론 표현식 빌더-guide-2

  3. 아래 다음 실행 예정에서 향후 5번의 실행 시각을 확인합니다. 오른쪽 타임존 선택으로 UTC와 KST(서울) 등을 바꿔 볼 수 있어서, 서버가 UTC로 도는데 내 머릿속은 한국 시간일 때 생기는 착오를 미리 잡을 수 있습니다.

    크론 표현식 빌더-guide-3

크론 표현식 기본

Unix/Linux 표준은 다섯 칸입니다. 왼쪽부터 분 시 일 월 요일 순서죠. 분은 0-59, 시는 0-23, 일은 1-31, 월은 1-12, 요일은 0-6입니다. 여기서 요일은 0이 일요일, 6이 토요일입니다. 그래서 0 9 * * 1-5는 월요일부터 금요일까지 오전 9시 정각이 됩니다.

각 자리에 쓰는 기호는 네 가지면 대부분 해결됩니다. *는 그 필드의 모든 값(매분·매시), ,는 값 나열(1,3,5), -는 범위(9-17은 9시부터 17시까지), /는 간격입니다. */15는 15마다 반복이라는 뜻이라 분 자리에 넣으면 15분마다가 되죠.

환경에 따라 칸 수와 규칙이 달라집니다. Quartz/Spring은 맨 앞에 초 칸이 붙어 여섯 칸이고, 요일이 1-7로 바뀌며 ?(일과 요일 중 한쪽을 비움)·L(마지막)·W(가까운 평일) 같은 기호를 더 씁니다. AWS EventBridge는 맨 뒤에 연도 칸이 붙은 여섯 칸입니다. 같은 "평일 9시"라도 환경마다 표현식 모양이 다르니, 상단 탭을 맞춰 놓고 봐야 오해가 없습니다.

크론 표현식 외우는 법

자리 순서만 외우면 절반은 끝납니다. Unix 기준으로 왼쪽에서 오른쪽으로 갈수록 시간 단위가 커진다고 생각하면 됩니다. 분 → 시 → 일 → 월 → 요일. 작은 것부터 큰 것 순이죠.

/가 붙으면 "N마다"라고 읽으세요. */10은 10마다, 분 자리면 10분마다입니다. -는 "부터 까지", ,는 "그리고"로 읽으면 0 9-11,14 * * *가 "매일 9시·10시·11시 그리고 14시"로 자연스럽게 풀립니다. 그래도 요일이 0부터인지 1부터인지, 이 자리가 시인지 일인지 헷갈리는 순간이 오는데, 그때는 외우려 애쓰지 말고 표현식을 그대로 붙여넣어 아래 설명과 다음 실행 시각으로 확인하는 편이 빠릅니다.

크론을 알아두면 좋은 이유

크론 문법은 리눅스 crontab 하나로 끝나지 않습니다. 스프링 배치의 @Scheduled, Kubernetes의 CronJob, GitHub Actions의 schedule, AWS EventBridge까지 주기적으로 뭔가를 돌리는 곳이면 거의 같은 다섯 칸(또는 여섯 칸)을 다시 만납니다. 읽는 법을 한 번 익혀 두면 새 도구를 만날 때마다 문법을 처음부터 배우지 않아도 되고, 남이 짜둔 스케줄을 보고 "이거 새벽 몇 시에 도는 거지"를 바로 판단할 수 있습니다.

자주 묻는 질문

표현식을 만들었는데 다음 실행 시각이 의도와 다르게 나와요.

가장 흔한 원인은 시간대 불일치입니다. 서버가 UTC로 동작하고 있다면 한국 시간 기준으로 쓴 표현식이 9시간 어긋나게 실행됩니다. 빌더 우측의 다음 실행 시각에서 KST와 UTC를 함께 확인하고, 서버의 실제 시간대에 맞는 값인지 검증해 보세요.

Linux crontab에 넣은 표현식을 Spring @Scheduled에 그대로 쓰면 안 되나요?

안 됩니다. Linux crontab은 분·시·일·월·요일 5개 필드를 사용하지만, Spring @Scheduled는 맨 앞에 초 필드가 추가된 6필드 형식입니다. Linux에서 쓰던 0 9 * * 1-5를 Spring에 그대로 넣으면 필드가 한 칸씩 밀려 의도와 전혀 다르게 동작합니다. AWS EventBridge도 연도 필드가 포함된 6필드라 주의가 필요합니다.

매월 마지막 날에 실행하려면 어떻게 표현하나요?

L 문자를 사용하면 됩니다. 예를 들어 0 0 L * ?는 매월 마지막 날 자정에 실행됩니다. 다만 L은 표준 Unix crontab에서는 지원하지 않고 Quartz, AWS EventBridge, Spring 같은 환경에서만 사용할 수 있습니다. Linux crontab을 쓴다면 28-31 범위와 조건을 조합하거나 스크립트 내부에서 말일 여부를 직접 판단해야 합니다.

표현식은 맞는 것 같은데 crontab에서 스크립트가 실행되지 않아요.

crontab의 실행 환경은 일반 셸과 다릅니다. PATH가 매우 제한적이라 셸에서는 잘 되던 명령이 crontab에서 실패하는 경우가 많습니다. 스크립트 경로를 절대 경로로 작성하고, 출력을 로그 파일로 리다이렉트해서(>> /var/log/script.log 2>&1) 실제 오류 메시지를 확인하는 것이 가장 빠른 디버깅 방법입니다.

이전 실행이 끝나지 않았는데 다음 실행이 겹쳐서 시작됩니다.

크론 자체에는 중복 실행 방지 기능이 없습니다. 단일 서버라면 flock 명령으로 파일 잠금을 걸면 되고, Kubernetes CronJob이라면 concurrencyPolicy: Forbid 옵션으로 이전 Job이 끝나기 전에는 새 Job을 시작하지 않도록 설정할 수 있습니다. 분산 환경에서는 Redis SETNX나 DB Advisory Lock 같은 분산 잠금을 활용하세요.