feat(seo): 사이트맵 안전 서빙·잡 견고화·분할 설정 + 캐시 설정 정합

봇 요청 스레드의 동기 생성을 제거하고 디스크 스트리밍 서빙으로 전환했다.
신선도가 만료되면 생성 잡만 예약하고 기존 세트(stale) 또는 503+Retry-After 로
응답해, 대용량에서 봇 요청이 OOM/타임아웃을 유발하던 경로를 끊었다.

- 잡: ShouldBeUnique(고정 uniqueId)+uniqueFor+retryUntil, timeout 300→1800, backoff
- 컨트롤러: 인덱스 stale/503 분기 + 자식 라우트(.xml/.xml.gz 동일 액션)
- 스케줄 withoutOverlapping, 관리자 재생성 큐 이관
- 설정 4키(분할 기준·압축·stale 서빙·수집기 상한) + 고급탭 Sitemap 캐시 짝
- SitemapManager 응답 메시지 다국어화

설정 저장 테스트가 실제 설정 파일(사이트명/언어 포함)을 덮어쓰던 사전결함을
TestCase 전역 격리로 차단하고, 두 탭이 같은 다국어 키를 공유해 한쪽에 틀린
한도를 안내하던 사전결함을 키 분리로 해소했다.
This commit is contained in:
HeuJung
2026-07-18 11:32:11 +09:00
parent 21f666e4b5
commit 1c99547bc1
71 changed files with 3767 additions and 2472 deletions
+5
View File
@@ -12,9 +12,14 @@ return [
// Sitemap manual regeneration
'sitemap_regenerated' => 'Sitemap regeneration completed.',
'sitemap_regenerate_dispatched' => 'Sitemap regeneration has started. It may take a while to finish.',
'sitemap_regenerate_failed' => 'Sitemap regeneration failed.',
'sitemap_disabled' => 'Sitemap generation is disabled.',
// Sitemap generation/serving
'sitemap_not_ready' => 'The sitemap is being prepared. Please try again shortly.',
'sitemap_generate_failed' => 'Sitemap generation failed: :error',
// Cache stats
'stats_period' => 'SEO cache statistics for the last :days days',
'stats_overall' => 'Overall Statistics',
+17 -6
View File
@@ -865,12 +865,16 @@ return [
'stats_cache_ttl_integer' => 'Stats cache TTL must be an integer.',
'stats_cache_ttl_min' => 'Stats cache TTL must be at least 0 seconds.',
'stats_cache_ttl_max' => 'Stats cache TTL may not be greater than 14400 seconds (4 hours).',
'seo_cache_enabled_required' => 'Please select the SEO cache setting.',
'seo_cache_enabled_boolean' => 'SEO cache must be true or false.',
'seo_cache_ttl_required' => 'Please enter the SEO cache TTL.',
'seo_cache_ttl_integer' => 'SEO cache TTL must be an integer.',
'seo_cache_ttl_min' => 'SEO cache TTL must be at least 0 seconds.',
'seo_cache_ttl_max' => 'SEO cache TTL may not be greater than 14400 seconds (4 hours).',
// Advanced tab only — the SEO tab override field (seo_cache_*) allows a different range.
'advanced_seo_cache_enabled_required' => 'Please select the SEO cache setting.',
'advanced_seo_cache_enabled_boolean' => 'SEO cache must be true or false.',
'advanced_seo_cache_ttl_required' => 'Please enter the SEO cache TTL.',
'advanced_seo_cache_ttl_integer' => 'SEO cache TTL must be an integer.',
'advanced_seo_cache_ttl_min' => 'SEO cache TTL must be at least 0 seconds.',
'advanced_seo_cache_ttl_max' => 'SEO cache TTL may not be greater than 14400 seconds (4 hours).',
'seo_sitemap_cache_ttl_integer' => 'Sitemap cache TTL must be an integer.',
'seo_sitemap_cache_ttl_min' => 'Sitemap cache TTL must be at least 3600 seconds (1 hour).',
'seo_sitemap_cache_ttl_max' => 'Sitemap cache TTL may not be greater than 604800 seconds (7 days).',
// Debug settings
'debug_mode_required' => 'Please select the debug mode setting.',
@@ -954,6 +958,13 @@ return [
'sitemap_cache_ttl_integer' => 'Sitemap cache TTL must be an integer.',
'sitemap_cache_ttl_min' => 'Sitemap cache TTL must be at least 3600 seconds (1 hour).',
'sitemap_cache_ttl_max' => 'Sitemap cache TTL may not be greater than 604800 seconds (7 days).',
'sitemap_urls_per_file_integer' => 'URLs per sitemap file must be an integer.',
'sitemap_urls_per_file_min' => 'URLs per sitemap file must be at least 1000.',
'sitemap_urls_per_file_max' => 'URLs per sitemap file may not be greater than 50000.',
'sitemap_gzip_boolean' => 'Sitemap compression setting must be true or false.',
'sitemap_serve_stale_on_miss_boolean' => 'Serving the previous sitemap setting must be true or false.',
'sitemap_max_urls_per_contributor_integer' => 'Maximum URLs per contributor must be an integer.',
'sitemap_max_urls_per_contributor_min' => 'Maximum URLs per contributor must be at least 0.',
'sitemap_schedule_invalid' => 'Please select a valid sitemap generation schedule.',
'sitemap_schedule_time_invalid' => 'Sitemap generation time must be in HH:mm format.',
+5
View File
@@ -12,9 +12,14 @@ return [
// Sitemap 수동 재생성
'sitemap_regenerated' => 'Sitemap 재생성이 완료되었습니다.',
'sitemap_regenerate_dispatched' => 'Sitemap 재생성을 시작했습니다. 완료까지 시간이 걸릴 수 있습니다.',
'sitemap_regenerate_failed' => 'Sitemap 재생성에 실패했습니다.',
'sitemap_disabled' => 'Sitemap 생성이 비활성화되어 있습니다.',
// Sitemap 생성/서빙
'sitemap_not_ready' => 'Sitemap 을 준비하는 중입니다. 잠시 후 다시 시도해주세요.',
'sitemap_generate_failed' => 'Sitemap 생성에 실패했습니다: :error',
// 캐시 통계
'stats_period' => '최근 :days일간 SEO 캐시 통계',
'stats_overall' => '전체 통계',
+17 -6
View File
@@ -903,6 +903,13 @@ return [
'sitemap_cache_ttl_integer' => 'Sitemap 캐시 TTL은 정수여야 합니다.',
'sitemap_cache_ttl_min' => 'Sitemap 캐시 TTL은 최소 3600초(1시간) 이상이어야 합니다.',
'sitemap_cache_ttl_max' => 'Sitemap 캐시 TTL은 최대 604800초(7일)를 초과할 수 없습니다.',
'sitemap_urls_per_file_integer' => 'Sitemap 파일당 URL 수는 정수여야 합니다.',
'sitemap_urls_per_file_min' => 'Sitemap 파일당 URL 수는 최소 1000개 이상이어야 합니다.',
'sitemap_urls_per_file_max' => 'Sitemap 파일당 URL 수는 최대 50000개를 초과할 수 없습니다.',
'sitemap_gzip_boolean' => 'Sitemap 압축 설정은 true 또는 false 값이어야 합니다.',
'sitemap_serve_stale_on_miss_boolean' => '이전 Sitemap 제공 설정은 true 또는 false 값이어야 합니다.',
'sitemap_max_urls_per_contributor_integer' => '수집기당 최대 URL 수는 정수여야 합니다.',
'sitemap_max_urls_per_contributor_min' => '수집기당 최대 URL 수는 0 이상이어야 합니다.',
'sitemap_schedule_invalid' => '유효한 Sitemap 생성 주기를 선택해주세요.',
'sitemap_schedule_time_invalid' => 'Sitemap 생성 시각은 HH:mm 형식이어야 합니다.',
@@ -938,12 +945,16 @@ return [
'stats_cache_ttl_integer' => '통계 캐시 만료 시간은 정수여야 합니다.',
'stats_cache_ttl_min' => '통계 캐시 만료 시간은 최소 0초여야 합니다.',
'stats_cache_ttl_max' => '통계 캐시 만료 시간은 최대 14400초(4시간)를 초과할 수 없습니다.',
'seo_cache_enabled_required' => 'SEO 캐시 설정을 선택해주세요.',
'seo_cache_enabled_boolean' => 'SEO 캐시는 true 또는 false 값이어야 합니다.',
'seo_cache_ttl_required' => 'SEO 캐시 만료 시간을 입력해주세요.',
'seo_cache_ttl_integer' => 'SEO 캐시 만료 시간은 정수여야 합니다.',
'seo_cache_ttl_min' => 'SEO 캐시 만료 시간은 최소 0초여야 합니다.',
'seo_cache_ttl_max' => 'SEO 캐시 만료 시간은 최대 14400초(4시간)를 초과할 수 없습니다.',
// 고급 탭 전용 — SEO 탭의 오버라이드 칸(seo_cache_*)과 허용 범위가 다르므로 키를 분리한다.
'advanced_seo_cache_enabled_required' => 'SEO 캐시 설정을 선택해주세요.',
'advanced_seo_cache_enabled_boolean' => 'SEO 캐시는 true 또는 false 값이어야 합니다.',
'advanced_seo_cache_ttl_required' => 'SEO 캐시 만료 시간을 입력해주세요.',
'advanced_seo_cache_ttl_integer' => 'SEO 캐시 만료 시간은 정수여야 합니다.',
'advanced_seo_cache_ttl_min' => 'SEO 캐시 만료 시간은 최소 0초여야 합니다.',
'advanced_seo_cache_ttl_max' => 'SEO 캐시 만료 시간은 최대 14400초(4시간)를 초과할 수 없습니다.',
'seo_sitemap_cache_ttl_integer' => 'Sitemap 캐시 만료 시간은 정수여야 합니다.',
'seo_sitemap_cache_ttl_min' => 'Sitemap 캐시 만료 시간은 최소 3600초(1시간) 이상이어야 합니다.',
'seo_sitemap_cache_ttl_max' => 'Sitemap 캐시 만료 시간은 최대 604800초(7일)를 초과할 수 없습니다.',
// 디버그 설정
'debug_mode_required' => '디버그 모드 설정을 선택해주세요.',