[워드프레스] 14. 쿼리 : WP_Query와 루프 완전 분해

워드프레스 해부 14편 - WP_Query와 루프 완전 분해

작성자

카테고리:

* 이 게시물은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

(워드프레스 해부 ⑭) 쿼리

페이지마다 “어떤 글을 보여줄까”는 WP_Query가 정한다. 홈이면 최근 글, 카테고리 페이지면 그 카테고리 글, 단일 글이면 그 글 하나. URL을 보고 워드프레스가 자동으로 만드는 이걸 메인 쿼리라고 한다.

그리고 그 결과를 화면에 뿌리는 게 루프(the Loop)다. 워드프레스 테마 어디서나 보이는 이 패턴:

if ( have_posts() ) :
    while ( have_posts() ) : the_post();
        the_title( '<h2>', '</h2>' );
        the_content();
    endwhile;
endif;

have_posts()로 남은 글이 있는지 보고, the_post()로 다음 글을 “현재 글”로 세팅한 뒤, the_title()·the_content()로 출력한다. 이 세 함수가 루프의 전부다.

메인 쿼리 말고 내가 원하는 조건의 글을 따로 뽑고 싶으면 new WP_Query()로 커스텀 쿼리를 만든다. 단 주의: 커스텀 루프 뒤엔 wp_reset_postdata()를 불러 “현재 글”을 원래대로 되돌려야 한다. 안 그러면 뒤 코드가 엉뚱한 글을 참조한다.

직접 해보기

// 최근 글 5개를 조건 지정해 직접 뽑기
$q = new WP_Query( array(
    'post_type'      => 'post',
    'posts_per_page' => 5,
    'orderby'        => 'date',
    'order'          => 'DESC',
) );
while ( $q->have_posts() ) : $q->the_post();
    echo '<li>' . get_the_title() . '</li>';
endwhile;
wp_reset_postdata();   // 꼭! 현재 글 복원

✅ 확인 — 커스텀 WP_Query로 최근 글 5개의 제목을 출력해 보자. 5개가 최신순으로 나오면 성공. 참고로 메인 쿼리를 바꾸고 싶을 땐 new WP_Query가 아니라 pre_get_posts 훅을 써야 한다 — 새 쿼리를 또 돌리면 그만큼 DB를 두 번 때리니까.

메인 쿼리 vs 커스텀 쿼리

‘이 페이지에 인기글 5개를 더’ 같은 추가 목록의 완성형은 이렇다.

$q = new WP_Query( array( 'posts_per_page' => 5, 'orderby' => 'comment_count' ) );
if ( $q->have_posts() ) :
    while ( $q->have_posts() ) : $q->the_post();
        the_title( '<h3>', '</h3>' );
    endwhile;
endif;
wp_reset_postdata();   // 반드시!

메인 쿼리를 바꾸려면 — pre_get_posts

✅확인에서 예고한 그 얘기다. 메인 쿼리 자체를 바꾸려면 pre_get_posts 훅에서 $query->set()으로 조건을 수정한다. is_main_query()로 메인만 골라 건드리는 게 안전하다.

add_action( 'pre_get_posts', function ( $q ) {
    if ( ! is_admin() && $q->is_main_query() && $q->is_home() ) {
        $q->set( 'posts_per_page', 6 );
    }
} );

자주 쓰는 인자들

인자뜻
post_type글 종류(post·page·CPT)
posts_per_page개수(-1=전부 — 주의)
orderby / order정렬 기준·방향
category_name / tax_query분류 필터
meta_query커스텀 필드 필터
paged페이지 번호(페이징)

분류·메타 필터 실전 — tax_query

표의 인자 중 실전에서 제일 헷갈리는 게 tax_query다. 배열 속 배열 구조라서인데, 동작하는 예 하나면 감이 온다 — “‘자바’ 카테고리이면서 ‘초급’ 난이도인 글”:

$q = new WP_Query( array(
    'tax_query' => array(
        'relation' => 'AND',                    // 두 조건 모두
        array( 'taxonomy' => 'category',
               'field'    => 'slug',
               'terms'    => 'java' ),
        array( 'taxonomy' => 'difficulty',
               'field'    => 'slug',
               'terms'    => array( 'beginner' ) ),
    ),
) );

relation을 OR로 바꾸면 ‘둘 중 하나’가 된다. 내부적으로는 12편에서 본 세 테이블을 조인하는 SQL로 번역된다 — 조건이 늘수록 조인이 늘어 무거워지니, 얼마나 무거운지는 Query Monitor로 전후 쿼리 수·시간을 직접 재보는 게 정확하다.

성능 함정 — posts_per_page=-1

posts_per_page => -1은 글이 수천 개면 전부 불러와 메모리를 터뜨린다. 필요한 만큼만 페이징하자. 또 페이징을 안 쓰는 목록이라면 'no_found_rows' => true로 불필요한 카운트 쿼리를 꺼서 속도를 아낀다. 쿼리 수와 소요 시간은 Query Monitor(32편)로 확인하는 습관을 들이면, 느린 페이지의 범인을 금방 찾는다.

무슨 글인지 정해졌으면, 다음은 “그걸 어떤 파일로 그릴까” — 템플릿 계층의 차례다.

자주 묻는 질문 (FAQ)

WP_Query와 the loop는 뭐가 다른가요?

WP_Query가 조건에 맞는 글을 ‘조회’하고, the loop는 그 결과를 하나씩 ‘출력’합니다.

커스텀 쿼리 후 왜 wp_reset_postdata를 부르나요?

전역 $post를 원래대로 돌려놓기 위해서입니다. 안 하면 이후 루프가 엉뚱한 글을 가리킬 수 있습니다.

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다