AI 활용

cron으로 '매월 첫째 토요일'은 못 쓴다? 날짜와 요일을 둘 다 지정하면 OR이 되는 함정

cron으로 '매월 첫째 토요일'은 못 쓴다? 날짜와 요일을 둘 다 지정하면 OR이 되는 함정

좋은 아침입니다. まさきん입니다.

집안 리마인더나 AI 정기 작업을 cron 형식으로 짜는 일이 늘었습니다. 그러다 ‘매월 첫째 토요일’을 쓰려다가 보기 좋게 함정을 밟았다는 걸 알게 됐습니다. 같은 방식으로 써 둔 분이 의외로 많을 것 같아서, 사양과 회피 방법을 정리해 둡니다.

※기사 공개 시점의 정보입니다. 최신 조건은 공식 사이트에서 확인해 주세요.

POINT
  • cron에서 날짜와 요일을 둘 다 지정하면 AND가 아니라 OR로 판정된다
  • 그래서 ‘0 9 1-7 * 6’은 첫째 토요일이 아니라 1~7일의 매일과 매주 토요일에 실행된다
  • 회피 방법은 요일로만 실행하고, 날짜 판정은 스크립트 쪽에서 하는 것
  • 일부 실행 환경에는 ‘몇째 주 무슨 요일’을 나타내는 확장 문법도 있지만, 쓸 수 있는지는 환경에 따라 다르다
  • 작성했다면 다음 실행일을 나열해 검산하고, UTC와의 차이도 확인한다

월 1회인 줄 알았는데, 월초에는 매일 실행되는 설정

계기는 월초 토요일에 보내는 장보기 리마인더였습니다. 한 달에 한 번만 오면 되니까 이렇게 써 두었습니다.

0 9 1-7 * 6

‘1~7일’이면서 ‘토요일’이라면 그달의 첫 토요일 하루로 정해질 겁니다. 사람 감각으로는 그렇게 읽히죠.

그런데 실제 동작은 달랐습니다. 매월 1~7일의 매일, 그리고 매주 토요일에 실행되는 형태였던 겁니다. 월 1회로 생각했던 게 월 초반에는 매일 아침 오는 리마인더가 되어 있었습니다.

5개 필드 복습

cron 형식은 공백으로 구분된 5개 필드로 이뤄져 있습니다. 왼쪽부터 순서대로 다음 의미입니다.

분 시 일 월 요일
0  9  1-7 *  6

’*‘는 ‘아무거나 상관없음’이라는 뜻입니다. ‘1-7’ 같은 범위나 ‘1,15’ 같은 나열도 쓸 수 있습니다. 요일은 많은 환경에서 0이 일요일, 6이 토요일입니다.

여기까지는 단순한 구조입니다. 문제는 ‘일’과 ‘요일’ 두 필드의 관계에 있습니다.

날짜와 요일은 OR로 판정된다

POSIX crontab 사양에는 다음과 같은 취지가 적혀 있습니다. 일 필드와 요일 필드가 둘 다 구체적으로 지정된 경우의 이야기입니다. 그때는 어느 한쪽에 일치하는 날이 대상이 된다고 되어 있습니다.

Linux에서 많이 쓰이는 cronie의 man 페이지에도 같은 설명이 있습니다. 두 필드가 모두 ’*‘가 아닐 때는 어느 한쪽이 일치하면 실행된다는 설명입니다. 예시로 ‘30 4 1,15 * 5’는 매월 1일과 15일에 더해 매주 금요일 4시 30분에도 실행된다고 나와 있었습니다.

즉 날짜와 요일을 둘 다 쓰는 순간 ‘그리고’가 아니라 ‘또는’이 됩니다. 개인적으로는 이게 cron에서 가장 직관에 어긋나는 부분이라고 생각합니다.

주의
  • 일 필드와 요일 필드 양쪽에 ’*’ 이외의 값을 쓰면, 어느 한쪽만 맞아도 실행된다
  • ‘17일이면서 토요일’로 생각하고 써도 ‘17일 또는 토요일’이 된다
  • 한쪽이 ’*‘라면 나머지 한쪽만으로 판정되므로 문제가 생기지 않는다

실제로 어떻게 실행되는지 나열해 보기

2026년 10월로 시험 삼아 세어 보겠습니다. ‘0 9 1-7 * 6’이 실행되는 날을 나열하면 이렇게 됩니다.

작성한 식의도2026년 10월에 실행되는 날횟수
0 9 1-7 * 6첫째 토요일만1~7일의 매일과 10·17·24·31일11회
0 9 * * 6매주 토요일3·10·17·24·31일5회
0 9 * * 6 + 날짜 판정첫째 토요일만3일1회

의도는 월 1회인데 11회나 오는 셈입니다. 1~7일 안에 있는 토요일은 겹치기 때문에 단순히 더한 것보다는 조금 줄어듭니다.

리마인더는 알림이 오는 게 당연한 물건입니다. 그래서 횟수가 너무 많아도 위화감을 느끼기 어려운 걸지도 모릅니다.

주요 실행 환경별 취급

cron 형식은 실행하는 환경에 따라 조금씩 다릅니다. 공식 문서에서 확인할 수 있었던 범위를 표로 정리했습니다.

실행 환경요일 세는 법날짜와 요일 동시 지정확장 문법시각 기준
POSIX crontab0~6(0이 일요일)어느 한쪽이 일치하면 실행사양에 없음사양에 명시 없음
Linux cronie0~7(0과 7이 일요일)어느 한쪽이 일치하면 실행명시 없음CRON_TZ로 지정 가능
Cloudflare Workers Cron Triggers1~7(1이 일요일)명시 없음L·W·# 지원UTC
GitHub Actions schedule0~6(0이 일요일)POSIX 형식을 쓴다고 기재@daily 등은 미지원기본은 UTC, timezone 지정도 가능

주의할 건 Cloudflare의 요일입니다. 1이 일요일이고 7이 토요일이라서 다른 환경과 하나씩 어긋납니다. 공식 문서에서도 헷갈리기 쉬우니 SAT 같은 세 글자 약어를 쓰는 방법을 권하고 있었습니다.

GitHub Actions는 POSIX cron 형식을 쓴다고 적혀 있습니다. 날짜와 요일의 관계까지는 페이지에 나와 있지 않습니다. 그래서 POSIX와 똑같이 OR이 된다는 전제로 짜 두는 게 무난하다고 봅니다.

회피 방법은 ‘요일로 실행하고 날짜를 본다’

가장 확실한 회피 방법은 한쪽 필드를 ’*‘로 두는 것입니다. 요일로만 실행하고, 날짜 판정은 스크립트 안에서 합니다.

  1. cron은 ‘0 9 * * 6’으로 해서 매주 토요일 9시에 실행한다
  2. 실행되면 오늘 날짜를 가져온다
  3. 날짜가 1~7일이면 리마인더를 보낸다
  4. 8일 이후라면 아무것도 하지 않고 종료한다

첫째 토요일은 반드시 1~7일 중 어딘가에 들어갑니다. 그래서 ‘토요일이면서 날짜가 7 이하’로 판정하면 한 달에 한 번만 실행됩니다. AND 조건은 cron에 맡기지 않고 내 코드로 쓴다는 느낌이죠.

이 방식은 응용이 가능합니다. 둘째 토요일이면 814일, 셋째 토요일이면 1521일입니다. 마지막 토요일이라면 ‘7일 뒤가 다음 달인가’로 판정하는 형태가 됩니다.

셸로 쓰는 경우의 예

Linux crontab에 직접 쓴다면 예를 들어 다음과 같은 형태가 됩니다.

0 9 * * 6 [ "$(date +\%d)" -le 7 ] && /path/to/remind.sh

여기서 ’%’ 앞에 백슬래시가 들어가 있는 게 포인트입니다. cronie의 man 페이지에는 명령 안의 ’%‘가 줄바꿈으로 취급된다고 적혀 있습니다. 이스케이프하지 않으면 의도하지 않은 위치에서 명령이 잘려 버립니다.

줄이 길어진다면 판정까지 통째로 스크립트 안에 넣는 편이 읽기 쉬울 수 있습니다. crontab 쪽은 ‘매주 토요일에 실행’만 남겨 두면 나중에 봐도 헷갈리지 않을 것 같습니다.

AI 정기 작업에서 쓰는 경우

AI에게 정기 작업을 맡기고 있는 경우에도 생각은 같습니다. 스케줄은 ‘매주 토요일 9시’로 해 둡니다. 그리고 지시문 맨 앞에 ‘오늘이 1~7일이 아니면 아무것도 하지 말고 종료’라고 씁니다.

Python으로 판정한다면 예를 들어 다음과 같은 형태입니다.

from datetime import datetime
from zoneinfo import ZoneInfo

today = datetime.now(ZoneInfo("Asia/Tokyo"))
if today.day > 7:
    raise SystemExit(0)  # 첫째 토요일이 아니므로 아무것도 하지 않음

여기서 일본 시간을 명시하는 게 중요합니다. 실행 환경의 시계는 UTC인 경우가 많아서, 날짜를 헷갈리지 않도록 하고 있습니다.

CHECK
  • cron 쪽은 한쪽 필드를 반드시 ’*‘로 둔다
  • 몇째 주 무슨 요일인지의 판정은 스크립트나 지시문 쪽에 쓴다
  • 날짜는 타임존을 명시해서 가져온다

확장 문법을 쓸 수 있는 환경도 있다

실행 환경에 따라서는 ‘몇째 주 무슨 요일’을 식 하나로 쓸 수 있는 확장이 있습니다. Cloudflare Workers의 Cron Triggers는 요일 필드에서 ’#‘을 쓸 수 있다고 표에 나와 있었습니다. Quartz라는 스케줄러에 가까운 확장을 지원한다는 설명입니다.

다만 공식 페이지에는 ’#‘을 쓴 구체적인 예시까지는 없었습니다. Cloudflare는 요일 숫자가 1부터 시작하므로 쓰는 법에 특히 주의가 필요합니다. 쓴다면 배포 전에 예상한 날에 실행되는지 확인하는 게 좋겠습니다.

한편 POSIX 사양이나 cronie의 man 페이지에는 ’#‘이 나오지 않습니다. GitHub Actions 표에도 없는 기호입니다. 환경을 옮겼을 때 동작하지 않을 수 있다는 점을 생각하면, 날짜 판정을 코드로 옮기는 방법이 이식성이 더 좋을 수 있습니다.

방법작성 난이도환경을 옮겼을 때잘 맞는 상황
요일로 실행하고 날짜 판정코드가 조금 늘어난다그대로 동작하기 쉽다가정 자동화 전반
’#’ 등의 확장 문법한 줄로 쓸 수 있다지원하지 않는 환경에서는 못 쓴다지원이 명시된 환경에서만 사용
날짜와 요일을 둘 다 쓰기겉보기엔 간단어디서든 OR이 되기 쉽다쓰지 않는 게 좋다

다음 실행일을 나열해 검산하기

cron 식을 쓰고 나면 다음에 실행될 일시를 몇 개 나열해 확인하는 걸 추천합니다. 이번 함정도 다음 몇 회분을 나열하면 한눈에 보입니다.

crontab.guru처럼 식을 자연어로 바꿔 주는 사이트도 있습니다. GitHub Actions 공식 문서에서도 형식을 만들고 실행 시각을 확인하는 데 쓸 수 있는 도구로 소개하고 있었습니다. 다만 서드파티 도구입니다. 표시가 실행 환경의 실제 동작과 반드시 일치한다는 보장은 없습니다. 확장 문법의 취급 등은 환경별 공식 문서와 함께 보는 게 안심이라고 생각합니다.

직접 확인한다면 한 달치 날짜를 돌리면서 조건에 맞는 날을 세기만 해도 충분합니다. 이 글의 10월 표도 날짜를 하루씩 나열해서 센 것입니다. 몇 줄짜리 코드면 되니, 식을 바꿀 때마다 돌리는 습관을 들여도 좋을 것 같습니다.

UTC 차이에도 주의

또 하나의 함정은 타임존입니다. Cloudflare의 Cron Triggers는 UTC로 동작한다고 명시되어 있습니다. GitHub Actions도 기본은 UTC이고, timezone을 지정하는 방법도 마련되어 있었습니다.

일본 시간은 UTC보다 9시간 빠릅니다. 그래서 일본 시간 아침 9시는 UTC로 정확히 0시입니다. 이보다 이른 시각이라면 이야기가 달라집니다. 예를 들어 일본 시간 토요일 8시는 UTC로 금요일 23시입니다.

이 경우 cron의 요일 필드는 토요일이 아니라 금요일로 해야 합니다. 게다가 스크립트 쪽 날짜 판정도 일본 시간으로 하지 않으면 어긋나 버립니다. 요일과 날짜가 둘 다 하루씩 밀릴 수 있다는 걸 기억해 두면 좋겠습니다.

가계 자동화에서 밟기 쉬운 상황

이 함정은 월 1회 리마인드를 만들 때 특히 밟기 쉽다고 느낍니다. 예를 들면 이런 상황입니다.

모두 ‘몇째 주 무슨 요일’ 형태가 되기 쉬운 것들입니다. 알림이 너무 많으면 점점 무시하게 됩니다. 중요한 청구 확인 알림까지 대충 넘기게 되면 자동화의 의미가 옅어질 수 있습니다.

가계 관련 자동 점검은 알림 빈도 자체가 사용감을 좌우합니다. 자동화로 생긴 시간에 매달 고정비도 함께 점검해 두면 효과가 클 수 있습니다.

가입하지 않아도 100포인트를 받는 요금 시뮬레이션 통신비도 함께 점검한다면 라쿠텐 모바일 확인하기

누르면 라쿠텐 로그인 페이지가 열립니다. 로그인하면 캠페인 내용이 표시됩니다.

제 결론, 그리고 또 하나의 걸림돌

결국 ‘매주 토요일’로 바꿨다

지금까지 회피 방법을 썼지만, 저 자신은 조금 다른 결론을 냈습니다. 운용을 다시 보고, 리마인더 자체를 매주 토요일 9시로 바꿨습니다.

0 9 * * 6

월 1회를 고집할 이유를 생각해 보니 사실 그렇게 강하지 않았습니다. 매주 온다면 그때 필요한지 아닌지 스스로 판단하면 될 뿐입니다. 구조를 단순하게 만드는 것도 하나의 답이라고 생각합니다.

정기 작업을 두는 곳이 제각각이었다

이번에 걸린 게 하나 더 있습니다. 고치려던 정기 작업을 좀처럼 찾지 못했던 겁니다.

정기 작업은 클라우드 스케줄과 Mac의 로컬 앱, 두 곳에 나뉘어 있었습니다. 이름으로 찾아도 한쪽에만 있어서, 어디 있는지 찾는 데 애를 먹었습니다.

두는 곳별로 목록을 만들어 두면 이럴 때 조금 편해질지도 모릅니다.

흔한 실수와 함정

cron으로 정기 작업을 짤 때 밟기 쉬운 실수를 정리해 둡니다.

제가 밟은 건 첫 번째와 여섯 번째입니다. 둘 다 한 번 알고 나면 막기 쉬운 종류의 실수라고 생각합니다.

시작하기 전에 확인할 것

자주 묻는 질문

Q. 날짜와 요일을 둘 다 쓰면 반드시 OR이 되나요?

POSIX 사양과 cronie의 man 페이지에는 어느 한쪽이 일치하면 실행된다고 적혀 있습니다. 한편 실행 환경에 따라서는 취급이 페이지에 적혀 있지 않은 경우도 있습니다. 그래서 둘 다 쓰지 않는 형태로 짜 두는 게 가장 안전하다고 생각합니다.

Q. 첫째 토요일을 한 줄로 쓰는 방법은 없나요?

Cloudflare Workers의 Cron Triggers처럼 요일 필드에서 ’#‘을 쓸 수 있는 환경도 있습니다. 다만 표준 cron에는 없는 확장입니다. 사용하는 환경의 공식 문서에서 지원 여부를 확인하고 쓰는 게 좋겠습니다.

Q. 그달의 마지막 토요일은 어떻게 쓰면 되나요?

Cloudflare에서는 ‘L’을 쓴 마지막 요일 예시가 공식 문서에 실려 있습니다. 표준 cron으로 쓴다면 매주 토요일에 실행해서 판정하는 형태가 됩니다. ‘7일 뒤가 다음 달이면 마지막 토요일’로 판정하는 게 이해하기 쉬울 수 있습니다.

Q. 날짜 판정을 1~7일로 해도 정말 괜찮나요?

그달의 첫 토요일은 반드시 1일부터 7일 사이 어딘가에 들어갑니다. 그래서 토요일에 실행해서 날짜가 7 이하인지 보면 첫째 토요일만 걸러낼 수 있습니다. 다만 날짜는 일본 시간으로 가져오지 않으면 어긋날 수 있으니 주의하세요.

Q. 다음 실행일은 어떻게 확인하면 되나요?

crontab.guru 같은 사이트에서 식의 의미를 확인하는 방법이 있습니다. GitHub Actions 공식 문서에서도 소개하고 있었습니다. 다만 서드파티 도구이므로 최종적으로는 실행 환경의 공식 정보와 함께 확인하는 게 안심입니다.

Q. GitHub Actions에서 일본 시간을 지정할 수 있나요?

공식 문서에서는 기본이 UTC라고 되어 있습니다. 그 위에서 IANA 형식의 타임존을 지정할 수 있다고 적혀 있었습니다. 예를 들어 Asia/Tokyo 같은 지정입니다. 서머타임이 있는 지역에서의 주의점도 적혀 있었지만, 일본 시간이라면 영향은 적을 것 같습니다.

참고한 공식 정보

마무리

cron에서 날짜와 요일을 둘 다 지정하면 AND가 아니라 OR로 판정됩니다. 그래서 ‘0 9 1-7 * 6’은 첫째 토요일이 아니라 월초의 매일과 매주 토요일에 실행돼 버립니다. 요일로만 실행하고 날짜를 코드로 보면 월 1회로 제대로 좁힐 수 있습니다.

작성한 뒤에는 다음 몇 회분의 실행일을 나열해서 확인하는 걸 추천합니다. UTC와의 차이나 실행 환경별 요일 세는 법에도 신경 써 두면 안심입니다.

정기 실행 시각을 정하는 법은 학교 연락을 자동으로 확인하던 이야기에서도 썼습니다. 가계 자동 점검에 대해서는 매일 밤 카드 사용 내역을 자동으로 확인하는 구조도 참고가 될지 모릅니다.

월 1회 리마인더를 짜 두신 분은 한 번만 식을 다시 살펴보세요.

가입하지 않아도 100포인트를 받는 요금 시뮬레이션 가계 자동화와 함께 휴대폰 요금은 라쿠텐 모바일로 점검해 보기

누르면 라쿠텐 로그인 페이지가 열립니다. 로그인하면 캠페인 내용이 표시됩니다.

이 글에는 제휴 광고가 포함되어 있습니다. 이 사이트의 링크를 통해 상품·서비스에 신청하시면 제휴사로부터 보상을 받을 수 있습니다. 또한 운영자는 라쿠텐 그룹 소속 직원으로, 사원 추천 프로그램을 통해 보상을 받는 경우가 있습니다. 기사 내용과 평가는 광고 유무와 관계없이 운영자의 실제 경험과 조사를 바탕으로 작성했지만, 위 관계를 이해하신 후 읽어 주시기 바랍니다. 자세한 내용은 면책조항·제휴 링크 안내를 참고해 주세요. 면책조항·제휴 링크 안내

이 글에는 기계 번역이 포함되어 있습니다. 정확한 조건은 라쿠텐 모바일 공식 사이트의 다국어 페이지에서 확인해 주세요.

라쿠텐 모바일의 통신 지역·전파 상황이 궁금하신 분은, 공식 전파 개선·조사 요청 양식 을 통해 문의하실 수 있습니다.

ABOUT THE AUTHOR
まさきん

라쿠텐 그룹 근무·디지털 마케터(FP 자격 보유)

40대 초반, 맞벌이·네 자녀의 아빠. 라쿠텐 그룹에서 디지털 마케터로 근무하면서, 재무설계사(FP) 자격을 활용해 가계 관리와 자산 형성에도 힘쓰고 있다.

프로필 보기

관련 글

RELATED
Gemini(Google AI Pro) 패밀리 공유는 가족 5명까지. 설정 절차와 범위 AI 활용

Gemini(Google AI Pro) 패밀리 공유는 가족 5명까지. 설정 절차와 범위

Google AI Pro는 관리자 외에 가족 5명까지 공유할 수 있습니다. Gemini 앱과 저장용량은 공유되고 YouTube Premium Lite는 관리자만. Flow 한도는 가족이 나눠 씁니다. 설정 절차와 자녀 계정 처리를 공식 도움말로 정리했습니다.

2026.10.13 · 읽는 시간 약 13분
AI 구독 청구일과 가계부가 어긋나는 3가지 이유|Claude는 업그레이드하면 청구일이 바뀐다 AI 활용

AI 구독 청구일과 가계부가 어긋나는 3가지 이유|Claude는 업그레이드하면 청구일이 바뀐다

Claude는 월 중간에 Max로 올리면 청구 주기가 리셋되어 그날이 새로운 청구일이 됩니다. 달러 결제의 엔화 환산은 카드사에 데이터가 도착한 시점에 정해지고, 명세와 가계부 반영은 며칠 늦습니다. 영수증 메일을 기준으로 월을 정하는 5단계를 정리했습니다.

2026.10.13 · 읽는 시간 약 14분
freee 회계의 미처리 내역을 월 1회 대사 작업으로. 매월 5일 7:30·3구분·상한 15건으로 사람에게 넘기는 설계 AI 활용

freee 회계의 미처리 내역을 월 1회 대사 작업으로. 매월 5일 7:30·3구분·상한 15건으로 사람에게 넘기는 설계

freee 회계의 미처리 내역이 18건 정도, 주간 확인 6번 동안 움직이지 않은 채 남아 있었습니다. 매월 5일 아침 7:30에 집 Mac에서 돌리는 대사 작업의 설계와, 확정할 수 있는 분은 월 1건, 망설여지는 내역은 1건씩 상한 15건으로 사람에게 넘기는 기준을 정리합니다.

2026.10.12 · 읽는 시간 약 15분
라쿠텐 모바일로 전환 시 최대 14,000포인트 가입하지 않아도 100포인트를 받는 요금 시뮬레이션 →
지금 신청하기 →