워드프레스 카테고리 메타박스 추가하고 드롭다운 카테고리 함수로 필수 요소로 만들기

Posted on

하나의 포스트는 하나의 분류(taxonomy)를 가지는 게 적절하다는 흔한 이야기가 있습니다. 워드프레스 사이트나 블로그에 일정 수 이상의 콘텐츠가 축적되어 특정 조건의 다양한 그룹으로 나눠(Query) 표현하고자 할 때가 되면 그 이야기가 틀린 것은 아니라 여깁니다.

그런데, 워드프레스 기본 상태에서 기본 분류인 ‘category’는 포스트 편집 화면에서 계층(hierarchical)의 메타박스로 정의되어 있습니다. 하나 이상의 카테고리(term)를 선택할 수 있다는 뜻입니다. 혼자 쓰는 블로그는 하나만 선택하는 자의의 기준을 두면 되지만, 강제성이 없으므로 복수를 선택하게 되는 경우가 더 많습니다. 특히, 여러 사람이 콘텐츠를 등록할 때는 하나만 선택하도록 제어할 필요가 있습니다.

이 글에서는 워드프레스 목록과 드롭다운 함수(List & Dropdown Functions)의 하나인 wp_dropdown_categories 함수로 드롭다운 형식의 카테고리(category) 메타박스를 추가하고, 기본으로 존재하는 카테고리 메타박스를 제거하는 아주 간단한 방법을 알아봅니다.

메타박스 추가, 출력 함수 정의, 메타데이터 저장의 3가지 단계가 일반적입니다.

메타박스 추가

워드프레스에서 메타박스는 추가는 다음의 코드로 간단하게 처리할 수 있습니다. add_meta_box 함수를 코덱스에서 검색하여 더 자세한 정보를 얻으세요.

// add_meta_boxes 훅 사용할 때 
add_action( 'add_meta_boxes', 'tax_category_add_meta_boxes', 10, 2 );
function tax_category_add_meta_boxes( $post_type, $post ){
    add_meta_box(
        'meta_box_tax_category', // 메타박스 ID (string)
        '카테고리', // 메타박스 제목
        'tax_category_build_meta_box', // 출력 함수
        'post', // 사용할 포스트 타입 편집 화면 또는 관리페이지 특정 페이지(screen)
        'side', // normal, side, advanced
        'default' // high, low
    );
}

// add_meta_boxes_{post_type}, 훅에 특정 포스트 타입을 지정할 때
add_action( 'add_meta_boxes_post', 'tax_category_add_meta_boxes' );
function tax_category_add_meta_boxes( $post ){
    add_meta_box(
        'meta_box_tax_category',
        '카테고리',
        'tax_category_build_meta_box',
        'post',
        'side',
        'default'
    );
}

위의 코드는 2가지 경우를 나열한 것인데, add_meta_boxes, add_meta_boxes_post 훅에 따라 전달하는 인자를 추가하는 부분이 다른 것을 확인할 수 있습니다. 둘 중의 하나를 선택하면 타입 ‘post’ 편집 화면 오른쪽에 메타박스가 추가된 것을 볼 수 있습니다.

출력, 드롭다운 카테고리

워드프레스 기본 분류인 ‘category’의 카테고리(term)를 wp_dropdown_categories 함수를 사용하여 드롭다운 형식으로 출력하는 코드는 다음처럼 아주 간단하며, 코덱스를 통해 쉽게 얻을 수 있습니다.

$args = array(
    //'show_option_all' => '카테고리',
    'show_option_none' => '선택',
    'option_none_value' => '',
    'orderby' => 'name',
    'show_count' => false,
    //'echo' => true, // Default 1/True
    'selected' => 0,
    'name' => 'tax_category_term',
    'id' => 'tax_category_term',
    'class' => 'postform',
    'taxonomy' => 'category', // 원하는 분류(taxonomy)
    'value_field' => 'term_id',
    'hide_empty' => false,
    'required' => true // 4.6부터
);
$select_category_term  = wp_dropdown_categories( $args );

위의 코드에서 워드프레스 4.6에 추가된 required 인자가 핵심입니다. 좀 더 편리해졌습니다. 함수 관련해 여러 옵션은 값을 변경하면서 확인하여 직접 경험해야 합니다. 위의 코드와 함께 출력 함수를 작성하면 다음과 같습니다.

function tax_category_build_meta_box( $post ){
    wp_nonce_field( basename( __FILE__ ), 'tax_category_meta_box_nonce' );
    $tax_category_term = get_post_meta( $post->ID, 'tax_category_term', true );
    $args = array(
        //'show_option_all' => '카테고리',
        'show_option_none' => '선택',
        'option_none_value' => '',
        'orderby' => 'name',
        'show_count' => false,
        //'echo' => true,
        'selected' => $tax_category_term, // 저장한 값(term)이 나타나도록(선택 상태) 설정
        'name' => 'tax_category_term',
        'id' => 'tax_category_term',
        'class' => 'postform',
        'taxonomy' => 'category',
        'value_field' => 'term_id',
        'hide_empty' => false,
        'required' => true // 4.6 버전에 추가
    );
    $select_category_term  = wp_dropdown_categories( $args );
}

추가한 메타박스에서 선택한 카테고리(term)의 ID 값은 tax_category_term 커스텀 필드에 저장하는데, 현재는 아직 저장하지 않은 상태이며, 드롭다운 목록에 해당 값이 선택된 상태로 나타나도록 설정한 것입니다. wp_nonce_field 함수로 히든 필드를 추가하여 최소한의 폼 데이터 검증 과정을 추가합니다.

위의 과정까지 진행하면 다음 그림의 메타박스를 볼 수 있습니다.

드롭다운 카테고리 메타박스

Select 폼 요소의 html 소스를 확인하면 다음과 같습니다.

<select required="" name="tax_category_term" id="tax_category_term" class="postform">

‘name’, ‘id’ 속성 값과 ‘postform’ CSS 클래스를 드롭다운 카테고리 출력 함수의 값과 비교하면 관계를 가늠할 수 있습니다. Html 소스에서 wp_nonce_field 함수 사용으로 생성되는 필드도 함께 확인하면 좋습니다.

카테고리 메타데이터 저장

추가한 메타박스의 드롭다운 카테고리에서 선택한 카테고리(term)의 ID 값을 tax_category_term 커스텀 필드에 저장하는 코드는 다음과 같습니다. Nonce 검증과 자동 저장 시 제외(return)는 기본 패턴으로 생각하면 됩니다. 물론 데이터의 성격에 따라 다를 수 있습니다.

add_action( 'save_post_post', 'tax_category_save_meta_box_data' );
function tax_category_save_meta_box_data( $post_id ) {
    // verify meta box nonce
    if ( !isset( $_POST['tax_category_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['tax_category_meta_box_nonce'], basename( __FILE__ ) ) ){
        return;
    }
    // autosave (자동 저장 제외)
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ){
        return;
    }
    // 권한 체크 (사이트 운영과 적합한 권한을 사용해야 함)
    if ( ! current_user_can( 'manage_options', $post_id ) ){
        return;
    }
    // 업데이트(저장), 분류(category)의 카테고리(term) 연결
    if ( isset( $_REQUEST['tax_category_term'] ) ) {
        // 메타데이터 업데이트 (tax_category_term 커스텀 필드에 카테고리(term) ID 저장)
        update_post_meta( $post_id, 'tax_category_term', sanitize_text_field( $_POST['tax_category_term'] ) );
        // 해당 포스트와 category 분류(taxonomy)의 해당 카테고리(term) 연결(term_id와 포스트 ID)
        wp_set_object_terms( $post_id, sanitize_text_field( $_POST['tax_category_term'] ), 'category', false );
    }
}

이 글에서는 분류(taxonomy) ‘category’의 카테고리(term)의 ID 값을 tax_category_term 커스텀 필드에 저장하는 것인데, 단지 ID만 저장하는 것으로는 기본 카테고리 메타박스의 역할을 대체하지 못합니다. 분류의 term과 포스트와의 관계 데이터가 데이터베이스에 저장되지 않는다는 뜻입니다. 물론 해당 포스트의 커스텀 필드 값만으로 프런트에 해당 분류(taxonomy)의 term 데이터를 출력하는 것은 어렵지 않지만, 메타박스를 추가하고 기존 메타박스를 제거하는 목적과 맞지 않습니다. 위의 코드에서 wp_set_object_terms 함수를 사용한 이유입니다.

메타박스의 데이터를 저장하는 것은 데이터 성격에 따라 원하는 방향과 방법에 따라 자유롭게 정의하면 됩니다.

기본 카테고리 메타박스 제거

아래는 2가지로 나누어 메타박스를 제거한 것인데, 권한(capability)을 조건으로 준 것입니다. 원하는 것으로 상황에 맞게 처리하면 됩니다. 메타박스 제거는 쉽게 얻을 수 있는 정보로 코덱스를 참고하는 것이 좋습니다.

// 관리자(manage_options Capability가 있는 사용자) 제외
add_action( 'admin_menu', 'my_remove_meta_boxes' );
function my_remove_meta_boxes() {
    if ( ! current_user_can( 'manage_options' ) ) {
        remove_meta_box( 'categorydiv', 'post', 'normal' );
    }
}

// 조건 없이 제거
add_action( 'admin_menu', 'my_remove_meta_boxes' );
function my_remove_meta_boxes() {
    remove_meta_box( 'categorydiv', 'post', 'normal' );
}

메타박스는 포스트 편집 화면 외에 관리페이지의 다양한 화면(screen)에도 존재하며, 간단한 기준이 있습니다. 메타박스의 Html 소스를 확인하여 해당 메타박스 블록의 ID 속성값을 확인하면 기본 메타박스, 추가한 메타박스, 플러그인에서 추가한 메타박스(이때는 훅의 순서를 변경해야 가능한 경우가 대부분입니다.)를 제거하는 데 빠른 힌트를 얻을 수 있습니다.

정리

다음 코드는 이 글의 최종 코드입니다. 코드를 복사하여 사용하는 테마의 functions.php 파일 등에 붙여넣은 후 포스트 편집 화면을 확인하면 됩니다.

add_action( 'add_meta_boxes_post', 'tax_category_add_meta_boxes' );
function tax_category_add_meta_boxes( $post ){
    add_meta_box(
        'meta_box_tax_category',
        '카테고리',
        'tax_category_build_meta_box',
        'post',
        'side',
        'default'
    );
}

function tax_category_build_meta_box( $post ){
    wp_nonce_field( basename( __FILE__ ), 'tax_category_meta_box_nonce' );
    $tax_category_term = get_post_meta( $post->ID, 'tax_category_term', true );
    $args = array(
        //'show_option_all' => '카테고리',
        'show_option_none' => '선택',
        'option_none_value' => '',
        'orderby' => 'name',
        'show_count' => false,
        //'echo' => true,
        'selected' => $tax_category_term, // 저장한 값(term)이 나타나도록(선택 상태) 설정
        'name' => 'tax_category_term',
        'id' => 'tax_category_term',
        'class' => 'postform',
        'taxonomy' => 'category',
        'value_field' => 'term_id',
        'hide_empty' => false,
        'required' => true // 4.6 버전에 추가
    );
    $select_category_term  = wp_dropdown_categories( $args );
}

add_action( 'save_post_post', 'tax_category_save_meta_box_data' );
function tax_category_save_meta_box_data( $post_id ) {
    // verify meta box nonce
    if ( !isset( $_POST['tax_category_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['tax_category_meta_box_nonce'], basename( __FILE__ ) ) ){
        return;
    }
    // autosave(자동 저장 제외)
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ){
        return;
    }
    // 권한 체크 (사이트 운영과 적합한 권한을 사용해야 함)
    if ( ! current_user_can( 'manage_options', $post_id ) ){
        return;
    }

    // 업데이트(저장), 분류(category)의 카테고리(term) 연결
    if ( isset( $_REQUEST['tax_category_term'] ) ) {
        // 메타데이터 업데이트 (tax_category_term 커스텀 필드에 카테고리(term) ID 저장)
        update_post_meta( $post_id, 'tax_category_term', sanitize_text_field( $_POST['tax_category_term'] ) );
        // 해당 포스트와 category 분류(taxonomy)의 해당 카테고리(term) 연결(term_id와 포스트 ID)
        wp_set_object_terms( $post_id, sanitize_text_field( $_POST['tax_category_term'] ), 'category', false );
    }
}

add_action( 'admin_menu', 'my_remove_meta_boxes' );
function my_remove_meta_boxes() {
    remove_meta_box( 'categorydiv', 'post', 'normal' );
}

위의 코드를 사용한 메타박스에서 카테고리를 선택하지 않을 때 나오는 화면은 다음과 같습니다.

드롭다운 카테고리 메타박스 선택 필수

글에서 ‘분류’, ‘taxonomy’, ‘term’, ‘카테고리’ 등 영문과 한글을 혼용하여 계속 표현했는데, ‘category’는 워드프레스 사이트에서 분류(taxonomy)의 하나라는 것을 워드프레스 사용이 아직 익숙하지 않다면 숙지할 필요가 있습니다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.