wp_cron()에 이벤트를 등록하는 wp_schedule_event()를 이용하면 정기적으로 해야 하는 작업을 자동화할 수 있다. (wp_cron() 함수는 정기적 작업을 중단할 때만 사용하므로 정기적 작업을 등록할 때는 사용할 일이 없다.)

예를 들면 Broken Link Checker라는 플러그인은 wp_cron에 이벤트를 등록해서 1시간에 한 번씩 깨진 링크를 체크한다.

나도 얼마 전에 정기적으로 자동화할 일이 생겼다. 트래픽을 분석해 보니, 구글과 네이버에서는 많은 수가 방문하는데, Daum에서는 방문수가 거의 없는 것이었다. 2012-07-12 트래픽을 보면, 트래픽이 아래와 같다.

  • 구글 검색 : 711회
  • 네이버 검색 : 221회
  • 다음 검색 : 7회

이건 뭔가 문제가 있다. 그래서 보니까 Daum 블로그 검색에서 내 블로그가 검색되지 않는 것을 발견했다. 웹 검색에서는 내 글이 좀 검색되는 것 같았는데 그마저도 몇 개 안 되는 것 같았고, 심지어 어떤 글은 내 블로그에서 퍼온 글만 검색되는 경우도 있었다. 다음 측에 문의를 했으나 시간이 걸린다는 답변만 오고 후속 답변이 오지 않았다. (이것에 대해서는 다음에 따로 글을 쓸 생각이다.)

그래서 궁여지책으로 다음 뷰에 블로그를 등록했다. 그리고 시간이 흐르니 다음 뷰에서 인지한 글은 검색이 되는 것이었다. 오호라! 그럼 다음 뷰에 글을 전부 다 보내면 되겠군!

하지만 몇 가지를 고려해야 했다. 일단 내 글이 700개가 넘는데 이걸 수동으로 하는 건 미친 짓이다. 그렇다고 스크립트를 짜서 한꺼번에 글을 보내는 것 역시 다음 뷰 측에서 제재를당할 수 있는 바보 같은 짓일 거다. 제재를 당하지 않는다고 해도 사용자들에게 “쟨 뭐냐” 하는 눈총을 당할 수 있다. 그래서 생각한 것이, 백그라운드에서 프로그램을 돌리는 것이었다. 30분에 한 개씩 다음 뷰 IT 섹션으로 트랙백을 보내도록 하기로 했다. 그래서 wp_cron을 사용하게 된 것이다.

wp_schedule_event()를 사용해 보자

핵심 원리는 다음과 같다.

  1. 30분에 한 번씩 작동할 함수를 만든다. 나는 trackback_to_daum_view() 라는 함수를 만들었다.
  2. 이 함수를 작동시킬 action을 만든다. 나는 tbview라는 action을 만들었다. 이런 식으로 추가해 준다.
    add_action('tbview', 'trackback_to_daum_view');
    액션 추가는 매번 실행해 줘야 한다. 난 function.php에서 추가하도록 했다.
  3. 이 액션을 wp_schedule_event()에 등록한다. 아래와 같은 형식이다.
    wp_schedule_event( time(), 'hourly', 'tbview');
    등록은 한 번만 해야 한다.(예컨대 플러그인이라면 활성화할 때.) DB의 wp_options 테이블의 option_namecron인 로우에 저장되기 때문이다. 여러 번 등록하면 졸라 많이 등록되는 거다.

실행시킬 함수를 만든다

자, 이제 상세 설명 들어간다. 일단 실행시킬 함수를 만든다. 나는 아래와 같이 만들었다.

/**
 * 다음 뷰로 트랙백을 보내지 않은 포스트를 다음 뷰에 쏜다.
 */
function trackback_to_daum_view(){
  global $wpdb;
  
  $querystr = "
      SELECT $wpdb->posts.* 
      FROM $wpdb->posts
      WHERE $wpdb->posts.pinged not like '%v.daum.net%'
      AND $wpdb->posts.post_status = 'publish'
      AND $wpdb->posts.post_type = 'post'
      ORDER BY $wpdb->posts.post_date DESC
      LIMIT 1
   "; 
  
  $pageposts = $wpdb->get_results($querystr, OBJECT);

  if($pageposts){
    global $post;
    foreach ($pageposts as $post) {
      setup_postdata($post);
      $tb = array(
        'title' => get_the_title(),
        'excerpt' => get_the_excerpt(),
        'id'=> get_the_ID()
      );
      $tb['url'] = 'https://mytory.net/archives/' . $tb['id'];
       //return getPrintr($tb);
       trackback('http://v.daum.net/tb/ch/it', $tb['title'], $tb['excerpt'], $tb['id']);
     }
   }
}

이 부분에선 cron과 상관없는 분야가 두 개 나오는데, Custom Select Query와 trackback 함수다. trackback 함수야 굉장히 사용하기 편하게 돼 있지만, Custom Select Query는 좀 복잡하다. 보통 워드프레스에서 포스트를 가져올 때는 WP_Query 클래스를 사용 한다. 옵션을 통해서 글을 골라 오는데, 어디에 트랙백을 보냈는지에 따라 포스트를 불러올 수 있도록 하는 옵션은 없었다. 그래서 Custom Select Query를 사용해야 했다. 사용례는 아래와 같다.

global $wpdb;

$querystr = "
    SELECT $wpdb->posts.* 
    FROM $wpdb->posts
    WHERE $wpdb->posts.pinged not like '%v.daum.net%'
    AND $wpdb->posts.post_status = 'publish'
    AND $wpdb->posts.post_type = 'post'
    ORDER BY $wpdb->posts.post_date DESC
    LIMIT 1
 "; 

$pageposts = $wpdb->get_results($querystr, OBJECT);

if($pageposts){
  global $post;
  foreach ($pageposts as $post) {
    setup_postdata($post);
    //get_the_title(), the_excerpt(), get_the_ID() 처럼
    //post 안에서 사용하는 함수를 사용하면 된다.
   }
 }

함수를 실행시킬 action을 등록한다

action은 워드프레스에서 벌어지는 다양한 ‘작동’을 일컫는 말이다. 기본적으로 등록돼 있는 Action Reference를 보면 다양한 Action들을 볼 수 있다. 예컨대 Template Action에는 get_header 같은 Action도 있다. 헤더를 불러오는 ‘작동’인 거다. 여기에 특정 함수를 걸면 header를 불러올 때마다 해당 함수가 실행되게 되는 것이다.

그런데 여기서 핵심은 기존에 있는 특정 액션에 trackback_to_daum_view 함수를 거는 게 아니라, 새로운 액션을 만든다는 데 있다. 그래서 알아서 액션의 이름을 정하면 된다. wp_cron은 특정 시간이 되면 해당 액션을 호출하는 거다. 그래서 나는 tbview라는 이름의 액션을 만든 거다.

이 액션은 페이지가 로딩될 때마다 추가돼야 한다. 그래야 제대로 작동한다. 그래서 function.php에 아래와 같이 코드를 박은 것이다.

add_action('tbview', 'trackback_to_daum_view');

tbview라는 액션이 실행되면, trackback_to_daum_view라는 함수를 실행하라는 코드다. 만약 함수에 인자값이 들어간다면 세 번째에 인자값을 넣어 주면 된다.(add_action 함수 레퍼런스 참고)

액션을 wp_cron에 등록하기

이제 자신만의 액션을 만들었으므로 이 액션을 wp_cron에 등록하자. wp_cron에 특정 액션을 등록하는 함수는 wp_schedule_event()다.

이 함수는 한 번만 호출해야 한다. 여러 번 호출하면 여러 번 등록된다. 그래서 나는 특정 페이지를 만들고, 페이지 번호를 따서 테마 폴더에 page-1234.php라는 파일을 만들었다. (이렇게 하면 페이지 ID가 1234인 글을 호출했을 때 page-1234.php 파일을 템플릿 파일로 사용하게된다. 꼭 이렇게 할 필요는 없다. 자신이 알아서 적당한 곳에서 호출하면 된다. 페이지 번호는 페이지 편집할 때 주소 표시줄을 보면 알 수 있다.)

거기에 아래와 같은 코드를 작성했다.

if(wp_schedule_event( time(), 'hourly', 'tbview') === NULL){
	echo '다음뷰로 트랙백을 보내도록 설정했습니다.';
}else{
	echo '실패!';
}

wp_schedule_event에 들어가는 인자값 설명을 보면 아래와 같다.

wp_schedule_event($timestamp, $recurrence, $hook, $args);

$timestamp

첫 번째에 들어가는 $timestamp는 처음 실행할 기준 시각이다. 현재 시각 타임스탬프를 적으면 되기 때문에 time() 함수를 넣었다. (타임스탬프가 뭔지 궁금하면 검색해 보라.)

근데 설명을 보면 아래와 같이 나와 있다.

$timestamp(integer) (필수) 이벤트가 일어나기 바라는 첫 번째 시각. 반드시 UNIX timestamp 포맷으로 넣어야 한다. PHP의 time() 함수 대신에 워드프레스의 current_time( 'timestamp' )를 사용하는 게 좋다. 그렇지 않으면 첫 번째 실행이 블로그의 로컬 타임에 맞춰서 되지 않을 것이다. 그러면 혼란을 겪을 수 있다.

$timestamp(integer) (required) The first time that you want the event to occur. This must be in a UNIX timestamp format. You should use WordPress’ current_time( ‘timestamp’ ) instead of PHP’s time(); otherwise the first occurrence of the event will not be in your local time, which may lead to confusion.

그런데 나는 오히려 current_time( 'timestamp' )를 사용했다가 혼란을 겪었다. 제 시간에 실행이 되지 않았기 때문이다. 왜 이런 일이 벌어졌는지는 모르겠는데, 여튼간에 current_time( 'timestamp' )를 사용했을 때 제대로 작동하지 않는다면 time() 함수를 사용해 보기 바란다.

$recurrence

$recurrence 인자값에는 반복되는 시간 간격을 적어 준다. 키워드로 적어 줘야 한다. 키워드는 정해진 것이 몇 개 있다. wp_get_schedules() 함수를 실행하면 키워드를 볼 수 있는데,

echo '<h2>cron 시간 간격 키워드들</h2><pre>';
print_r(wp_get_schedules())
echo '</pre>';

이렇게 써 보면 키워드 목록을 볼 수 있다. 아래처럼 나온다.

Array
(
    [weekly] => Array
        (
            [interval] => 604800
            [display] => 주마다 한번
        )

    [bimonthly] => Array
        (
            [interval] => 936000
            [display] => Twice a Month
        )

    [hourly] => Array
        (
            [interval] => 3600
            [display] => 매 시간
        )

    [twicedaily] => Array
        (
            [interval] => 43200
            [display] => 일일 2회
        )

    [daily] => Array
        (
            [interval] => 86400
            [display] => 매일
        )

)

weekly, bimonthly, hourly, twicedaily, daily 가 바로 키워드다. 일단 나는 예시 코드에 hourly라고 적었다. (실제로는 twicehourly라는 키워드를 새로 만들어서 twicehourly로 실행하도록 했다. 원하는 시간 간격을 만드는 방법은 레퍼런스를 참고하면 된다.)

$hook

$hook은 대상 액션의 이름을 말한다. 문자열로 넣어 주면 된다. 아까 add_action으로 만든 액션이 기억날 것이다. 바로 ‘tbview‘였다. 그걸 넣으면 된다. 그래서 완성된 코드가 아래와 같은 것이다.

wp_schedule_event( time(), 'hourly', 'tbview')

자, 여기에 내가 몇 가지 검증 코드를 첨가한 게 아래 코드다.

if(wp_schedule_event( time(), 'hourly', 'tbview') === NULL){
	echo '다음뷰로 트랙백을 보내도록 설정했습니다.';
}else{
	echo '실패!';
}

비활성화 코드 만들기

활성화 코드를 만들었으면 비활성화 코드도 있는 편이 좋겠다. 이건 상대적으로 쉬우니까 뜯어 보고 알아서 적용하기 바란다. 질문있으면 댓글로 해 주면 된다.

$timestamp = wp_next_scheduled( 'tbview' );
if($timestamp){
	//다음뷰로 트랙백 보내는 거 비활성화
	remove_action( 'tbview', 'trackback_to_daum_view' );
	wp_clear_scheduled_hook( 'tbview' );	
	echo '다음뷰로 트랙백을 보내는 cron을 비활성화했습니다.';
}else{
	//다음뷰로 트랙백 보내는 거 활성화하기
	if(wp_schedule_event( time(), 'hourly', 'tbview') === NULL){
		echo '다음뷰로 트랙백을 보내도록 설정했습니다.';
	}else{
		echo '실패!';
	}
}

자, 한 번 활성화를 하면 그 때부터는 자동으로 동작하기 시작한다.

wp_cron 작업 목록 확인하는 방법

등록돼 있는 wp_cron 작업 목록을 확인해 보고 싶을 것이다. 아래 코드를 사용하면 된다.

echo '<h2>wp_cron 작업 목록</h2><pre>';
print_r(get_option('cron'));
echo '</pre>';

간단하게 플러그인을 설치해도 된다. cron으로 검색해서 설치하면 될 텐데, cron 들어간다고 모두 작업목록을 보여 주는 건 아니다. 나는 Cron View를 설치했다. 그러면 관리자 모드의 ‘도구’ 메뉴에 What`s in Cron? 이라는 항목이 생긴다.

핵심 정리

  • wp_schedule_event()에 등록하는 것은 action이다. 등록은 한 번만 해야 한다.
  • add_action은 매번 호출돼야 하며, 액션명은 알아서 정하면 된다.
  • 내 경우엔 time() 으로 해야 작동했다. current_time('timestamp') 로 하니까 제대로 작동하지 않았다. 제대로 작동하지 않으면 저걸 바꿔 보면 도움이 될 거다.

wp_cron의 작동 원리

이건 보너스다. wp_cron이 도대체 어떻게 작동하는 걸까 궁금했는데, wp_schedule_event() 레퍼런스에 잘 설명돼 있었다.

누군가 블로그에 방문했을 때, 스케쥴에 등록된 시간이 지났으면 액션이 작동한다.

The action will trigger when someone visits your WordPress site, if the scheduled time has passed.