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

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

지난 과정에 이어 이번 장에서도 예제의 워드프레스 커스텀 검색 기능을 구성합니다. 실제, 커스텀 검색 기능은 지난 과정으로 완료되었습니다. 이번 장의 내용이 검색 기능 자체에 꼭 필요하다고 볼 수 없다는 뜻입니다. 그러나, 검색 폼의 분류 데이터 등, 직접 마크업으로 구성한 검색 폼은 조금 불완전하므로 다듬는 것이 좋습니다. 또, 폼에는 불특정 사용자가 데이터를 입력할 수 있으므로 약간의 보안 대응도 필요합니다. 지난 과정에서 일부 대응하여 처리한 부분도 있습니다.

먼저, 이번 장에서 변경할 파일을 미리 엽니다. 다음 목록을 참고하여 편집기로 파일을 여세요.

    • inc/main-query.php
    • search.php
    • searchform.php

검색을 위한 nonce

예제에서는 싱글 이미지 포스트 페이지에서 파일을 다운로드할 때 nonce 데이터를 사용하여 다운로드 과정을 제어한 때가 있습니다. 지금 추가할 nonce 데이터는 ‘검색 폼을 통해서만 검색’할 수 있도록 제어하는 것과 ‘사이트 내에서만 검색’할 수 있도록 제어하기 위함이 목적입니다. 물론, 검색 기능 구현에 꼭 필요한 것은 아닙니다.

편집기에 열린 searchform.php 파일 48번 줄에 다음 소스가 있습니다.

// searchform.php 파일 48번 줄
<input type="hidden" id="_srcnonce" name="_srcnonce" value="">

다음 코드로 변경하고 저장합니다.

// searchform.php 파일 48번 줄을 다음으로 대체
<?php wp_nonce_field( 'pics_search', '_srcnonce', false, true ); ?>

코드에서 falsereferrer 필드와 데이터 출력을 하지 않는다는 뜻이며, true는 nonce 데이터 출력을 뜻합니다. 사이트 소스에서 검색 폼 부분을 확인하면 알 수 있습니다.

확인을 위해 사이트의 검색 폼에서 검색 버튼을 클릭한 후 브라우저의 주소 필드를 확인해보세요. 다음의 URL 쿼리 스트링처럼 끝에 nonce 데이터가 출력될 것입니다.

  • localhost/?mediacat=&photocat=&pic_axis=&pic_width=&pic_height=&s=&_srcnonce=23a549a7f0

이제는 nonce 데이터로 검색 기능을 제어합니다. 편집기에 열린 main-query.php 파일 23번 줄에 다음 코드를 추가하고 저장합니다.

$_wpnonce = $_REQUEST['_srcnonce'];
if ( ! ( wp_verify_nonce( $_wpnonce, 'pics_search' ) ) ) {
    wp_redirect( esc_url( home_url( '/' ) ) );
    exit;
}

지금의 nonce 데이터는 쿼리 변수로 사용할 필요가 없으므로 1번 줄에서 $_REQUEST 변수를 사용하였고, 2번 줄의 nonce 데이터 검증 조건을 추가한 것입니다.

기능 확인을 위해 사이트의 검색 폼에서 검색 버튼을 클릭하고, 브라우저 주소 필드의 URL 쿼리 스트링을 복사합니다. 그리고 URL 쿼리 스트링 끝에 있는 nonce 데이터를 지우거나 아무 데이터를 추가 입력하여 자동으로 생성한 nonce 데이터와 다르게 입력한 후 키보드 엔터 키를 눌러 결과를 확인해보세요.

또, 먼저 복사한 자동으로 생성된 올바른 URL 쿼리 스트링을 세션이 다른 브라우저의 주소 필드에 추가하여 결과를 확인해보세요. 모든 결과는 ‘프런트 페이지로 이동’일 것입니다. 이와 같은 결과가 나온다면 올바릅니다.

검색 폼 다듬기

다음 그림은 현재의 검색 폼으로 이번 장에서 추가할 내용을 간략하게 정리한 것입니다.

워드프레스 8 - 무료 이미지 저장소

워드프레스 커스텀 검색 폼

폼 액션 URL

우선, 폼 액션 URL을 변경한 후 폼 프로그래밍을 진행합니다. 편집기의 searchform.php 파일 1번 줄을 다음으로 변경하고 저장합니다.

// 편집기의 searchform.php 파일 1번 줄을 다음으로 변경
<form id="search-form" role="search" method="GET" class="top_search" action="<?php echo esc_url( home_url( '/' ) ); ?>">

검색 결과를 출력하는 것은 search.php 파일이지만, 검색 결과 페이지는 쿼리 스트링을 포함한 도메인으로 구성되므로 퍼머링크가 필요하지 않습니다. 페이지 템플릿으로 커스텀 검색 결과 페이지를 구성하는 경우는 해당 페이지의 퍼머링크로 액션 URL을 지정할 수 있습니다.

분류 목록

편집기의 searchform.php 파일 10번에서 12번 줄의 소스는 다음과 같습니다. 분류 mediacat term을 직접 마크업한 것입니다.

<label><input type="radio" name="mediacat" value="그래픽"> 그래픽</label>
<label><input type="radio" name="mediacat" value="사진"> 사진</label>
<label><input type="radio" name="mediacat" value="일러스트"> 일러스트</label>

위의 소스를 다음으로 대체합니다.

<?php
    $mediacat_terms = get_terms( array( 'taxonomy' => 'mediacat', 'hide_empty' => false ) );
    foreach ( $mediacat_terms as $term ) {
        $term_name = $term->name;
        echo '<label><input type="radio" name="mediacat" value="' . $term_name . '"> ' . $term_name . '</label>';
    }
?>

2번 줄은 get_terms 함수를 사용하여 mediacat 분류에서 term에 속한 포스트가 없는 term도 포함하도록 정의한 것이며, 4, 5번 줄 에서 term 데이터 중 ‘이름’을 폼 요소에 출력하도록 정의한 것입니다.

앞의 코드를 추가한 편집기의 searchform.php 파일 22번에서 27번 줄의 소스는 다음과 같습니다. 분류 photocat term 중 몇 가지를 직접 마크업한 것입니다

<select name="photocat" id="photocat" class="select_photocat">
    <option value="">전체</option>
    <option value="감정">감정</option>
    <option value="건강">건강</option>
    <option value="건축">...</option>
</select>

위의 소스를 다음 코드로 대체하고 저장합니다.

<?php
    $args = array(
        'show_option_all' => '전체',
        'orderby' => 'name',
        'show_count' => false,
        'selected' => $tax_photocat,
        'name' => 'photocat',
        'id' => 'photocat',
        'class' => 'select_photocat',
        'taxonomy' => 'photocat',
        'value_field' => 'name',
        'hide_empty' => false
    );
    $select_photocat = wp_dropdown_categories( $args );
?>

분류 photocat term 목록을 출력하기 위해 wp_dropdown_categories 함수를 사용한 것입니다. 이 함수는 많이 사용하는, 보편적 함수로 코드 설명은 생략합니다.

지금 과정까지 저장한 후 사이트를 새로 고쳐 검색 폼을 확인하면 2개의 분류에 해당하는 term 목록은 모두 올바르게 출력되지만, 한 가지 오류가 나타납니다. QM 영역을 보면 확인할 수 있는데, 위의 코드 6번 줄의 정의하지 않은 $tax_photocat 변수가 원인입니다. 이어서 처리하므로 그대로 두고 사이트에서 검색 폼을 확인하여 분류의 term 목록이 출력되는지만 확인하면 됩니다.

함수를 사용하여 출력한 2개의 분류 term 목록은 나중에 다른 방법으로 출력할 것입니다.

검색 폼 데이터 유지

검색 폼에서 입력한 키워드나 선택한 필드 데이터를 검색 후에도 폼에 그대로 유지하도록 처리합니다. 지난 과정에서 진행한 쿼리 변수 코드가 이미 있어 그대로 사용하면 되므로 간단합니다.

편집기의 searchform.php 파일 맨 위에 새 줄을 만들어 다음 코드를 추가합니다.

<?php
/**
 * 검색 폼
 */

 $search_term = trim( wp_filter_nohtml_kses( get_query_var( 's' ) ) );
 if ( is_search() ) { // 검색 결과 페이지에서
    $tax_mediacat = trim( wp_filter_nohtml_kses( get_query_var( 'mediacat' ) ) );
 } else {
    $tax_mediacat = ''; // 검색 결과 페이지가 아닐 때
}
 ( !$tax_mediacat ) ? $query_mediacat = '모든 미디어' : $query_mediacat = $tax_mediacat;
 $tax_photocat = trim( wp_filter_nohtml_kses( get_query_var( 'photocat' ) ) );
 $pic_axis = trim( wp_filter_nohtml_kses( get_query_var( 'pic_axis' ) ) );
 $pic_width = absint( get_query_var( 'pic_width' ) );
 $pic_height = absint( get_query_var( 'pic_height' ) );
?>

7번 줄에서 12번 줄은 사이트 검색 폼의 상세 검색 팝업 활성 버튼(모든 미디어)에도 분류 mediacat term을 출력하기 위한 것입니다. 쿼리 변수는 검색에만 관계된 것이 아니라 워드프레스 쿼리 요청 전체에 사용되므로 예제와 같은 기능을 구현할 때 약간 주의할 필요가 있습니다.

필요한 변수를 모두 정의했으므로 이제는 폼 요소에 대입하여 정리합니다. 편집기 searchform.php 파일 19번 줄 근처에서 다음 1번 줄을 찾아 2번 줄로 변경합니다.

<div class="ddb">모든 미디어</div>
<div class="ddb"><?php echo $query_mediacat; ?></div>

파일 67번 근처에서 다음 1, 2, 3번 소스를 찾아 5, 6, 7번 줄로 변경합니다.

<input type="text" name="pic_width" value="" placeholder="너비(px)">
<span class="x">x</span>
<input type="text" name="pic_height" value="" placeholder="높이(px)">

<input type="text" name="pic_width" value="<?php echo $pic_width; ?>" placeholder="너비(px)">
<span class="x">x</span>
<input type="text" name="pic_height" value="<?php echo $pic_height; ?>" placeholder="높이(px)">

파일 76번 줄 근처에서 다음 1번 줄을 찾아 2번 줄로 대체합니다.

<input type="search" name="s" id="s" class="search_term" value="" placeholder="Search here...">
<input type="search" name="s" id="s" class="search_term" value="<?php echo $search_term; ?>" placeholder="Search here...">

폼 필드 유형에 따른 워드프레스 함수

이제 남은 것은 미디어 종류(mediacat)와 방향(pic_axis) 옵션 필드입니다. 카테고리(photocat) 드롭다운 필드는 이미 완료되었는데, 함수에서 제공하는 다음 옵션이 그 역할을 합니다. 함수를 사용할 때 편리한 때가 많습니다.

// wp_dropdown_categories 함수 사용 시
'selected' => $tax_photocat,

워드프레스는 일부 폼 필드의 유형(checkbox, radio button, select)에 따른 특정 함수를 제공하는데, 지금 구현하려는 기능에 유용한 다음 목록의 함수가 있습니다.

  • checked
  • selected
  • disabled

편집기의 searchform.php 파일 31번 줄 정도에는 다음 1번 줄이, 60번과 61번 줄 정도에는 다음 3, 4번 줄의 코드가 있습니다.

echo '<label><input type="radio" name="mediacat" value="' . $term_name . '"> ' . $term_name . '</label>';

<label><input type="radio" name="pic_axis" value="가로"> 가로</label>
<label><input type="radio" name="pic_axis" value="세로"> 세로</label>

워드프레스 함수를 사용하여 검색 폼에 방문자가 선택한 검색 데이터를 유지하려면 다음처럼 정의할 수 있습니다. 다음의 코드로 각각 대체하고 저장합니다.

echo '<label><input ' . checked( $tax_mediacat, $term_name, false ) . 'type="radio" name="mediacat" value="' . $term_name . '"> ' . $term_name . '</label>';

<label><input <?php checked( $pic_axis, '가로' ); ?> type="radio" name="pic_axis" value="가로"> 가로</label>
<label><input <?php checked( $pic_axis, '세로' ); ?> type="radio" name="pic_axis" value="세로"> 세로</label>

함수 checked 기본 예시는 다음 1번 줄과 같고, 2번 줄은 한글로 쉽게 해석하여 표기한 것입니다.

checked( $checked, $current, $echo );
checked( 유동데이터, 기준데이터, checked="checked" 출력 );

대체한 코드 중 다음의 false 설정 이유는 echo 함수가 존재하기에 checked 함수에는 출력 설정을 false로 설정한 것입니다.

// echo 함수가 존재하므로 checked 함수에는 false
echo '<label><input ' . checked( $tax_mediacat, $term_name, false ) . 'type="radio" name="mediacat" value="' . $term_name . '"> ' . $term_name . '</label>';

분류 목록 출력 방식은 나중에 변경한다고 말했는데, 그때 카테고리 목록은 selected 함수를 사용하여 검색 폼에서 선택한 데이터를 유지할 것입니다. 나중의 변경 부분만 제외하면 검색 폼 구성은 이것으로 끝입니다.

폼 데이터 보안 및 검증

예제에서는 워드프레스 함수를 사용하여 검색 폼에서 받은 데이터의 모든 태그와 공백을 제거하고, 일부는 양수 조건으로 다음처럼 정의하여 사용했습니다.

$search_term = trim( wp_filter_nohtml_kses( get_query_var( 's' ) ) );
$tax_mediacat = trim( wp_filter_nohtml_kses( get_query_var( 'mediacat' ) ) );
$tax_photocat = trim( wp_filter_nohtml_kses( get_query_var( 'photocat' ) ) );
$pic_axis = trim( wp_filter_nohtml_kses( get_query_var( 'pic_axis' ) ) );
$pic_width = absint( get_query_var( 'pic_width' ) );
$pic_height = absint( get_query_var( 'pic_height' ) );

먼저, 워드프레스 wp_filter_nohtml_kses 함수는 모든 태그를 제거하며, trim PHP 함수는 처음과 끝의 공백을 제거하는 함수입니다. 워드프레스 absint 함수는 양수 변환 또는 조건을 뜻하는 함수입니다.

검색은 데이터베이스에서 데이터를 검색하여 브라우저에 결과를 출력하는 것이 기본이며, 이때 악의적인 사용을 방지하기 위해 최소한의 기본 대응을 위한 코딩도 필수입니다. 기본 대응의 방식은 보통 특정 태그의 ‘부호화(Escape)’나 특정 문자의 제거입니다. 예제는 이러한 방식을 사용하였습니다. 검색을 위한 nonce 데이터 추가도 포함합니다.

검색 폼 구성이 끝난 현재 사이트에서 ‘둘러보기’ 메뉴를 클릭하여 사이트를 새로 고치고 검색 조건을 초기화한 후 검색 폼 입력 필드(s)에 다음 목록의 키워드를 순서대로 입력하여 결과를 확인하는데, 그다음 목록의 순서로 각각 결과를 확인해보세요. 예제 구성에 꼭 필요한 내용이 아니므로 실행하지 않아도 됩니다.

  • 멕시코
  • <p>멕시코</p>
  • <script>alert(‘XSS’);</script>
  • 입력 필드에 유지되는 키워드
  • QM 영역 하위 ‘Request’ 메뉴 클릭 후 Query Vars 행의 ‘s’, ‘search_terms’ 행 데이터 확인

이번에는 main-query.php 및 searchform.php 파일의 다음 코드 1번 줄을 찾아 한 번은 2번 줄로 변경하고 저장 후 위의 과정을 진행, 또 3번 줄로 변경하여 위의 과정을 다시 진행해보세요.

$search_term = trim( wp_filter_nohtml_kses( get_query_var( 's' ) ) );
$search_term = sanitize_text_field( get_query_var( 's' ) );
$search_term = get_query_var( 's' );

결과는 따로 안내하지 않지만, 태그가 제거되는가 아닌가의 차이를 가장 쉽게 확인할 수 있으며, 2번 줄의 경우는 특정 태그를 입력 필드에 다시 출력하지도 않을 것입니다. 예제의 검색 폼에는 2번 줄을 사용해도 전혀 문제가 없습니다.

분류 데이터는 데이터베이스에서 가져오지만, URL 쿼리 스트링으로 브라우저에 노출되는 경우 직접 사용자가 검색 데이터를 변경할 수 있으므로 사이트 구성에 문제가 되지 않는다면 다양한 때를 대비하는 것도 좋습니다.

예제가 보안에 완벽하다고 절대 확신할 수 없으며, 취약한 부분이 존재할 것임은 명백합니다. 물론, 워드프레스의 함수와 코어가 보안에 취약하다는 뜻은 아닙니다. 테마 편집으로 직접 사이트를 구성할 때 해당 부분에 관한 관심과 개선은 지속해야 한다는 뜻으로 생각하면 됩니다.

검색 데이터 출력

다음 그림은 이번 과정을 완료한 후 특정 검색 기준으로 예제의 검색 폼 필드를 모두 사용하여 검색한 결과 페이지입니다.

모든 검색 조건 사용 결과

그림에서 이미지 위에 출력된 내용은 검색 데이터와 결과 포스트 개수입니다. 검색 데이터 출력은 지금까지 사용한 함수와 변수로 쉽게 구현할 수 있으므로 해당 코드 제공으로 간단하게 처리합니다.

다음 코드는 편집기에 열린 search.php 파일 15번 줄에서 18번 줄의 코드입니다.

<h1 class="page-title"><?php
    /* translators: %s: search query. */
    printf( esc_html__( 'Search Results for: %s', 'pics_press' ), '<span>' . get_search_query() . '</span>' );
?></h1>

편집기에서 위의 코드를 찾아 다음 코드로 대체하고 저장합니다.

<div class="page-title">
<?php
    $query_vars = $wp_query ->query_vars;
    if ( $query_vars['s'] ) echo '<span class="qvar">' . $query_vars['s'] . '</span>';
    if ( $query_vars['mediacat'] ) echo '<span class="qvar">' . $query_vars['mediacat'] . '</span>';
    if ( $query_vars['photocat'] ) echo '<span class="qvar">' . $query_vars['photocat'] . '</span>';
    if ( $query_vars['pic_axis'] ) echo '<span class="qvar">' . $query_vars['pic_axis'] . '가 긴</span>';
    if ( $query_vars['pic_width'] ) echo '<span class="qvar">가로가 ' . $query_vars['pic_width'] . 'px 보다 큰</span>';
    if ( $query_vars['pic_height'] ) echo '<span class="qvar">세로가 ' . $query_vars['pic_height'] . 'px 보다 큰</span>';
    echo '<span class="q_count">' . number_format_i18n( $wp_query->found_posts ) . '</span>';
?>
</div>

다음 1번 줄 코드는 위의 3번 줄 코드의 일부, 다음 2번 줄은 위의 10번 줄 코드의 일부입니다.

$wp_query->query_vars;
$wp_query->found_posts;

쿼리 변수와 값을 포함한 배열 데이터 query_vars, 쿼리에 의한 포스트의 총 개수 found_posts는 워드프레스 WP_Query 클래스의 프로퍼티입니다. WP_Query 클래스는 $wp_query 메인 쿼리 변수에 방대한 정보를 할당하므로 위의 코드처럼 해당 변수를 사용하여 원하는 데이터를 얻을 수 있습니다.

워드프레스 쿼리는 관련한 일정 지식 습득이 필요하고, 사용 경험과 워드프레스 루프(The Loop)에 관한 기본의 정보도 필요하지만, 이런 내용은 직접 많은 예제나 예시를 통해 경험을 축적하는 것이 최선의 방법입니다. 기초 지식을 습득하고 예제에 접근할 때보다 반대의 경우로 워드프레스 내부에서 처리되는 일련의 과정을 조금씩 이해하는 방식이 오히려 효율적인 때가 많습니다.

예제의 검색 기능 확인

다음은 이번 장에서 변경한 파일을 받을 수 있는 링크입니다. 먼저, 편집기에 열린 모든 파일을 닫고, 다운로드 후 압축을 풀어 테마 루트에 덮어쓰세요.

그 후 다음 조건으로 검색 폼에서 검색하여 결과를 확인해보세요.

  • 키워드: 소녀
  • 미디어 종류: 사진
  • 카테고리: 사람
  • 방향: 가로
  • 크기: 4000 x 3600

위의 조건으로 검색했을 때 다음 이미지 포스트 하나만 출력된다면 예제의 검색 기능을 올바르게 구성한 것이며, 예제의 검색 기능 구현은 끝입니다.

운기조식

사이트에서 더 다양한 검색 조건과 상황을 조합하여 지금까지 구성한 예제 사이트의 검색 기능과 결과를 확인해보세요.

예제 목차

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. 워드프레스 커스텀 검색 – 쿼리 변수

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

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

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

31. 워드프레스 WP_Query

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

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

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

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

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

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

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

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

40. 워드프레스 Transient API

41. 워드프레스 분류의 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 인증과 제한 및 제어

이메일로 소식 받기

이메일로 새로운 포스트를 받으려면 아래에 이메일 주소를 입력하고, 구독합니다! 버튼을 클릭하세요. 그리고 입력한 이메일 계정에 접속하여 구독 확인 메일 내용을 확인하세요.