diff --git a/app/Extension/Traits/CachesModuleStatus.php b/app/Extension/Traits/CachesModuleStatus.php index 95cd4c49..aa011ff8 100644 --- a/app/Extension/Traits/CachesModuleStatus.php +++ b/app/Extension/Traits/CachesModuleStatus.php @@ -76,23 +76,26 @@ public static function invalidateModuleStatusCache(): void /** * DB 연결 + 테이블 존재 여부를 확인합니다 (인스톨러 안전성). * - * 설치 완료 상태(`config('app.installer_completed')`)일 때는 테이블 존재를 - * 전제로 하여 `Schema::hasTable()` 호출을 건너뜁니다. 인스톨러 이전 환경이나 - * 테스트에서는 기존 체크 경로로 폴백합니다. + * 항상 실제 테이블 존재 여부를 확인합니다. + * 결과는 정적 캐시에 저장되어 같은 요청 내에서 중복 Schema 조회를 방지합니다. + * (신규 서버 배포 시 INSTALLER_COMPLETED=true 상태에서 마이그레이션 전 실행되는 경우 대응) */ private static function isExtensionTableReady(string $table): bool { - if (config('app.installer_completed')) { - return true; + static $cache = []; + + if (isset($cache[$table])) { + return $cache[$table]; } try { DB::connection()->getPdo(); - - return Schema::hasTable($table); + $cache[$table] = Schema::hasTable($table); } catch (\Throwable $e) { - return false; + $cache[$table] = false; } + + return $cache[$table]; } /** diff --git a/app/Extension/Traits/CachesPluginStatus.php b/app/Extension/Traits/CachesPluginStatus.php index e6c1f172..9d0a4370 100644 --- a/app/Extension/Traits/CachesPluginStatus.php +++ b/app/Extension/Traits/CachesPluginStatus.php @@ -74,22 +74,28 @@ public static function invalidatePluginStatusCache(): void } /** - * 설치 완료 상태에서는 `Schema::hasTable()` 호출을 건너뜁니다. - * 인스톨러 이전 환경이나 테스트에서는 기존 체크 경로로 폴백합니다. + * DB 연결 + 테이블 존재 여부를 확인합니다 (인스톨러 안전성). + * + * 항상 실제 테이블 존재 여부를 확인합니다. + * 결과는 정적 캐시에 저장되어 같은 요청 내에서 중복 Schema 조회를 방지합니다. + * (신규 서버 배포 시 INSTALLER_COMPLETED=true 상태에서 마이그레이션 전 실행되는 경우 대응) */ private static function isPluginTableReady(): bool { - if (config('app.installer_completed')) { - return true; + static $cache = null; + + if ($cache !== null) { + return $cache; } try { DB::connection()->getPdo(); - - return Schema::hasTable('plugins'); + $cache = Schema::hasTable('plugins'); } catch (\Throwable $e) { - return false; + $cache = false; } + + return $cache; } private static function resolvePluginStatusCache(): CacheInterface diff --git a/app/Extension/Traits/CachesTemplateStatus.php b/app/Extension/Traits/CachesTemplateStatus.php index 10294afd..36f74d2f 100644 --- a/app/Extension/Traits/CachesTemplateStatus.php +++ b/app/Extension/Traits/CachesTemplateStatus.php @@ -99,22 +99,28 @@ public static function invalidateTemplateStatusCache(): void } /** - * 설치 완료 상태에서는 `Schema::hasTable()` 호출을 건너뜁니다. - * 인스톨러 이전 환경이나 테스트에서는 기존 체크 경로로 폴백합니다. + * DB 연결 + 테이블 존재 여부를 확인합니다 (인스톨러 안전성). + * + * 항상 실제 테이블 존재 여부를 확인합니다. + * 결과는 정적 캐시에 저장되어 같은 요청 내에서 중복 Schema 조회를 방지합니다. + * (신규 서버 배포 시 INSTALLER_COMPLETED=true 상태에서 마이그레이션 전 실행되는 경우 대응) */ private static function isTemplateTableReady(): bool { - if (config('app.installer_completed')) { - return true; + static $cache = null; + + if ($cache !== null) { + return $cache; } try { DB::connection()->getPdo(); - - return Schema::hasTable('templates'); + $cache = Schema::hasTable('templates'); } catch (\Throwable $e) { - return false; + $cache = false; } + + return $cache; } private static function resolveTemplateStatusCache(): CacheInterface diff --git a/app/Providers/LanguagePackServiceProvider.php b/app/Providers/LanguagePackServiceProvider.php index a4df551c..7b1ef5ad 100644 --- a/app/Providers/LanguagePackServiceProvider.php +++ b/app/Providers/LanguagePackServiceProvider.php @@ -478,10 +478,8 @@ private function isRegistryReady(): bool return false; } - if (config('app.installer_completed') === true) { - return true; - } - + // 항상 테이블 존재 여부를 확인합니다 (installer_completed 여부와 무관). + // 신규 서버 배포 시 INSTALLER_COMPLETED=true 상태에서 마이그레이션 전 실행되는 경우 대응. try { return Schema::hasTable('language_packs'); } catch (QueryException $e) { diff --git a/app/Providers/ModuleRouteServiceProvider.php b/app/Providers/ModuleRouteServiceProvider.php index 2e28b5a0..99ba9aea 100644 --- a/app/Providers/ModuleRouteServiceProvider.php +++ b/app/Providers/ModuleRouteServiceProvider.php @@ -49,22 +49,20 @@ protected function loadModuleRoutes(): void return; } - // 설치 완료 상태에서는 Schema introspection 을 건너뜀 (매 요청 쿼리 제거). - // 인스톨러 이전 환경에서는 기존 체크 경로로 폴백. - if (! config('app.installer_completed')) { - try { - if (! Schema::hasTable('modules')) { - return; - } - - // identifier 컬럼이 없으면 스킵 (마이그레이션 미적용 상태) - if (! Schema::hasColumn('modules', 'identifier')) { - return; - } - } catch (\Exception) { - // DB 연결 실패 시 조용히 스킵 (설정 오류, 마이그레이션 전 등) + // 항상 테이블 존재 여부를 확인합니다 (installer_completed 여부와 무관). + // 신규 서버 배포 시 INSTALLER_COMPLETED=true 상태에서 마이그레이션 전 실행되는 경우 대응. + try { + if (! Schema::hasTable('modules')) { return; } + + // identifier 컬럼이 없으면 스킵 (마이그레이션 미적용 상태) + if (! Schema::hasColumn('modules', 'identifier')) { + return; + } + } catch (\Exception) { + // DB 연결 실패 시 조용히 스킵 (설정 오류, 마이그레이션 전 등) + return; } // 활성화된 모듈 identifier 목록 가져오기 diff --git a/app/Providers/PluginRouteServiceProvider.php b/app/Providers/PluginRouteServiceProvider.php index 5de99fd5..f94ee27b 100644 --- a/app/Providers/PluginRouteServiceProvider.php +++ b/app/Providers/PluginRouteServiceProvider.php @@ -45,17 +45,15 @@ protected function loadPluginRoutes(): void return; } - // 설치 완료 상태에서는 Schema introspection 을 건너뜀 (매 요청 쿼리 제거). - // 인스톨러 이전 환경에서는 기존 체크 경로로 폴백. - if (! config('app.installer_completed')) { - try { - if (! Schema::hasTable('plugins')) { - return; - } - } catch (\Exception) { - // DB 연결 실패 시 조용히 스킵 (설정 오류, 마이그레이션 전 등) + // 항상 테이블 존재 여부를 확인합니다 (installer_completed 여부와 무관). + // 신규 서버 배포 시 INSTALLER_COMPLETED=true 상태에서 마이그레이션 전 실행되는 경우 대응. + try { + if (! Schema::hasTable('plugins')) { return; } + } catch (\Exception) { + // DB 연결 실패 시 조용히 스킵 (설정 오류, 마이그레이션 전 등) + return; } $plugins = File::directories($pluginsPath); diff --git a/config/database.php b/config/database.php index da01b564..22dfe112 100644 --- a/config/database.php +++ b/config/database.php @@ -47,21 +47,21 @@ 'driver' => 'mysql', 'read' => [ 'host' => [ - env('DB_READ_HOST', '127.0.0.1'), + env('DB_READ_HOST', env('DB_HOST', '127.0.0.1')), ], - 'port' => env('DB_READ_PORT', '3306'), - 'database' => env('DB_READ_DATABASE', 'laravel'), - 'username' => env('DB_READ_USERNAME', 'root'), - 'password' => env('DB_READ_PASSWORD', ''), + 'port' => env('DB_READ_PORT', env('DB_PORT', '3306')), + 'database' => env('DB_READ_DATABASE', env('DB_DATABASE', 'laravel')), + 'username' => env('DB_READ_USERNAME', env('DB_USERNAME', 'root')), + 'password' => env('DB_READ_PASSWORD', env('DB_PASSWORD', '')), ], 'write' => [ 'host' => [ - env('DB_WRITE_HOST', '127.0.0.1'), + env('DB_WRITE_HOST', env('DB_HOST', '127.0.0.1')), ], - 'port' => env('DB_WRITE_PORT', '3306'), - 'database' => env('DB_WRITE_DATABASE', 'laravel'), - 'username' => env('DB_WRITE_USERNAME', 'root'), - 'password' => env('DB_WRITE_PASSWORD', ''), + 'port' => env('DB_WRITE_PORT', env('DB_PORT', '3306')), + 'database' => env('DB_WRITE_DATABASE', env('DB_DATABASE', 'laravel')), + 'username' => env('DB_WRITE_USERNAME', env('DB_USERNAME', 'root')), + 'password' => env('DB_WRITE_PASSWORD', env('DB_PASSWORD', '')), ], 'sticky' => true, 'url' => env('DB_URL'),