워드프레스 분류의 term 데이터를 캐시 데이터로 생성

연재고품격 고품질 워드프레스 무료 사진 저장소완결2

만료 기간이 있으며 데이터베이스에 저장하는 캐시 데이터를 워드프레스 Transient API로 생성하는 일반 내용에 관하여 지난 과정에서 살폈습니다. 이번 장에서 실제 예제 구성 요소 일부를 캐시 데이터로 생성하여 적용합니다. 다음은 캐시 데이터로 변경할 목록입니다.

  • 검색 폼의 photocat 및 mediacat 분류의 term
  • 프런트 페이지 photocat term 목록
  • 프런트 페이지만 제외 사이트 오른쪽 아래 mediacat term 목록

위의 목록에는 다음의 공통 코드가 있습니다.

// 분류 mediactat
$mediacat_terms = new WP_Term_Query( array(
    'taxonomy' => 'mediacat',
    'hide_empty' => false,
) );

// 분류 photocat
$photocat_terms = new WP_Term_Query( array(
    'taxonomy' => 'photocat',
    'hide_empty' => false,
) );

검색 폼은 사이트 전체 페이지에 출력하는데, 프런트 페이지 photocat 분류의 term 데이터 쿼리는 검색 폼의 photocat 분류 term 데이터 쿼리와 중복, 프런트 페이지 제외 전체 페이지 아래 출력하는 mediacat 분류의 term 데이터는 검색 폼의 mediacta 분류 term 데이터 쿼리와 중복입니다.

따라서, 2가지의 분류 term 쿼리로 필요한 데이터만 캐시 데이터로 생성하여 검색 폼과 나머지 term 목록에 역시 필요한 데이터만 사용하면 조금 더 효율적으로 운용할 수 있습니다.

위의 목록과 관련한 파일 및 이번 장에서 추가 필요한 파일은 다음과 같습니다. 모두 편집기로 엽니다.

  • inc/template-functions.php
  • front-page.php
  • searchform.php
  • footer.php

코드를 추가하고 변경하여 몇 가지 결과를 확인하는 정도로 이어갑니다.

분류 term 캐시 데이터 생성

현재, 각 파일에 있는 분류 term 쿼리를 다음의 기준으로 필요한 데이터만 캐시 데이터로 생성합니다.

  • term 아이디, term 이름, term 아카이브 URL 데이터만 생성
  • 만료 기간 1년

위의 기준과 Transient API 일반 사용 방식으로 템플릿 태그(함수)를 구성하면 다음으로 정리할 수 있습니다.

/* 1. mediacat taxonomy */
function tax_mediacat_term_list_transient() {
    if ( false === ( $tax_mediacat_term_list_transient = get_transient( 'tax_mediacat_term_list_transient' ) ) ) {
        $mediacat_term = new WP_Term_Query( array(
            'taxonomy' => 'mediacat',
            'hide_empty' => false,
        ) );
        if ( ! empty( $mediacat_term ) ) {
            foreach ( $mediacat_term->terms as $term ) {
                $term_id = $term->term_id; // term id
                $term_name = $term->name; // term name
                $tax_mediacat_term_list[] = array(
                    'id' => $term_id,
                    'name' => $term_name,
                    'url' => get_term_link( $term_id ) // term link
                );
            }
        }

        $tax_mediacat_term_list_transient = $tax_mediacat_term_list;
        set_transient( 'tax_mediacat_term_list_transient', $tax_mediacat_term_list_transient, 365 * DAY_IN_SECONDS );
    }
    return $tax_mediacat_term_list_transient;
}

/* 2. photocat taxonomy */
function tax_photocat_term_list_transient() {
    if ( false === ( $tax_photocat_term_list_transient = get_transient( 'tax_photocat_term_list_transient' ) ) ) {

        $photocat_term = new WP_Term_Query( array(
            'taxonomy' => 'photocat',
            'hide_empty' => false,
        ) );        
        if ( ! empty( $photocat_term ) ) {
            foreach ( $photocat_term->terms as $term ) {
                $term_id = $term->term_id; // term id
                $term_name = $term->name; // term name
                $tax_photocat_term_list[] = array(
                    'id' => $term_id,
                    'name' => $term_name,
                    'url' => get_term_link( $term_id ) // term link
                );
            }
        }
        $tax_photocat_term_list_transient = $tax_photocat_term_list;
        set_transient( 'tax_photocat_term_list_transient', $tax_photocat_term_list_transient, 365 * DAY_IN_SECONDS );
    }
    return $tax_photocat_term_list_transient;
}

코드는 지난 과정에서 계속 반복한 것이므로 설명하지 않으며, 위의 코드를 termplate-functions.php 파일 아래에 추가합니다.

검색 폼

편집기에 열린 searchform.php 파일 28번 줄에서 38번 줄을 다음 코드로 대체합니다.

/* Transient API */
$mediacat_terms = tax_mediacat_term_list_transient();
foreach ( $mediacat_terms as $mterm ) {
    $mterm_name = $mterm['name'];
    echo '<label><input ' . checked( $tax_mediacat, $mterm_name, false ) . 'type="radio" name="mediacat" value="' . $mterm_name . '"> ' . $mterm_name . '</label>';
}

위의 코드로 대체하기 전 searchform.php 파일 52번 줄에서 65번 줄을 다음 코드로 대체하고 저장합니다.

/* Transient API */
$photocat_terms = tax_photocat_term_list_transient();
echo '<select name="photocat" id="photocat" class="select_photocat">';
echo '<option value="">전체</option>';
foreach ( $photocat_terms as $pterm ) {
    $pterm_name = $pterm['name'];
    echo '<option value="' . $pterm_name . '"' . selected( $tax_photocat, $pterm_name ) . '>' . $pterm_name . '</option>';
}
echo '</select>';

검색 폼은 사이트 전체 페이지에 출력되므로 여기까지 작업한 후 사이트 아무 페이지에 접근하면 캐시 데이터가 데이터베이스에 생성됩니다. 나머지 요소도 변경한 후 알아봅니다.

프런트 페이지

편집기의 front-page.php 파일 54번 줄에서 70번 줄을 다음 코드로 대체하고 저장합니다.

$photocat_terms = tax_photocat_term_list_transient();
if ( ! empty( $photocat_terms ) ) {
    echo '<h2>' . $f3_text . '</h2>';
    echo '<ul>';
    foreach ( $photocat_terms as $pterm ) {
        //$pterm_id = $pterm['id'];
        $pterm_name = $pterm['name'];
        $pterm_url = $pterm['url'];
        echo '<li>';
        echo '<a href="'. esc_url( $pterm_url ) . '">' . $pterm_name . '</a>';
        echo '</li>';
    }
    echo '</ul>';
}

footer.php

편집기의 footer.php 파일 14번 줄에서 32번 줄을 다음 코드로 대체하고 저장합니다.

$mediacat_terms = tax_mediacat_term_list_transient();
if ( ! empty( $mediacat_terms ) ) {
    echo '<div class="mediacats_list">';
        echo '<ul>';
        if ( ! empty( $mediacat_terms ) ) {
            foreach ( $mediacat_terms as $mterm ) {
                //$mterm_id = $mterm['id'];
                $mterm_name = $mterm['name'];
                $mterm_url = $mterm['url'];
                echo '<li>';
                echo '<a href="'. esc_url( $mterm_url ) . '">' . $mterm_name . '</a>';
                echo '</li>';
            }
        }
        echo '</ul>';
    echo '</div>';
}

캐시 데이터

이제 사이트 아무 페이지나 접근하여 캐시 데이터를 생성합니다. 이때 캐시 테이터가 없을 때, 처음 캐시 데이터를 생성할 때는 쿼리 개수가 늘어나는데, 사이트를 한 번 새로 고칠 때 어드민바의 QM 영역을 확인하고, 한 번 더 새로 고쳐 QM 영역의 쿼리 개수를 확인하세요.

따로 안내하지는 않지만, 사이트에서 검색 폼의 분류 term 데이터가 출력되고, 검색 결과가 올바르며, term 하나를 클릭하여 term 아카이브 페이지로 이동한다면 캐시 데이터를 사용하고 있다는 것입니다.

다음 그림은 localhost/wp-admin/options.php 주소로 접근했을 때 _options 테이블의 캐시 데이터와 만료 기간 데이터입니다.

전체 옵션 페이지의 transient 옵션 필드

캐시 데이터는 시리얼 데이터이므로 나오지 않는데, 연습장 파일에 다음 코드를 추가하고, 사이트에서 연습장 페이지를 보면 캐시 데이터를 볼 수 있습니다. 직접 확인하세요.

echo '<pre>' . print_r( get_transient('tax_mediacat_term_list_transient'), true ) . '</pre>';
echo '<pre>' . print_r( get_transient('tax_photocat_term_list_transient'), true ) . '</pre>';

캐시 데이터 삭제

만료 기간이 있는 캐시 데이터는 만료 후 자동으로 삭제된다고 이미 말했습니다. 사이트에 누군가 접근해야 삭제 이벤트가 실행된다는 것도 한 번 더 알립니다. 그런데, 이번 장에서 작업한 1년 유지 분류 term 캐시 데이터의 원본 term 데이터는 1년이 되기 전 변경될 가능성이 있을 수도 있지만, 없을 수도 있습니다.

가상 예제의 구성 기준에서 ‘미디어분류(mediacat)’와 ‘2차분류(photocat)’의 term은 운영자가 미리 정하여 제공하는 것이며, 이런 분류는 현실에서도 변경 가능성이 작습니다. 쉽게 ‘고정 데이터’라고 볼 수 있습니다. 물론, 오타에 의한 수정 또는 term 이름 변경 등의 편집 작업이 발생할 수는 있습니다. 당연히 새로운 term을 추가할 때도 있습니다.

만약, 예제 사이트에서 첨부되지 않은 attachment 포스트 타입의 개수를 캐시 데이터로 생성하여 제공한다고 가정해봅니다. 만료 기간을 정하지만, 새로운 attachment 포스트를 등록할 때와 등록한 포스트를 편집 또는 삭제할 때 기존 캐시 데이터를 삭제하는 기능을 추가해야 할까요?

잦은 포스트 등록과 변경이라면 캐시 데이터 삭제 기능은 추가하지 않는 게 좋다고 생각할 수 있는데 그렇지 않을 수도 있습니다. 캐시 데이터로 제공하는 포스트 전체 개수 정보가 사이트가 제공하는 다른 콘텐츠나 서비스에 영향이 있다면 삭제 기능을 추가하여 실시간으로 개수 데이터를 제공해야 합니다. 결론은 운영자의 판단과 사이트 제공 서비스의 특징에 따라 결정하면 됩니다.

개별 term 캐시 데이터 삭제

이런저런 이유를 떠나서 예제는 2가지 분류의 term 추가, 편집, 삭제 시 term 캐시 데이터를 삭제하도록 정의합니다. 다음 코드를 template-functions.php 파일 아래에 추가하고 저장합니다.

/* 3. 캐시 데이터 삭제 (Flush out the transients) */
add_action( 'edited_mediacat', 'delete_mediacat_term_list_transient' );
add_action( 'delete_mediacat', 'delete_mediacat_term_list_transient' );
add_action( 'created_mediacat', 'delete_mediacat_term_list_transient' );
function delete_mediacat_term_list_transient() {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    delete_transient( 'tax_mediacat_term_list_transient' ); // mediacat
}
add_action( 'edited_photocat', 'delete_photocat_term_list_transient' );
add_action( 'delete_photocat', 'delete_photocat_term_list_transient' );
add_action( 'created_photocat', 'delete_photocat_term_list_transient' );
function delete_photocat_term_list_transient() {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    delete_transient( 'tax_photocat_term_list_transient' ); // photocat
}

위의 코드는 개별 분류에 속한 term 데이터 작업으로 구분한 것인데, 다음 코드 예시처럼 분류 슬러그Slug로 각 액션을 구분하여 처리할 수 있습니다.

edited_{taxonomy_slug}
delete_{taxonomy_slug}
created_{taxonomy_slug}

캐시 데이터 전체 삭제

다음 코드처럼 분류 슬러그로 구분하지 않은 훅을 사용하여 2가지 분류의 캐시 데이터를 한꺼번에 삭제할 수도 있습니다.

add_action( 'create_term', 'delete_mediacat_term_list_transient' );
add_action( 'delete_term', 'delete_mediacat_term_list_transient' );
add_action( 'edit_term', 'delete_mediacat_term_list_transient' );
function delete_term_list_transient() {
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
        return;
    }
    delete_transient( 'tax_mediacat_term_list_transient' ); // mediacat
    delete_transient( 'tax_photocat_term_list_transient' ); // photocat
}

다음의 일반적 훅을 기억하는 정도면 됩니다.

create_term
delete_term
edit_term

캐시 데이터 삭제 확인

캐시 데이터를 삭제하는지 간단하게 확인합니다. 직접 해보세요. 브라우저 탭 3개로 다음 주소에 각각 접근하고, 각 탭의 페이지를 한 번 새로 고치세요. 그리고, 프런트 페이지 탭에서 어드민바의 쿼리 개수를 기억합니다.

  • localhost
  • localhost/wp-admin/edit-tags.php?taxonomy=mediacat&post_type=attachment
  • localhost/pics

위의 주소 목록 2번째 ‘미디어분류’ 편집 화면에서 ‘동영상’ 이름의 분류를 추가합니다. 바로, 프런트 페이지 탭으로 가서 프런트 페이지를 ‘한 번’만 새로 고칩니다. 이때 쿼리 개수가 늘었다면 캐시 데이터가 삭제된 것이며, 한 번 더 새로 고쳐 쿼리 개수가 줄어들면 캐시 데이터를 사용한다는 것으로 올바르게 적용된 것입니다. 마지막으로 위의 주소 목록 3번째 ‘둘러보기’ 페이지 탭에서 맨 아래 오른쪽에 ‘동영상’ term 이름이 출력되는지 확인하세요.

다시, 위의 주소 목록 2번째 관리페이지 ‘미디어분류’ 편집 화면에서 방금 추가한 ‘동영상’ term을 삭제하고, 프런트 페이지를 ‘한 번’만 새로 고쳐 어드민바 쿼리 개수가 늘어나는 것을 확인하고, 다시 새로 고쳐 쿼리 개수가 줄어드는지 확인하세요. 그리고 ‘둘러보기’ 페이지의 맨 아래 오른쪽에 ‘동영상’ term 이름이 출력되지 않는지 확인하세요.

분류의 term 추가 및 삭제와 더불어 term 이름 변경 등의 편집 과정, 나머지 photocat 분류도 같은 방법으로 캐시 데이터 삭제와 생성 결과를 확인하기 바랍니다.

사이트 성능 향상을 위한 캐시 데이터

Transient API로 캐시 데이터를 생성하여 사용하는 일반적인 목적은 사이트 로드 속도 향상에 있다고 하였는데, 아직 확인한 때가 없습니다. 성능 향상은 실제 운용 사이트에서 많은 방문자에 의한 트래픽이 있을 때 관련 요소를 도입할 가치가 있으며, 예제 사이트와 예제 구성 과정을 따라 하는 지금은 사실 의미가 없습니다. 그래도 이번 장의 캐시 데이터 도입으로 페이지 로드 속도가 향상되는지 간략하게 확인해봅니다.

다음 코드를 연습장 파일에 추가하고 저장한 후 사이트 연습장 페이지를 새로 고치세요.

/* WP_Term_Query */
echo 'WP_Term_Query<br />';

timer_start();

$photocat_terms = new WP_Term_Query( array(
    'taxonomy' => 'photocat',
    'hide_empty' => false,
) );
echo '<select name="photocat" id="photocat" class="select_photocat">';
    echo '<option value="">전체</option>';
    if ( ! empty( $photocat_terms ) ) {
        foreach ( $photocat_terms->terms as $pterm ) {
            $pterm_name = $pterm->name;
            echo '<option value="' . $pterm_name . '"' . $pterm_name . '>' . $pterm_name . '</option>';
        }
    }
echo '</select>';


echo('<P>1. Seconds: '.timer_stop( 0 ).'<br />');
echo('2. Seconds: '.timer_stop( 0, 10 ).'<br />');
echo('3. Seconds: '.timer_stop( 0, 20 ).'</P>');

/* Transient API */
echo 'Transient API<br />';

timer_start();

$photocat_terms = tax_photocat_term_list_transient();
echo '<select name="photocat" id="photocat" class="select_photocat">';
echo '<option value="">전체</option>';
foreach ( $photocat_terms as $pterm ) {
    $pterm_name = $pterm['name'];
    echo '<option value="' . $pterm_name . '"' . $pterm_name . '>' . $pterm_name . '</option>';
}
echo '</select>';

echo('<P>1. Seconds: '.timer_stop( 0 ).'<br />');
echo('2. Seconds: '.timer_stop( 0, 10 ).'<br />');
echo('3. Seconds: '.timer_stop( 0, 20 ).'</P>');

위의 코드는 timer_start, timer_stop 함수를 사용하여 코드 실행 시작과 끝부분의 시간을 알아보는 것으로, WP_Term_Query 클래스와 Transient API 캐시 데이터를 사용했을 때를 구분한 것입니다.

필자는 다음의 결과를 확인하였으며 드롭다운 term 목록은 생략하였습니다. 결과 데이터는 다를 수 있으며, 캐시 데이터는 10번 정도 페이지를 새로 고쳐야 확인 가능한 수치가 나오므로 나올 때까지 페이지를 새로 고치세요.

WP_Term_Query

1. Seconds: 0.004
2. Seconds: 0.0040011406
3. Seconds: 0.00400114059448242188

Transient API

1. Seconds: 0.001
2. Seconds: 0.0009999275
3. Seconds: 0.000999927520751953125

위의 결과 데이터처럼 캐시 테이터 사용에 의한 사이트 성능 향상을 사람이 체감할 수 있을 정도로 만들 수는 없습니다. 그러나, 더 복잡한 쿼리에서 필요한 데이터만 얻어 구성할 때나 많은 방문자가 일시에 몰릴 때 등을 생각한다면 워드프레스 Transient API 캐시 데이터 사용은 사이트 구성에 필요한 요소임이 분명합니다.

다음 링크의 이번 장 변경 파일을 받고 압축을 풀어 테마 루트에 덮어쓰세요.

싱글 캐시 데이터

예제에서는 inc/template-tags.php 파일에 다음 목록의 개수 테이터를 wpdb 클래스로 구하고 템플릿 태그로 정의하여 출력합니다.

  • 회원별 등록 이미지 포스트 ID(s)
  • 회원별 등록 사진의 추천 합계
  • 회원별 등록 사진의 다운로드 합계
  • 첨부되지 않은 attachment 포스트 수
  • 추천이 가장 많은 포스트

목록에서 회원별 등록 이미지 포스트 ID 데이터를 제외하면 모두 개수를 구하는 것으로 싱글 데이터로 볼 수 있습니다. 회원별 등록 이미지 포스트 ID도 하나의 데이터는 아니지만, 싱글 데이터를 나열한 것입니다.

예제에서 도입하는 것은 아니지만, 회원별 등록 이미지 포스트 ID를 1일 캐시 데이터로 구성한다면 다음으로 정의할 수 있습니다.

// 회원별 포스트 ID(s)
function author_pics_list_transient() {
    $author_id = get_the_author_meta( 'ID' );
    $transient_name = 'author_pics_list_' . $author_id;
    if ( false === ( $author_pics_list = get_transient( $transient_name ) ) ) {
        global $wpdb;
        $pics_list_query = $wpdb->get_col( $wpdb->prepare( 
        "SELECT ID FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'inherit' AND post_author = %d",
        get_the_author_meta( 'ID' )
        ) );
        $author_pics_list = implode( ',', $pics_list_query );

        set_transient( $transient_name, $author_pics_list, 24 * HOUR_IN_SECONDS );
    }
    return $author_pics_list;
}

위의 코드에 의한 캐시 데이터는 테이터베이스에 다음의 형식으로 단순 숫자 나열로 저장됩니다.

// author_pics_list_{authorID}
24,25,26,27,29,30,33,35,37,38,40,43,45,47,50,52,55,57,59,61,62,65,67,70,71,73,75,77,79,82,84,86,87,90,92,94,97,98,101,103,104,107,109,112,113,115,117,118,121,123,126,127,130,132,134,138,140,142,145,146,149,150,152,154,156,158,160,163,165,167,170,172,174,176,178,181,184,186,188,191,193,195,197,201,203,206,208,210,213,215,218,220,222,224,227,230,233,235,239,241,243,244,247,250,253,255,258,261,263,265,268,269,272,275,277,281,282,284,287,290,291,294,296,298,301,304,307,309,312,314,317,318,321,323,325,327,331,333,335,337,339,342,343,347,350,352,354,356,358,360,362,365,367,369,371,372,375,377,379,382,384,387,389,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414

회원별 추천 및 다운로드 수도 캐시 데이터로 사용하기 위해서는 위의 코드 기준으로 조정하면 됩니다. 나머지 전체 이미지 포스트 수와 추천이 많은 포스트도 캐시 데이터로 정의하여 단일 데이터로 만들 수 있습니다.

경험적으로 이와 같은 싱글 데이터, 특히 단순 데이터 형식과 개수를 구하는 쿼리에 워드프레스 Transient API 방식을 사용하는 것은 앞에서 구성한 term 캐시 데이터만큼 유용하다고 생각합니다. 나중에 추가 학습하여 직접 만들어 보세요.

예제 목차

0. 고품격 고품질 워드프레스 무료 사진 저장소

1. 예제 구성 환경과 파일

2. XAMPP, 워드프레스, 테마, 플러그인 설치와 설정

3. 테마 Pics Press

4. page 포스트 타입과 페이지 템플릿, 메뉴 구성

5. 워드프레스 핵심 용어 짚기

6. 워드프레스 포스트 타입 attachment

7. 워드프레스 이미지 사이즈

8. 워드프레스 이미지 사이즈 추가 및 변경

9. 워드프레스 이미지 파일 제어

10. 타입 attachment 템플릿과 image.php

11. 워드프레스 이미지 메타 데이터

12. GPS 데이터를 워드프레스 메타 데이터로 저장

13. 이미지 메타 데이터를 포스트 메타 데이터에 추가

14. Attachment 타입을 위한 워드프레스 커스텀 분류 등록

15. 이미지 메타 데이터를 워드프레스 분류와 필드 데이터에 저장

16. 이미지를 편집할 때 포스트 데이터와 메타 데이터 업데이트

17. 워드프레스 미디어 파일 업로드

18. 워드프레스 싱글 이미지 포스트 페이지

19. 워드프레스 아바타와 Author Archives

20. 워드프레스 이미지 사이즈별 데이터 출력

21. 워드프레스 폼 요소로 원하는 이미지 사이즈 다운로드

22. 워드프레스 텍스트 단락 및 줄 바꿈, wpautop

23. 워드프레스 사진의 EXIF 데이터 출력

24. 구글 지도에 표시하는 사진 촬영 위치

25. 워드프레스 attachment 포스트 타입의 아카이브

26. 워드프레스 함수로 자바스크립트 변수 데이터 생성

27. 워드프레스 커스텀 검색 – 쿼리 변수

28. 워드프레스 커스텀 검색 – 검색 폼과 쿼리 데이터

29. 워드프레스 커스텀 포스트 타입 ‘pic_album’

30. 커스텀 포스트 타입의 싱글 페이지

31. 워드프레스 WP_Query

32. 커스텀 쿼리, 페이지 템플릿, 포스트 아카이브

33. 분류 기준의 관련 포스트 커스텀 쿼리

34. wpdb 클래스로 구글 지도에 마커와 섬네일 표시

35. 워드프레스 분류 데이터 쿼리 클래스, WP_Term_Query

36. 워드프레스 템플릿 태그

37. 워드프레스 옵션 페이지, 옵션 필드

38. 워드프레스 사이트 프런트 페이지

39. 사이트 메뉴 및 포스트 페이지 링크

40. 워드프레스 Transient API

» 워드프레스 분류의 term 데이터를 캐시 데이터로 생성

42. 워드프레스 역할 그룹과 권한으로 구성 요소 제어

43. 간단한 워드프레스 코멘트 폼 수정

44. 워드프레스 대시보드 위젯 추가

45. 워드프레스 REST API 간략 이해

46. 워드프레스 REST API 응답에 커스텀 필드 추가

47. 워드프레스 REST API 커스텀 라우트 및 엔드포인트

48. 워드프레스 REST API 커스텀 엔드포인트로 구글 클러스터 지도 마커와 인포 윈도 표시

49. 워드프레스 REST API, Underscore.js 자바스크립트 템플릿, 포스트 Ajax Load More

50. 워드프레스 REST API, Underscore.js 자바스크립트 템플릿, 코멘트 Ajax Load More

51. 워드프레스 REST API 인증과 제한 및 제어