diff --git a/app/Listeners/ExtensionConfigCacheListener.php b/app/Listeners/ExtensionConfigCacheListener.php index e785c3e3..7ceb3d8f 100644 --- a/app/Listeners/ExtensionConfigCacheListener.php +++ b/app/Listeners/ExtensionConfigCacheListener.php @@ -46,6 +46,12 @@ class ExtensionConfigCacheListener implements HookListenerInterface $hooks[$hookName] = [ 'method' => 'onExtensionToggled', 'priority' => 30, + // config 캐시 재생성은 인프라 작업이라 큐로 미루면 안 된다. Action 훅의 기본 + // 등록은 큐 디스패치(HookListenerRegistrar) 이므로, sync 를 명시하지 않으면 + // 워커 미가동 환경에서 config:cache 가 재생성되지 않는다. 또한 확장 토글은 + // CLI 커맨드(plugin:deactivate 등)로도 수행되며, 커맨드 프로세스는 실행 후 + // 즉시 종료되어 큐에 쌓인 작업을 처리할 주체가 없다. 반드시 동기 실행한다. + 'sync' => true, ]; } diff --git a/docs/backend/service-provider.md b/docs/backend/service-provider.md index aa2d4ffd..aefd901d 100644 --- a/docs/backend/service-provider.md +++ b/docs/backend/service-provider.md @@ -456,6 +456,8 @@ config 소스(`config/*.php`, `.env`, `storage/app/settings/*.json`, 활성 확 | 확장 설치/삭제/업데이트 | `ExtensionManager::updateComposerAutoload()` (오토로드·훅 캐시와 동일 생명주기) | | 확장 활성화/비활성화 | `ExtensionConfigCacheListener` (`core.*.activated` / `core.*.after_deactivate` 훅 구독) | +`ExtensionConfigCacheListener` 의 구독 훅은 `getSubscribedHooks()` 에서 `'sync' => true` 로 선언한다. 훅 Action 리스너의 기본 등록 정책은 큐 디스패치(`HookListenerRegistrar`)인데, config 캐시 재생성을 큐로 미루면 (1) 워커 미가동 환경에서 영영 재생성되지 않고, (2) 확장 토글을 수행하는 CLI 커맨드(`plugin:deactivate` 등)는 실행 후 프로세스가 종료되어 큐 작업을 처리할 주체가 없다. config 캐시 재생성은 인프라 부수효과이므로 반드시 동기 실행한다. 회귀 방지 테스트: `ExtensionConfigCacheListenerTest::test_all_hooks_are_synchronous`. + **확장별 개별 환경설정 저장(board/ecommerce/plugin SettingsService)은 재생성 대상이 아니다.** 확장 설정은 `config/*.php` 가 아니라 settings JSON 에 저장되고, 매 요청 `CoreServiceProvider::boot` 의 `loadModule/PluginSettingsToConfig` 가 런타임 `Config::set('g7_settings.*', 최신값)` 으로 config 캐시에 박제된 값을 덮어쓴다. 따라서 값 stale 이 없고(즉시 반영), config 캐시를 clear 하지도 않으므로(성능 손실 없음) 재생성이 실효가 없다. --- diff --git a/tests/Unit/Listeners/ExtensionConfigCacheListenerTest.php b/tests/Unit/Listeners/ExtensionConfigCacheListenerTest.php index 847fbacb..463e2623 100644 --- a/tests/Unit/Listeners/ExtensionConfigCacheListenerTest.php +++ b/tests/Unit/Listeners/ExtensionConfigCacheListenerTest.php @@ -66,4 +66,19 @@ class ExtensionConfigCacheListenerTest extends TestCase $this->assertArrayHasKey('priority', $config); } } + + public function test_all_hooks_are_synchronous(): void + { + // config 캐시 재생성은 인프라 작업이라 큐로 미루면 안 된다. Action 훅의 기본 등록은 + // 큐 디스패치(HookListenerRegistrar::applySubscribedHooks) 이므로, sync 플래그가 + // 없으면 워커 미가동 환경(대부분의 소규모 사이트)에서 config:cache 가 영영 재생성되지 + // 않는다. 게다가 CLI 커맨드(plugin:deactivate 등)는 실행 후 프로세스가 종료되어 큐에 + // 쌓인 작업을 처리할 주체가 없다. 모든 구독 훅은 sync=true 여야 한다. + $hooks = ExtensionConfigCacheListener::getSubscribedHooks(); + + foreach ($hooks as $hookName => $config) { + $this->assertArrayHasKey('sync', $config, "{$hookName} 은 sync 플래그가 있어야 합니다 (config 캐시 재생성은 동기 실행 필수)."); + $this->assertTrue($config['sync'], "{$hookName} 은 sync=true 여야 합니다 — 큐로 미루면 워커 미가동 시 config 캐시가 재생성되지 않습니다."); + } + } }