WP Rocket 다국어 캐시: 임시글 저장 때 번역판 캐시가 사라지는 원인과 해결

WP Rocket 다국어 캐시는 TranslatePress 번역판 주소마다 언어 폴더 아래에 따로 저장되는 페이지 캐시입니다. Nginx + Apache 구성의 워드프레스 서버에서 임시글을 저장할 때마다 번역판 캐시가 언어 폴더째 사라지는 원인을 WP Rocket 코드에서 추적한 결과, 발행되지 않은 글은 번역판 캐시 삭제에서 제외하고 직접 지운 주소는 프리로드에 다시 알리는 코드로 해결하게 되었습니다.
같은 서버 안에서 잰 첫 바이트는 번역판 캐시가 있을 때 0.015초, 없을 때 2.492초였습니다. 언어 폴더가 비면 번역판 방문자는 누군가 그 주소를 다시 열어 캐시가 만들어질 때까지 이 느린 응답을 받게 됩니다.
1️⃣ WP Rocket 다국어 캐시 구조: 언어별 폴더와 번역판 지우기
번역판은 원문 글을 번역해 보여 주는 화면이라 캐시도 원문과 한 묶음일 것으로 생각하기 쉽지만, WP Rocket은 번역판 주소를 별개의 페이지로 캐시합니다. 캐시 폴더 아래 원문 글 폴더와 나란히 [en]·[ja] 같은 언어 폴더가 생기고, 번역판 글은 그 안에 번역 슬러그 이름으로 저장됩니다.
글을 수정하면 WP Rocket의 TranslatePress 연동이 그 글의 번역판 캐시도 함께 삭제합니다. 공식 문서도 일반 수정은 모든 번역판을, TranslatePress 번역 편집기에서 고친 경우는 바뀐 언어만 지운다고 안내하고 있습니다.
// wp-rocket/inc/ThirdParty/Plugins/I18n/TranslatePress.php (3.23.5.1)
'post_updated' => 'clear_post_languages', // 글이 저장될 때마다 실행
public function clear_post_languages( $post_id ) {
// …
$default_permalink = get_permalink( $post_id ); // 글 상태는 확인하지 않음
foreach ( $trp_settings['translation-languages'] as $language ) {
if ( $language === $trp_settings['default-language'] ) {
continue;
}
$clear_urls[] = $converter->get_url_for_language( $language, $default_permalink, '' );
}
// …
rocket_clean_files( $clear_urls );
}연동 함수는 글 주소를 언어마다 번역판 주소로 변환한 뒤 캐시 파일 정리 함수 [rocket_clean_files]로 넘깁니다. 이 과정에 글 상태를 확인하는 줄이 없어, 발행된 글이든 임시글이든 같은 순서로 번역판 주소를 만들어 삭제하게 됩니다.
2️⃣ 임시글 저장: 언어 폴더 전체 삭제
임시글 저장은 번역판 캐시와 무관하지 않습니다. 발행 전 글의 고유주소는 슬러그가 아니라 [?p=글번호] 꼴이며, TranslatePress가 이 주소를 [/en/?p=글번호]처럼 언어 경로를 붙인 주소로 변환하기 때문입니다.
[rocket_clean_files]는 주소에서 쿼리를 제외하고 경로만으로 캐시 위치를 찾습니다. 남은 경로가 [/en/]이면 그 언어의 캐시 폴더 전체가 되고, 대상이 폴더이면 하위 폴더까지 재귀적으로 삭제합니다.
// wp-rocket/inc/functions/files.php (3.23.5.1) — rocket_clean_files() 안
$parsed_url = get_rocket_parse_url( $url ); // 경로와 쿼리를 나눔
// …
$entry = $dir . $parsed_url['path']; // …/example.com/en/
// …
if ( $filesystem->is_dir( $entry ) ) {
rocket_rrmdir( $entry, [], $filesystem ); // 폴더째 재귀 삭제
} else {
$filesystem->delete( $entry );
}메인 페이지도 같은 경로를 거칩니다. 메인 주소 [/]가 [/en/]으로 변환되므로, 발행된 메인 페이지를 저장해도 언어 폴더 전체가 삭제됩니다. 임시글 제목만 고쳐 다시 저장해도 네 언어의 캐시 폴더가 모두 비었고, 임시글 저장이 몰린 하룻밤 동안 같은 삭제가 30번 넘게 반복된 것을 저장 시각과 캐시 삭제 기록으로 확인했습니다.
코드상 편집기 자동 저장도 같은 경로입니다. 발행 글을 편집하는 동안 저장되는 리비전 역시 [?p=] 꼴 주소를 사용합니다.
Nginx + Apache 구성의 테스트 사이트에서 발행 글을 편집기로 고치며 언어 폴더 파일 수를 확인했습니다. 첫 자동 저장에서는 영어·일본어 폴더가 각각 20개 그대로였고, 1분 뒤 두 번째 자동 저장에서 두 폴더 모두 0개가 되었습니다.
첫 자동 저장은 리비전을 새로 만들고, 두 번째부터는 같은 리비전을 고쳐 저장하므로 [post_updated]가 실행됩니다. 첫 자동 저장 뒤에도 본문을 계속 고치면, 저장 버튼을 누르지 않아도 번역판 캐시가 언어 폴더째 삭제됩니다.
WP Rocket 공식 저장소에도 같은 증상이 2023년 8월 31일부터 이슈 #6130으로 열려 있습니다. 고치는 풀 리퀘스트 #7616은 2025년 9월에 올라왔지만 합쳐지지 않았고, 2026년 10월 2일에 나온 3.23.5.1 변경 기록에도 수정 내용은 없습니다.
3️⃣ 해결 코드: 발행 글만 번역판 캐시 삭제
공식 수정이 나오기 전까지는 연동 함수를 떼어 내고, 주소가 확실한 경우에만 번역판을 지우는 함수로 대신하게 됩니다. 이슈 댓글에 올라온 우회 방법도 발행되지 않은 글이면 연동 함수에서 바로 빠져나가는 같은 방향입니다.
다만 글 상태만 확인하는 조건으로는 두 경우가 남습니다. 메인 페이지는 발행 상태여도 저장할 때 언어 폴더 전체를 지우고, 발행 글을 임시글로 내리면 번역판 캐시가 삭제되지 않아 내린 글이 번역판 주소에서 계속 열립니다.
<?php
/**
* Plugin Name: Rocket TranslatePress Draft Fix
* Description: WP Rocket TranslatePress 연동의 번역판 캐시 삭제를 주소가 확실한 경우로 제한
*/
// 1. WP Rocket 연동 함수를 post_updated 에서 떼어 내고 보관
add_action( 'init', function () {
global $wp_filter;
if ( empty( $wp_filter['post_updated'] ) ) {
return;
}
foreach ( $wp_filter['post_updated']->callbacks as $priority => $callbacks ) {
foreach ( $callbacks as $cb ) {
$fn = $cb['function'];
if ( is_array( $fn ) && is_object( $fn[0] )
&& 0 === strpos( get_class( $fn[0] ), 'WP_Rocket\\' )
&& false !== stripos( get_class( $fn[0] ), 'TranslatePress' ) ) {
remove_action( 'post_updated', $fn, $priority );
$GLOBALS['rtdf_original'] = $fn;
}
}
}
}, 9999 );
// 2. 대신할 함수
add_action( 'post_updated', function ( $post_id, $after, $before ) {
if ( empty( $GLOBALS['rtdf_original'] ) || ! class_exists( 'TRP_Translate_Press' ) ) {
return;
}
$trp = TRP_Translate_Press::get_trp_instance();
$converter = $trp->get_component( 'url_converter' );
$settings = $trp->get_component( 'settings' )->get_settings();
$languages = array_diff( $settings['translation-languages'], [ $settings['default-language'] ] );
// 메인 페이지 — 언어 폴더가 아니라 언어 홈 캐시 파일만
if ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === (int) $post_id ) {
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$home = $converter->get_url_for_language( $lang, home_url( '/' ), '' );
$dir = WP_ROCKET_CACHE_PATH . wp_parse_url( $home, PHP_URL_HOST ) . wp_parse_url( $home, PHP_URL_PATH );
array_map( 'unlink', glob( $dir . 'index*.html*' ) ?: [] );
$urls[] = $home;
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
do_action( 'rocket_after_clean_terms', $urls ); // 프리로드에 알림(5️⃣)
return;
}
// 발행 글 저장 — WP Rocket 원래 함수 그대로
if ( 'publish' === $after->post_status ) {
call_user_func( $GLOBALS['rtdf_original'], $post_id );
return;
}
// 발행 → 임시글·비공개·휴지통 — 내리기 전 주소의 번역판만
if ( 'publish' === $before->post_status ) {
$old = get_permalink( $before );
$urls = [];
add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
foreach ( $languages as $lang ) {
$urls[] = $converter->get_url_for_language( $lang, $old, '' );
}
remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' );
rocket_clean_files( $urls );
}
// 그 밖(임시글·리비전·자동 저장)은 번역판 주소가 없으므로 삭제하지 않음
}, 10, 3 );첫 부분은 WP Rocket이 등록한 연동 함수를 찾아 [post_updated]에서 떼어 내고, 원래 함수는 보관해 둡니다. 대신할 함수는 메인 페이지면 언어 홈의 캐시 파일만 지우고, 발행 글이면 보관한 원래 함수를 그대로 실행하며, 발행 글을 내린 경우에는 내리기 전 주소로 번역판을 찾아 삭제합니다.
[wp-content/mu-plugins/] 폴더에 PHP 파일로 저장하면 활성화 없이 바로 적용되고, 파일을 지우면 WP Rocket 원래 동작으로 돌아갑니다. 같은 동작의 코드로 시험한 결과, 임시글을 다시 저장해도 언어 폴더의 파일 수가 그대로였고 발행 글을 다시 저장하면 그 글의 원문과 번역판 폴더만 지워졌습니다.
위 코드를 넣기 전과 넣은 뒤, 같은 환경에서 네 경우의 언어 폴더 파일 수를 비교했습니다. 영어·일본어 폴더에는 언어 홈과 글 네 편의 PC·모바일 캐시가 각각 20개 있었습니다.
코드를 넣기 전에는 임시글 저장, 메인 페이지 저장, 발행 취소 세 경우 모두 두 폴더가 0개가 되었고, 발행 글 저장만 그 글의 번역판 캐시를 삭제했습니다.
코드를 넣은 뒤에는 임시글 저장과 편집기 자동 저장에서 20개가 그대로 남았습니다. 메인 페이지 저장은 언어 홈 캐시 4개, 발행 글 저장과 발행 취소는 그 글의 번역판 4개만 삭제되어 16개가 남았고, 내린 글의 영어 번역판 주소는 404를 응답했습니다.
| 경우 | 고치기 전 | 코드를 넣은 뒤 | 넣은 뒤 삭제된 캐시 |
|---|---|---|---|
| 임시글 저장 | 20 → 0 | 20 → 20 | 없음 |
| 발행 글 저장 | 그 글 번역판만 삭제 | 20 → 16 | 그 글 번역판 4개 |
| 메인 페이지 저장 | 20 → 0 | 20 → 16 | 언어 홈 캐시 4개 |
| 발행 취소 | 20 → 0 | 20 → 16 | 그 글 번역판 4개 · 번역판 주소 404 |
4️⃣ 프리로드 재생성 누락: 대기열에 남은 완료 상태
임시글 저장을 막아도, WP Rocket 함수의 삭제 신호 없이 지운 캐시 파일은 프리로드가 다시 채우지 않습니다. WP Rocket 프리로드는 주소마다 상태를 기록하는 대기열 표를 사용하며, 요청할 주소는 대기, 요청을 보낸 주소는 진행 중, 캐시가 만들어진 주소는 완료로 바뀌고 3분 안에 끝나지 않으면 실패가 됩니다.
완료된 주소를 다시 대기로 돌리는 경우는 캐시 수명이 끝나 지워진 파일, WP Rocket이 지우면서 삭제 신호를 낸 주소, 실패한 지 12시간이 지난 주소 세 가지입니다. [rocket_clean_files]에 셋째 인자를 false로 넘기거나 파일을 직접 삭제하면 이 신호가 나지 않아, 대기열에는 완료로 남은 채 캐시 파일만 사라집니다.
셋째 인자는 삭제 신호를 받아 다른 캐시를 이어서 지우는 코드가 되풀이 호출을 막으려고 false로 넘기게 됩니다. 번역판 캐시를 대조한 시점에 영어 번역판 65개 중 3개, 일본어 번역판 65개 중 7개가 대기열에는 완료로 남은 채 캐시 파일만 없었습니다.
WP Rocket 지원팀 기여자도 이슈 #6130 댓글에서 캐시를 지운 뒤 표의 주소가 완료로 남아 상태가 어긋난다고 인정했습니다. 표와 캐시 파일을 주기적으로 맞추자는 제안(#2939)은 2020년에 구현되지 않은 채 닫혔습니다.
5️⃣ 프리로드 재등록: 직접 지운 주소를 대기열에 알리기
직접 지운 주소는 지운 직후 프리로드에 알리는 한 줄로 해결합니다. 프리로드는 [rocket_after_clean_terms] 신호를 받으면 넘겨받은 주소를 대기 상태로 되돌리고, 표에 없는 주소는 새로 추가합니다.
// 삭제 신호 없이 지운 뒤 — 지운 주소 배열을 프리로드 대기열에 다시 등록
rocket_clean_files( $urls, null, false ); // 셋째 인자 false = 삭제 신호 없음
do_action( 'rocket_after_clean_terms', $urls ); // 프리로드가 이 주소들을 대기(pending)로WP Rocket 안에서 이 신호를 받는 곳은 프리로드 대기열 함수와 SpinUpWP 호스팅 연동뿐이며, 프리로드 함수는 대기열 행만 고치고 WP Rocket 캐시 파일을 지우거나 삭제 신호를 다시 내지 않아 되풀이 호출이 생기지 않습니다. 캐시를 WP Rocket 함수로만 지우고 셋째 인자를 기본값으로 두는 사이트는 신호가 이미 나므로 이 줄이 필요하지 않습니다.
이 줄을 넣은 뒤 번역판 네 언어가 있는 글 하나의 캐시를 지우자 2초 안에 대기열 행 넷이 대기로 바뀌었습니다. 크론을 1분마다 실행하는 서버에서 두 번 시험해, 101초와 153초 뒤 번역판 PC·모바일 캐시 파일 8개가 모두 다시 만들어졌습니다. 확인은 대기열 표의 상태와 캐시 폴더 파일로 합니다.
# 대기열 표에서 그 주소의 상태 (표 이름 앞 wp_ 는 사이트 접두어)
wp db query <<'SQL'
SELECT url, status, modified FROM wp_wpr_rocket_cache WHERE url LIKE '%/en/sample-post-en'
SQLurl status modified
https://example.com/en/sample-post-en pending 2026-10-17 09:10:48# 1분쯤 뒤 캐시 파일이 다시 생겼는지
ls -l wp-content/cache/wp-rocket/example.com/en/sample-post-en/total 240
-rw-r--r-- 1 example nobody 95084 Oct 17 18:12 index-https.html
-rw-r--r-- 1 example nobody 22352 Oct 17 18:12 index-https.html_gzip
-rw-r--r-- 1 example nobody 95083 Oct 17 18:12 index-mobile-https.html
-rw-r--r-- 1 example nobody 22348 Oct 17 18:12 index-mobile-https.html_gzip지운 직후에는 상태가 [pending]으로 바뀌고, 프리로드가 요청을 보낸 뒤 캐시 파일이 생기면 [completed]가 됩니다. 상태가 [completed]인데 캐시 파일이 없다면 그 주소는 신호 없이 지워진 것입니다.
메인 페이지를 저장해 위 코드가 언어 홈 캐시 파일을 지우고 신호를 내자, 영어·일본어 홈 주소가 대기열에 다시 올라갔습니다. 언어 홈의 PC·모바일 캐시는 늦어도 두 번째 크론 실행에서 다시 만들어졌습니다.
두 번 시험해 저장부터 마지막 캐시 파일까지 47초와 91초가 소요되었습니다.
🔢 FAQ & 추천 콘텐츠








ℹ️ 제휴 안내
본 사이트의 콘텐츠에는 제휴 링크가 포함되어 있습니다. 방문자가 이 링크를 통해 상품 또는 서비스를 구매하면 본 사이트는 판매처로부터 수수료를 지급받습니다. 이 과정에서 구매자가 지불하는 금액(이벤트 할인 시 금액이 내려갑니다↓)은 오르지 않습니다. 게시된 가격·할인·재고 정보는 작성 시점 기준이며 실제와 다를 수 있으므로, 구매 전 판매처에서 최종 확인하시기 바랍니다. 상품 선정과 평가는 자체 기준에 따라 작성되며, 수수료 지급 여부가 소개 순서나 평가 내용에 영향을 주지 않습니다.






