WordPress 자동 발행 사고 막기: 검수 메타데이터로 공개 상태 차단하기

기존의 짧은 뉴스 요약 글을 비공개로 정리한 뒤에도 주제와 맞지 않는 글 한 개가 다시 공개됐다. 수집 소스를 끄는 것만으로는 충분하지 않았다. 다른 REST API 클라이언트나 관리자 화면에서도 publish 상태를 보낼 수 있기 때문이다. 그래서 이 사이트는 새 글에 검수 표시가 없으면 WordPress가 공개 요청을 draft로 바꾸도록 마지막 방어선을 추가했다.

문제는 “생성”과 “공개”가 같은 단계였다는 점

자동화 도구는 초안을 만드는 데 유용하지만, 초안 생성 성공이 곧 공개 적합성을 뜻하지 않는다. 사이트 주제와의 관련성, 직접 경험 여부, 출처, 템플릿 잔여 문구와 개인정보를 사람이 확인해야 한다.

자료 수집 → 초안 생성 → 사람 검토 → 승인 표시 → 공개

기존 자동 수집원은 비활성화했고, 메인 도메인에 대한 자동 예약도 애플리케이션 단계에서 중지했다. 여기에 WordPress 자체 검사를 더해 외부 도구의 설정이 바뀌더라도 바로 공개되지 않게 했다.

검수 상태를 REST API에서 다룰 수 있게 등록했다

먼저 hongzi_editorial_status라는 post meta를 등록했다. 문자열 하나만 저장하고, 글 편집 권한이 있는 사용자만 수정할 수 있다.

register_post_meta('post', 'hongzi_editorial_status', array(
    'type'              => 'string',
    'single'            => true,
    'show_in_rest'      => true,
    'sanitize_callback' => 'sanitize_key',
    'auth_callback'     => function () {
        return current_user_can('edit_posts');
    },
));

show_in_rest를 켠 이유는 승인 과정을 관리자 화면뿐 아니라 인증된 REST 클라이언트에서도 같은 방식으로 처리하기 위해서다. 공개용 REST 응답에 비밀을 저장한다는 의미는 아니다. 값은 단순한 편집 상태이며 API 인증과 WordPress 권한 검사를 통과해야 수정할 수 있다.

공개 요청에 승인 표시가 없으면 draft로 되돌린다

wp_insert_post_data 필터에서 post type과 요청 상태를 확인한다. 새 글이 publish로 저장되려 할 때 기존 검수 완료 글이 아니고 meta 값도 approved가 아니면 상태를 draft로 바꾼다.

if ($data['post_type'] === 'post' &&
    $data['post_status'] === 'publish') {
    $approved = get_post_meta($post_id, 'hongzi_editorial_status', true);
    if ($approved !== 'approved') {
        $data['post_status'] = 'draft';
    }
}

이미 검수를 마친 기존 글 일곱 개는 이 기능을 추가하기 전에 작성돼 meta가 없으므로 ID allow-list로 유지했다. 새 글은 먼저 draft로 저장하고, 검수 후 meta를 approved로 바꾼 다음 별도 요청에서 공개한다. 생성과 승인을 같은 요청으로 합치지 않는 것이 핵심이다.

실제로 공개 차단이 되는지 테스트했다

기능을 추가한 뒤 인증된 REST API로 임시 글을 만들면서 일부러 status=publish를 보냈다. WordPress 응답의 실제 상태는 draft였고, 공개 글 목록에도 나타나지 않았다. 확인이 끝난 임시 글은 바로 삭제했다.

요청 상태: publish
저장 상태: draft
공개 글 목록 노출: 없음

반대로 검수한 글은 draft 상태에서 승인 meta를 먼저 저장한 뒤 publish로 전환할 수 있다. 이 검사는 자동화 도구가 보내는 화면상의 상태가 아니라 WordPress DB에 최종 저장된 상태를 확인해야 한다.

비공개와 noindex는 같은 조치가 아니다

품질이 낮은 글을 검색에서만 숨기려고 noindex를 붙여도 방문자는 URL로 계속 읽을 수 있고 사이트 심사 도구가 페이지를 발견할 수 있다. 이번 정리에서는 보존할 글을 WordPress의 private 상태로 바꿨다. 공개 REST 목록과 sitemap에서는 제외되고, 로그인하지 않은 방문자는 내용을 볼 수 없다.

반면 공개 글의 짧은 카테고리 보관함은 글 자체가 아니므로 페이지는 탐색용으로 남기고 noindex, follow를 사용했다. 게시물 공개 여부와 보관함 색인 여부를 서로 다른 문제로 처리한 것이다.

이 장치가 대신할 수 없는 것

  • 승인 값을 무조건 넣는 잘못된 자동화까지 내용 검수해 주지는 않는다.
  • 이미 공개된 과거 글의 품질을 자동으로 개선하지 않는다.
  • 관리자 권한이 탈취된 상황을 막는 보안 기능은 아니다.
  • WordPress 업데이트와 백업, 실제 사람이 읽는 편집 절차가 계속 필요하다.

이 기능의 역할은 실수 한 번이 바로 공개 게시물로 이어지지 않도록 승인 단계를 강제하는 것이다. 자동화의 속도보다 공개 상태의 예측 가능성을 우선했다.

참고한 공식 문서