Technote

성능 최적화 실무

연재 워드프레스 성능 프로파일링 8부 중 5부

트랜지언트 — 캐시를 만들면 무효화가 내 책임이 됩니다

저장은 세 줄이면 됩니다. 어려운 것은 언제 지울 것인가이고, 지워야 할 사건은 최소 두 가지입니다. 하나만 놓쳐도 증상이 갈립니다.

트랜지언트는 내가 직접 소유하는 캐시입니다. 만료 시간을 붙여 값을 저장하고, 다음에 그대로 꺼내 씁니다. 영속 오브젝트 캐시가 있으면 Redis 로, 없으면 wp_options 로 들어갑니다.

$data = get_transient( 'acme_top' );

if ( false === $data ) {
    $data = acme_query_top_products();
    set_transient( 'acme_top', $data, HOUR_IN_SECONDS );
}

여기까지는 쉽습니다. 어려운 것은 다음 질문입니다 — 이 값을 언제 버려야 합니까? 캐시를 도입한 순간 무효화라는 새 문제를 내가 떠안은 것이고, 그 문제는 저장 코드가 아니라 운영 중에 드러납니다.

무효화해야 하는 두 사건

모든 캐시에는 최소 두 가지 무효화 사건이 있습니다. 이름을 붙여 두지 않으면 반드시 하나를 놓칩니다.

두 사건과 한 안전망 — 만료를 전략으로 쓰면 그 시간만큼 거짓말을 한다

①을 놓치면 관리자는 바꿨는데 방문자는 만료 시각까지 옛 값을 봅니다. 가격 · 재고 · 공지처럼 틀리면 곤란한 값일수록 이 시간이 길게 느껴집니다.

②를 놓치면 더 나쁩니다. 새로 배포한 코드가 옛 구조의 배열을 꺼내 읽으면서 존재하지 않는 키를 참조하고, 경고나 치명적 오류가 납니다. 로컬에서는 캐시가 비어 있어 멀쩡하고 프로덕션에서만 터지는 전형적인 형태입니다.

세대 번호 — 두 사건을 한 장치로

키가 파라미터 조합으로 여러 개라면 개별 삭제가 곧 관리 불가능해집니다. 캐시 키에 세대 번호를 넣고 그 번호만 올리면, 옛 키는 아무도 읽지 않는 채 만료로 사라집니다.

function acme_gen(): int {
    return (int) get_option( 'acme_cache_gen', 1 );
}

function acme_top(): array {
    $key  = 'acme_top_v2_' . acme_gen();          // v2 = 형태의 버전 (사건 ②)
    $data = get_transient( $key );

    if ( false === $data ) {
        $data = acme_query_top_products();
        set_transient( $key, $data, HOUR_IN_SECONDS );
    }

    return $data;
}

// 사건 ① — 원본이 바뀌면 세대를 올린다
add_action( 'save_post_product', 'acme_bump_gen' );
add_action( 'deleted_post',      'acme_bump_gen' );

function acme_bump_gen(): void {
    update_option( 'acme_cache_gen', acme_gen() + 1 );
}

키 안의 v2 가 사건 ②를 담당합니다. 저장 구조를 바꾼 배포에서 이 문자열을 올리면 옛 구조의 값은 새 코드가 아예 조회하지 않습니다. 배포 절차에 “캐시 비우기” 를 넣는 것보다 안전한데, 잊어버릴 사람에게 기대지 않기 때문입니다.

세대 번호는 작은 정수 하나이므로 모든 요청에 실려도 무해합니다. 문제가 되는 것은 큰 값이며, 그 이야기는 두 회차 뒤에 다시 나옵니다.

캐시 스탬피드와 키 길이

인기 있는 값의 캐시가 비는 순간, 동시에 들어온 요청이 전부 재생성에 들어갑니다. 무거운 계산이라면 그 순간이 가장 느립니다. 재생성 중임을 알리는 짧은 잠금 값을 함께 두거나, 만료 직전에 미리 갱신하는 방식으로 완화합니다.

또 트랜지언트 이름에는 길이 제한이 있습니다. 파라미터를 그대로 이어 붙이면 조용히 잘려 서로 다른 조건이 같은 키를 쓰게 되므로, 파라미터는 해시로 접습니다 — 'acme_top_v2_' . acme_gen() . '_' . md5( wp_json_encode( $args ) ).

캐시 설계를 다루는 다른 글은 성능 최적화 아카이브에 있고, 무효화 사고를 포함한 실제 점검 항목은 무료 도구의 사이트 진단으로도 일부 확인할 수 있습니다.

다음 회차

여기까지는 애플리케이션 안의 캐시였습니다. 완성된 HTML 을 통째로 보관하는 페이지 캐시는 자리가 다릅니다 — 다음 회차는 그것을 서버 계층에 두는 이유와, 두 겹으로 쌓았을 때 생기는 무효화 어긋남입니다.

이 주제의 다른 글

노하우 목록으로

성능 최적화 실무

마케터가 직접 확인하는 랜딩 페이지 속도

개발자에게 "빠르게 해 주세요" 라고 요청하면 무엇을 해야 할지 정해지지 않습니다. 이미지 · 서드파티 스크립트 · 폰트 · 히어로 이미지 네 가지는 마케터가 직접 확인해 항목으로 넘길 수…

마케터 5분 읽기

성능 최적화 심화

부모 테마 자산 걷어내기 — 절차와 뒤따르는 책임

쓰지 않는 스크립트를 내리는 것은 가능합니다. 다만 그 자산에 기대던 기능의 책임이 함께 넘어오므로, 무엇이 실제로 깨지는지 확인하는 절차가 필요합니다.

개발자 5분 읽기

₩270,000 · 신청하기