예약해 둔 글이 한참 뒤에 발행되거나, 백업이 며칠째 돌지 않거나, 업데이트 알림이 갱신되지 않습니다. 코드를 들여다봐도 이상이 없습니다. 원인은 대개 워드프레스의 크론이 크론이 아니기 때문입니다.
워드프레스의 크론은 크론이 아니다
진짜 크론은 시계를 보고 스스로 깨어납니다. 워드프레스의 것은 다릅니다. 방문 요청이 들어올 때마다 “지금 실행할 예정 작업이 있는가” 를 확인하고, 있으면 자기 자신에게 별도의 요청을 보내 그 작업을 실행합니다.
이 구조에서 따라오는 결과가 셋입니다.
조용한 사이트에서는 아무것도 실행되지 않습니다. 하루에 몇 명 들어오는 사이트라면 예약 발행이 몇 시간씩 늦는 것이 정상 동작입니다 — 고장이 아니라 설계입니다.
페이지 캐시를 붙이면 오히려 나빠집니다. 캐시된 응답은 PHP 를 타지 않으므로, 방문이 있어도 크론 확인 자체가 일어나지 않습니다. 성능을 개선한 바로 다음 주에 예약 작업이 굶기 시작하는 흔한 경로입니다.
루프백이 막힌 서버도 있습니다. 서버가 자기 도메인을 자기가 호출하지 못하는 구성(방화벽 · hosts · 내부 DNS)이면 확인은 되는데 실행 요청이 도달하지 못합니다. 이 경우 증상은 “가끔 되고 가끔 안 된다” 가 아니라 “전혀 안 된다” 입니다.
옮기는 절차 — 두 단계는 한 배포에
먼저 요청에 얹힌 실행을 끕니다.
define( 'DISABLE_WP_CRON', true );
⚠ 이것만 하고 멈추면 최악의 상태가 됩니다. 예정 작업은 계속 쌓이는데 실행하는 주체가 아무도 없고, 화면에는 아무 표시도 나오지 않습니다. 두 단계를 반드시 같은 배포에 넣으세요.
그다음 시스템 크론에 등록합니다.
*/5 * * * * cd /var/www/example && /usr/local/bin/wp cron event run --due-now --quiet
세 가지를 지킵니다. 웹서버 사용자로 등록하고(crontab -u www-data -e), wp 를 절대 경로로 부르고(크론의 PATH 는 로그인 셸과 다릅니다), 주기는 필요한 정밀도에 맞춥니다 — 5분이면 예약 발행 오차가 5분 이내이고, 더 촘촘해야 하면 1분으로 둡니다.
옮긴 뒤 확인
wp cron event list 로 예정 작업을 봅니다. 다음 실행 시각이 계속 과거로 밀려 있는 항목이 있다면 그 작업은 실행되지 않고 있는 것입니다. 정상이라면 시각이 미래로 갱신됩니다.
wp cron event list --fields=hook,next_run_relative,recurrence
wp cron event run --due-now --dry-run
wp cron test 는 루프백 스폰이 되는지를 확인하는 명령입니다. 시스템 크론으로 옮긴 뒤에는 여기서 실패해도 정상입니다 — 우리는 더 이상 스폰에 의존하지 않기 때문입니다. 이 사실을 모르면 멀쩡한 구성을 고장으로 오진하게 됩니다.
예약 발행은 GMT 로 판단된다
크론이 정상적으로 도는데도 예약 발행 시각이 이상하다면, 남은 원인은 데이터 쪽입니다. 예약 발행 이벤트는 post_date_gmt 를 기준으로 등록됩니다. 5회차에서 본 것처럼, 스크립트로 글 날짜를 갱신할 때 GMT 를 함께 주지 않으면 옛 값이 남아 목록의 날짜와 실제 발행 시각이 갈립니다.
사이트 시간대 설정도 같은 계산에 들어갑니다. 설치 직후 기본값 그대로 두면 로컬 날짜 표시가 실제 의도와 어긋납니다.
옮기고 나서 생기는 것
가장 큰 이득은 정시성이 아니라 기록입니다. 크론 항목에 출력 리다이렉트를 붙이면 언제 무엇이 돌았는지 파일로 남고, 실패했을 때 알림을 붙일 수 있습니다. 요청에 얹혀 있을 때는 그런 기록 자체가 존재하지 않았습니다.
여기서 한 걸음 더 나가면 감시입니다. 예약 작업이 멈춘 것을 며칠 뒤 사람이 발견하는 대신 InfraGuard 지속 관제처럼 상태를 계속 지켜보는 구성을 두면, 조용한 실패가 조용하지 않게 됩니다.
서버 계층의 구성과 캐시 배치는 서버 · 인프라 아카이브에서 더 다룹니다.
다음 회차
마지막 회차입니다. 지금까지 만든 명령 · 스크립트 · 검사를 사람이 기억해서 돌리는 대신, 파이프라인이 돌리게 만드는 구성을 다룹니다.