feat(core): 정적 최적화 서버 대응 자산 URL 이중 모드
nginx 의 정규식 location 은 프리픽스 location 보다 먼저 매칭되므로, `location ~* \.(js|css|json)$` 블록이 있는 서버에서는 확장자 붙은 동적 엔드포인트가 `try_files ... /index.php` 폴백이 실행될 기회 없이 404 가 된다. aaPanel/CyberPanel/Plesk 기본 템플릿에 들어있어 드물지 않으며, 관리자 화면조차 뜨지 않아 "서버 설정을 고치세요" 안내가 순환 참조가 된다. 관례를 깬 쪽이 G7 이므로 해소 책임도 G7 에 두고, 두 형태를 모두 서빙한 뒤 환경에 따라 택일한다. 확장자 형태는 영구 유지한다 — 제거하면 URL 을 하드코딩한 서드파티 확장이 깨진다. - 라우트: dualSuffix / dualSuffixSegment / dualAsset 매크로로 23개 엔드포인트와 프로브를 이중 등록. 확장자 형태를 먼저 등록한다 — 확장자 없는 쪽이 더 느슨한 패턴이라 순서가 뒤집히면 `.json` 요청까지 삼킨다 - URL 생성: 서버 App\Support\AssetUrl, 프론트 core/support/assetUrl.ts 로 집약. 기본 모드에서 생성 결과는 치환 이전과 문자열까지 동일하다 (쿼리 순서 포함 — 순서가 바뀌면 의미는 같아도 HTTP 캐시 키가 갈린다) - 자가 복구: 부트스트랩 자산 로드 실패 시 확장자 없는 형태로 1회 단방향 전환. 역방향 금지 + 기존 재시도 예산 공유로 무한 왕복을 막는다 - 감지·전환: 인스톨러 프로브(설치 시 확정) / 관리자 환경설정 일반 탭 / g7:asset-url-mode. 판정은 상태코드가 아니라 매직 토큰 + Content-Type 으로 한다. 상태코드만 보면 "404 대신 200 + 에러 HTML" 환경에서 영원히 오판한다 - 봇은 JavaScript 를 실행하지 않아 자가 복구가 닿지 않으므로, 모드 변경 시 SEO 프리렌더 캐시를 비워 재생성시킨다 가드: audit 룰 2종(dynamic-route-static-extension / asset-url-builder-required), Playwright 6건(정적 블록 가로채기 시뮬레이션), 루프 방지 불변식 L1~L9 전수 red 증명. 부수 정리: 양 Composer 에 중복돼 있던 확장 에셋 수집 123줄을 트레이트로 통합하고, 자산 확장자 화이트리스트 누락(.map)과 경로 정규화 우회 가능성을 함께 교정. sir.kr 커뮤니티의 hang 님께서 제보해주셨습니다.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
// /install/lang/ko.php
|
||||
|
||||
return [
|
||||
@@ -424,7 +425,7 @@ return [
|
||||
'abort_rollback_success' => '[중단] 롤백 완료: :message',
|
||||
'abort_rollback_failed' => '[중단] 롤백 실패: :message (계속 진행)',
|
||||
'abort_no_rollback_needed' => '[중단] 롤백할 작업이 없습니다. (current_task가 null이거나 이미 완료됨)',
|
||||
'abort_by_user' => "[중단] 사용자가 설치를 중단했습니다. (현재 작업: :task)",
|
||||
'abort_by_user' => '[중단] 사용자가 설치를 중단했습니다. (현재 작업: :task)',
|
||||
'abort_installation_stopped' => '설치가 중단되었습니다.',
|
||||
|
||||
// Worker 실패 시 롤백 관련 메시지
|
||||
@@ -548,7 +549,7 @@ return [
|
||||
'validation_incomplete_title' => '다음 항목을 완료해주세요:',
|
||||
'confirm_leave_page' => '설정이 저장되지 않았습니다. 페이지를 나가시겠습니까?',
|
||||
'installation_in_progress_alert' => '설치가 진행 중입니다. 설정 페이지로 돌아가시겠습니까?',
|
||||
'confirm_go_to_settings' => "설정 페이지로 이동하시겠습니까?",
|
||||
'confirm_go_to_settings' => '설정 페이지로 이동하시겠습니까?',
|
||||
'confirm_go_to_settings_simple' => "설정 페이지로 이동하시겠습니까?\n\n설치 상태가 초기화되며, 모든 작업이 처음부터 다시 실행됩니다.\n\n⚠️ 데이터베이스에 생성된 테이블은 자동으로 삭제되지 않습니다.\n필요 시 phpMyAdmin 등을 통해 수동으로 정리해주세요.",
|
||||
'confirm_go_to_settings_title' => '설정 페이지로 이동',
|
||||
'confirm_go_to_settings_desc' => '설치 상태가 초기화되며, 모든 작업이 처음부터 다시 실행됩니다.\n\n⚠️ 데이터베이스에 생성된 테이블은 자동으로 삭제되지 않습니다. 필요 시 phpMyAdmin 등을 통해 수동으로 정리해주세요.',
|
||||
@@ -712,7 +713,7 @@ ini_set(\'zlib.output_compression\', \'off\');
|
||||
'deselect_all' => '전체 해제',
|
||||
|
||||
// install-worker.php 다국어 키
|
||||
'db_task_abort_detected_before_start' => '[DB 작업] 시작 전 중단 상태 감지 - 작업을 건너뜁니다.',
|
||||
'db_task_abort_detected_before_start' => '[DB 작업] 시작 전 중단 상태 감지 - 작업을 건너뜁니다.',
|
||||
'db_task_failed_rollback_start' => '[DB 작업] :task 실패 - 롤백을 시작합니다.',
|
||||
'db_task_abort_reason_connection' => '연결 끊김',
|
||||
'db_task_abort_reason_user' => '사용자 요청',
|
||||
@@ -831,6 +832,15 @@ ini_set(\'zlib.output_compression\', \'off\');
|
||||
'core_update_settings' => '코어 업데이트 설정 (선택)',
|
||||
'core_update_pending_path' => '업데이트 대기 디렉토리 경로',
|
||||
'core_update_pending_path_help' => '비워두면 기본값(storage/app/core_pending)을 사용합니다. 외부 경로를 사용하려면 절대 경로 또는 그누보드7 루트 기준 상대 경로를 입력하세요.',
|
||||
|
||||
// 자산 URL 방식 (이슈 #486)
|
||||
'asset_url_mode' => '자산 파일 주소 방식',
|
||||
'asset_url_mode_extension' => '확장자 사용 (권장)',
|
||||
'asset_url_mode_extensionless' => '확장자 미사용',
|
||||
'asset_url_mode_help' => '서버 설정을 자동으로 감지해 선택합니다. 일부 서버는 .js/.css/.json 주소를 가로채 화면이 뜨지 않는데, 그 경우 확장자 미사용을 선택하세요.',
|
||||
'asset_url_mode_detected_extension' => '감지 결과: 확장자를 사용할 수 있는 환경입니다.',
|
||||
'asset_url_mode_detected_extensionless' => '감지 결과: 서버가 확장자 주소를 가로채고 있어 확장자 미사용을 선택했습니다.',
|
||||
'asset_url_mode_detected_unavailable' => '감지하지 못했습니다. 설치 후 화면이 뜨지 않으면 확장자 미사용으로 바꿔 보세요.',
|
||||
'core_update_github_url' => 'GitHub 저장소 URL',
|
||||
'core_update_github_url_help' => '코어 업데이트를 확인할 GitHub 저장소 URL입니다.',
|
||||
'core_update_github_token' => 'GitHub 액세스 토큰',
|
||||
@@ -915,4 +925,3 @@ ini_set(\'zlib.output_compression\', \'off\');
|
||||
// 상대경로 병기 안내
|
||||
'or_relative_path' => '또는 그누보드7 루트 디렉토리에서:',
|
||||
];
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user