Files
Gnuboard7/routes/web.php
T
HeuJung 1c99547bc1 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 전역 격리로 차단하고, 두 탭이 같은 다국어 키를 공유해 한쪽에 틀린
한도를 안내하던 사전결함을 키 분리로 해소했다.
2026-07-18 11:32:11 +09:00

61 lines
2.7 KiB
PHP

<?php
use App\Http\Controllers\Api\Public\SitemapController;
use App\Seo\TemplateRouteResolver;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
// 개발용 라우트 - 디버그 모드 + 관리자 인증 필수
Route::get('/dev', function () {
// 1. 디버그 모드 확인
if (! config('app.debug')) {
abort(404);
}
// 2. 관리자 인증 확인 (세션 기반 - stateful 미들웨어로 로그인 시 세션 생성)
$user = Auth::guard('web')->user();
if (! $user || ! $user->is_super) {
abort(403, '관리자 권한이 필요합니다.');
}
return view('dev-dashboard');
})->name('web.dev');
// Admin 라우트 - admin 템플릿 의존성 검증
Route::prefix('admin')
->middleware('template.dependencies:admin')
->group(function () {
Route::get('/{any?}', function (Request $request) {
// 미등록 경로는 SPA 셸 본문 + HTTP 404 (soft 404 방지 — 공개#47).
// 본문은 그대로라 클라이언트가 404 레이아웃을 렌더하고, 봇은 404 색인 제외.
$path = '/'.ltrim($request->getPathInfo(), '/');
if (! app(TemplateRouteResolver::class)->routeExists($path, 'admin')) {
return response(view('admin'), 404);
}
return view('admin');
})->where('any', '(?!.*\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)).*');
});
// Sitemap XML 라우트
// 자식 경로는 SitemapFileStore::childUrl() 이 sitemapindex 의 <loc> 에 기록하는 값과 일치해야 한다.
// gzip 여부는 manifest 가 결정하므로 .xml / .xml.gz 를 같은 액션에서 처리한다.
Route::get('/sitemap.xml', [SitemapController::class, 'index'])->name('web.sitemap');
Route::get('/sitemap-{n}.xml', [SitemapController::class, 'child'])->whereNumber('n')->name('web.sitemap.child');
Route::get('/sitemap-{n}.xml.gz', [SitemapController::class, 'child'])->whereNumber('n')->name('web.sitemap.child.gz');
// User 라우트 - user 템플릿 의존성 검증 + SEO 봇 감지
Route::middleware(['template.dependencies:user', 'seo'])
->group(function () {
Route::get('/{any?}', function (Request $request) {
// 미등록 경로는 SPA 셸 본문 + HTTP 404 (soft 404 방지 — 공개#47).
$path = '/'.ltrim($request->getPathInfo(), '/');
if (! app(TemplateRouteResolver::class)->routeExists($path, 'user')) {
return response(view('app'), 404);
}
return view('app');
})->where('any', '(?!admin)(?!api)(?!plugins)(?!.*\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)).*');
});