fix(core): 공개 템플릿 config 캐시 무효화 누락과 확장 캐시 삭제 경로 동종 결함 정비

공개 config.json 의 고정 캐시 키(template.config.{identifier})가 어떤 갱신
경로에서도 forget 되지 않아 template:update/cache-clear/비활성화/삭제 후에도
최대 1시간(TTL) 이전 manifest 로 응답되던 결함(공개 )을 수정한다.

- TemplateManager::clearTemplateCache 공개화 — config/components_manifest(두
 변종)/현재 버전 routes·language 키 능동 forget, updateTemplate cleanup 호출
- forgetLayoutCacheKeys 가 편집기용 .with_source_meta 변종까지 삭제
- ext.cache_version 쓰기를 incrementExtensionCacheVersion 단일 지점으로 통일
- module/plugin:cache-clear 의 유령 키 forget 을 실재 캐시(레이아웃 서빙/해석
 캐시, 상태 키, 미들웨어 인덱스, 버전 bump) 무효화로 교체
- 공개 routes/language/layout API 의 ?v 생략 폴백을 0 → 현재 버전으로 교정
This commit is contained in:
HeuJung committed 2026-08-17 13:22:48 +09:00
1 parent 09a700e4c7
commit e819db9d42
27 files changed
+891 -247

No files matched your search

@@ -3,10 +3,12 @@
namespace App\Http\Controllers\Api\Public;
use App\Enums\ExtensionStatus;
use App\Extension\Traits\ClearsTemplateCaches;
use App\Helpers\PermissionHelper;
use App\Http\Controllers\Api\Base\PublicBaseController;
use App\Services\LayoutService;
use App\Services\TemplateService;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
@@ -17,6 +19,8 @@ use Illuminate\Http\Response;
*/
class PublicLayoutController extends PublicBaseController
{
use ClearsTemplateCaches;
/**
* 레이아웃 캐시 TTL (초)
*/
@@ -69,7 +73,10 @@ class PublicLayoutController extends PublicBaseController
// 키만 forget 하므로 키 형식이 어긋나 무효화가 빗나간다(저장/복원 후 편집기 캔버스만
// stale). nonce 는 브라우저 HTTP 캐시 우회용(URL·ETag 차이로 이미 달성)이고, 서버 캐시 키
// 정합은 정수 버전이 SSoT 다. `(int)` 캐스팅은 PHP 가 소수점에서 절단해 정수부만 남긴다.
$cacheVersion = (int) request()->query('v', 0);
// `?v` 생략 시 현재 버전으로 폴백 — 리터럴 0 폴백은 워밍/무효화 어느 경로에도
// 걸리지 않는 `.v0` 영구 사각 키를 만든다 (#588).
$rawVersion = request()->query('v');
$cacheVersion = $rawVersion !== null ? (int) $rawVersion : self::getExtensionCacheVersion();
// 편집기 출처 메타 옵션
// - 옵션이 truthy 면 각 노드에 `__source` 메타를 부여한 응답을 반환
@@ -119,7 +126,7 @@ class PublicLayoutController extends PublicBaseController
$mergedLayout,
self::CACHE_TTL
);
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
} catch (ModelNotFoundException $e) {
// 레이아웃 또는 부모 레이아웃을 찾을 수 없음 - 예외 메시지 전달
return $this->notFound($e->getMessage());
}
@@ -22,6 +22,8 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
*/
class PublicTemplateController extends PublicBaseController
{
use ClearsTemplateCaches;
public function __construct(
private TemplateService $templateService,
private TemplateLayoutAttachmentService $layoutAttachmentService,
@@ -40,8 +42,11 @@ class PublicTemplateController extends PublicBaseController
// API 사용량 기록
$this->logApiUsage('templates.routes', ['identifier' => $identifier]);
// 캐시 버전을 키에 포함하여 모듈/플러그인 변경 시 캐시 무효화
$cacheVersion = request()->query('v', 0);
// 캐시 버전을 키에 포함하여 모듈/플러그인 변경 시 캐시 무효화.
// `?v` 생략 시 현재 버전으로 폴백 — 리터럴 0 폴백은 워밍/무효화 어느 경로에도
// 걸리지 않는 `.v0` 영구 사각 키를 만든다 (#588). `(int)` 캐스트로 키 위생 겸용.
$rawVersion = request()->query('v');
$cacheVersion = $rawVersion !== null ? (int) $rawVersion : self::getExtensionCacheVersion();
// 확장 업데이트 중에는 활성 디렉토리가 잠시 비어 그 모듈의 라우트가 통째로 빠진다.
// 그 순간의 응답을 버전 키에 캐시하면 업데이트가 끝난 뒤에도 캐시가 만료될 때까지
@@ -226,7 +231,7 @@ class PublicTemplateController extends PublicBaseController
// 캐시 버전을 응답에 포함하여 프론트엔드가 API 호출 시 사용하도록 함
$responseData = $configData['data'];
$responseData['cache_version'] = ClearsTemplateCaches::getExtensionCacheVersion();
$responseData['cache_version'] = self::getExtensionCacheVersion();
return $this->success(
__('templates.messages.config_retrieved'),
@@ -249,8 +254,10 @@ class PublicTemplateController extends PublicBaseController
'locale' => $locale,
]);
// 캐시 버전을 키에 포함하여 모듈/플러그인 변경 시 캐시 무효화
$cacheVersion = request()->query('v', 0);
// 캐시 버전을 키에 포함하여 모듈/플러그인 변경 시 캐시 무효화.
// `?v` 생략 시 현재 버전으로 폴백 (getRoutes 와 동일 — `.v0` 사각 키 방지)
$rawVersion = request()->query('v');
$cacheVersion = $rawVersion !== null ? (int) $rawVersion : self::getExtensionCacheVersion();
// 캐싱된 응답 반환 (1시간 유효)
$languageData = $this->cached(