워드프레스의 검색 시스템 이해하기
워드프레스의 검색 시스템을 활용하여 망보드의 모든 게시판의 제목을 검색하는 기능을 구현하다보면 워드프레스의 이해도를 높일 수 있습니다.
워드프레스는 query 기반으로 작동하게 되는데요.
$wp, $wp_query, $wp_query->query_vars 중요한 object 와 paramter 가 있습니다.
모든 게시판이던 파라미터 전달을 쫓아가면서 디버깅을 하고 구조를 파악하게 되는데, 워드프레스 역시 파라미터를 던지고 받는 부분이 있고, 이것을 query_vars 라고 합니다.
일반적으로 p 는 post_id 를 s 는 search parameter 를 넘기는 변수입니다.
한편 워드프레스에 s=aaa 로 파라미터 값이 넘어가게 되면 내부적으로는 themes/search.php 파일을 열게 합니다.
일반적인 search.php 코드들을 보면 대충 이런 패턴으로 제작되어 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<?php get_header(); ?> <section id="content"> <div class="container"> <div class="row"> <div id="main" class="col-sm-8 col-md-9"> <!-- <div class="travelo-box"> <h3><?php echo __( 'New Search', 'trav' );?></h3> <p><?php echo __( 'If you are not happy with the results below please do another search.', 'trav' ) ?></p> <div class="row"> <div class="col-md-5"> <?php get_search_form(); ?> </div> </div> </div> --> <div class="page"> <div class="post-content"> <?php if ( have_posts() ): ?> <div class="blog-infinite"> <?php while(have_posts()): the_post(); trav_get_template( 'loop-blog.php', '/templates' ); endwhile; ?> </div> <?php global $ajax_paging; if ( ! empty( $ajax_paging ) ) { next_posts_link( __( 'LOAD MORE POSTS', 'trav' ) ); } else { echo paginate_links( array( 'type' => 'list' ) ); } ?> <?php else: ?> <div class="travelo-box"> <h2><?php echo __( "Nothing Found", 'trav'); ?></h2> <p><?php echo __( "Sorry, no posts matched your criteria. Please try another search.", 'trav' ); ?><br /><?php echo __( "You might want to consider some of our suggestions to get better results:", 'trav' ); ?></p> <ul class="triangle"> <li><?php echo __( "Check your spelling.", 'trav' ); ?></li> <li><?php echo __( "Try a similar keyword.", 'trav' ); ?></li> <li><?php echo __( "Try using more than one keyword.", 'trav' ); ?></li> <li><?php echo __( "See frequently asked questions.", 'trav' ); ?></li> <li><?php echo __( "Contact the support center.", 'trav' ); ?></li> </ul> </div> <?php endif; ?> </div> </div> </div> <div class="sidebar col-sm-4 col-md-3"> <?php dynamic_sidebar( 'sidebar-post' ); ?> </div> </div> </div> </section> <?php get_footer(); |
이 예제는 Travelo 라는 여행 관련 테마의 search.php 를 예시한 것이고요.
코드 안에 보시면
1 2 3 |
while(have_posts()): the_post(); trav_get_template( 'loop-blog.php', '/templates' ); endwhile; |
라는 부분이 보이실 것입니다.
이 부분이 검색 결과를 담아서 루프 돌리는 부분인데, s 값을 받아서 모든 mb_$board_id 테이블을 query 날리고 그 이차원 배열을 저걸로 교체해야 된다고 생각할 수 있습니다.
하지만 그러기에는 수정해야할 부분이 너무 많습니다.
우선 템플릿 안에 loop-blog.php 에 있는 변수 부분들을 모조리 교체해야 합니다.
그 다음에는 파라미터 s 가 넘어오는 바람에 자동으로 생긴 메타값들 ( wp_posts 에서 잡힌 검색결과 갯수라던지 심지어 wp_posts query 자체를 disable 한다던지 ) 을 핸들링 힘든 일들이 동반되게 됩니다.
여기서 마법을 보여줄 수 있는 것이 wpDataTables 입니다.
코드 라인은 단 한 줄.
1 |
<?php echo do_shortcode('[wpdatatable id=1]'); ?> |
어떻게 이 syntax 하나면 모든 힘든 작업을 다 바이패스하고 그럴 듯한 뷰를 만들어주는 걸까요 ?
비밀은 wpdatatable 은 사용자가 날리는 parameter 를 받아서 db 속에 넣은 다음 그 결과를 서버사이드로 리턴해준다는 데에 답이 있습니다.
이제 구성은 대충 파악했으니, data source 를 어떤게 구성하느냐가 관건이겠네요.
다행히도 망보드의 컨텐츠는 아주 심플하게 비슷한 구성을 가지고 있어서, 서로 다른 테이블을 한 라인으로 union all 하는 것이 핵심입니다.
union all 은 횡을 종으로 바꿔치기하는 마법 명령어!
통상적으로 row 를 길게 가진 테이블을 압축하기는 쉬운 편입니다.
sum, max, concat, group_concat 같은 aggregate 함수들을 쓰면 되니깐요.
반대로 서로 다른 테이블로 분리되어 있는 것을 하나로 연결할려면 union all 을 사용하면 됩니다.
망보드 통합 검색 구현에 왜 union all 이야기를 하느냐 라면.. 망보드 테이블이 여러개로 나뉘어져 있어서 통합 검색은 검색할 대상을 하나의 query 에 넣어야 가능하기 때문이지요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
CREATE VIEW all_mb_board AS select a.pid AS pid,a.title AS title,a.reg_date AS reg_date,a.board AS board,b.board_name AS board_name,b.description AS description,b.post_id AS post_id from (((select mb_a001.pid AS pid,mb_a001.title AS title,mb_a001.reg_date AS reg_date,'a001' AS board from mb_a001) union all select mb_a002.pid AS pid,mb_a002.title AS title,mb_a002.reg_date AS reg_date,'a002' AS board from mb_a002 union all select mb_a003.pid AS pid,mb_a003.title AS title,mb_a003.reg_date AS reg_date,'a003' AS board from mb_a003 union all select mb_b001.pid AS pid,mb_b001.title AS title,mb_b001.reg_date AS reg_date,'b001' AS board from mb_b001 union all select mb_b002.pid AS pid,mb_b002.title AS title,mb_b002.reg_date AS reg_date,'b002' AS board from mb_b002 union all select mb_b003.pid AS pid,mb_b003.title AS title,mb_b003.reg_date AS reg_date,'b003' AS board from mb_b003 union all select mb_b004.pid AS pid,mb_b004.title AS title,mb_b004.reg_date AS reg_date,'b004' AS board from mb_b004 union all select mb_b005.pid AS pid,mb_b005.title AS title,mb_b005.reg_date AS reg_date,'b005' AS board from mb_b005 union all select mb_b006.pid AS pid,mb_b006.title AS title,mb_b006.reg_date AS reg_date,'b006' AS board from mb_b006 union all select mb_b007.pid AS pid,mb_b007.title AS title,mb_b007.reg_date AS reg_date,'b007' AS board from mb_b007 union all select mb_b008.pid AS pid,mb_b008.title AS title,mb_b008.reg_date AS reg_date,'b008' AS board from mb_b008 union all select mb_c001.pid AS pid,mb_c001.title AS title,mb_c001.reg_date AS reg_date,'c001' AS board from mb_c001 union all select mb_c002.pid AS pid,mb_c002.title AS title,mb_c002.reg_date AS reg_date,'c002' AS board from mb_c002) a join mb_boards b on((a.board = b.board_name))) order by a.reg_date desc; |
이렇게 view 를 만든 다음에는 검색 query 가 여기 전달만 되면 검색이 실행될 것입니다.
이 작업은 wpDataTables 에서 기본적으로 설정되는 var1 var2 var3 ( 총 3가지 파라미터를 전달할 수 있다고 합니다) 값을 던져서 할 수 있습니다.
wpDataTables 설정하기
이제 wdt_column_filter[0] 에 사람들이 query 한 결과값을 전달하는 부분을 설정하는 부분이네요.
두 가지 옵션이 있는데요.
한 가지는 서버사이드에서는 모든 결과를 검색해서 보여주되, 검색창에 검색을 하면 마치 처음 랜더링 될 때 querystring 부분이 검색창에 입력되어 검색된 효과 처럼 보여지는 jquery 방식이 있을테고,
다른 한가지는 서버사이드에서 검색어가 적용되어서 처음부터 검색키워드가 아닌 다른 검색어는 볼 수 없게 하는 방식이 있습니다.
첫 번째 방식을 구현할 때는 단순히 querystring 에 wdt_column_filter[0]=value 만 넣어주면 구현이 끝납니다.
두 번째 방식을 구현할 때는 소스상에서 add_query_vars 를 해서 변수를 받아주거나 s p 같은 이미 전달받을 수 있는 인수로 값을 받은 다음에 mysql query 부분의 where 절에 title = ‘%var1%’ 이런식으로 넣어주면 됩니다
위에 보시면 query 부분에서 pid 값을 concat 함수를 이용해서 조금 다듬어 준 부분이 있습니다.
이렇게 세팅하고 apply 하면 하단에 preview 부분에서 listing 이 보여집니다.
다음으로 display 화면 옵션을 선택할 수 있는데요. 상단에 있는 부분은 테이블 전체의 환경 설정이고, 하단의 부분은 각 필드별로 항목을 어떤 식으로, 무엇을 먼저 보여줄 것인지, 헤더의 제목을 바꾸거나 width 를 지정하거나 심지어 특정한 값을 어떤게 치환하거나 각 필드나 값에 따라 이팩트 같은걸 줄 수 있습니다.
또 놀라운 것은 한 colume 의 value 가 다른 wpDataTables ID 값을 연결하는 foreign key 로써의 역할을 하게끔 설계할 수도 있게 되어 있습니다.
display header 는 디폴터 값은 mysql query 인 경우 field , json object 인 경우 object 의 key 값이 됩니다만 사용자페이지용으로 헤더의 제목을 바꿀 수 있게 되어 있습니다.
column position 은 0/1/2/3.. 식으로 순서를 바꿀 수 있게 되어 있고요. 만약 다른 column 을 visible on front end 에서 감춰버리면 포지션이 자동으로 줄어들더라고요.
검색된 데이타의 값 속성을 지정하는 것인데 이것은 마치 엑셀에서 컬럼 열의 데이타 형을 string 으로 선언해주면 뷰 부분이 깔끔하게 맞춰지는 그런 것이라고 생각하시면 됩니다.
sorting 부분은 header 에서 이 값을 sorting 가능하게 할지, 또는 처음 로딩될 때 default sorting column 으로 지정할지 정하는 부분이 있습니다.
filtering 부분은 검색 기능을 의미하는 것이고요, 이 행을 검색가능하게 할지 부분을 정하는 부분입니다. 당연히 통합검색에서는 제목부분에 대해서 이 값을 가능하게 해야 검색이 되겠죠 ?
검색값의 패턴과 유형을 미리 정해두는 부분인데, 통합검색에서는 string 만 이용하니깐 string 으로 두면 됩니다.
conditional formatting 은 상당히 재미있는 부분입니다.
이 두 가지 화면을 보여드리면 이 기능을 어떻게 사용할 수 있을지는 설명드리지 않아도 아실 것 같네요.
완성된 작품
http://marketing.wper.kr/?wdt_column_filter%5B0%5D=%EC%A0%9C%ED%92%88&s=%EC%A0%9C%ED%92%88&submit=Go
URL 링크를 타고 들어가면 볼 수 있고요.
한국인 사용자가 충분히 없어서 번역 본은 저희들이 만들어야 할 소명이 생겼답니다.
페이지 이동을 저렇게 버턴으로 밖에 구현 못할까 라는 생각이 문득 들 수 있는데요.
이것을 페이지 이동을 제목 클릭시 가능하게 하려면 mysql query 문에서 concat(‘<a href=”‘,post_id,'”>’,title,'</a>’) .. 이런식으로 concat 을 도배해서 만들면 어느정도 가능은 할 것 같네요.
아니면 concat 을 도배하거나 substring_index 를 도배하면 mysql 에서도 불가능한 것이 없으니깐요.
사실 그 정도 공을 들인다면 php 에서 json 으로 빼내고 그 json 을 데이타 소스로 쓰는 편이 정신건강에 좋긴 하지만요.