Commit Graph
15 Commits
Author SHA1 Message Date
HeuJung 072b3f50d9 feat(core,admin-template): 자산 URL 방식 감지 지점 보강 + 테스트 격리 결함 수정
계획서 §검증 후반부를 다시 대조해 미구현 3건을 확인하고 모두 구현했다. 앞선 커밋에서
"미구현 1건" 으로 보고한 것은 §12 불변식 표만 파일로 대조하고 §검증 섹션의 표 두 개는
확인하지 않은 결과였다.

- 설치 2단계 요구사항 화면에 자산 URL 방식 항목 추가. 판정은 서버가 아니라 브라우저가
 한다 — 서버에서 자기 APP_URL 로 요청하면 loopback 이 vhost·프록시 체인을 우회해
 실제 방문자와 다른 답을 낸다. 2단계와 3단계가 같은 판정 함수를 쓰도록 추출했다
- 대시보드가 스스로 프로브를 던져 **저장된** 방식과 대조하고 어긋나면 안내한다.
 런타임 값이 아니라 저장값을 보는 이유는, 자가 복구가 이미 전환해 놓았을 때가 바로
 알려야 할 상황이기 때문이다 — 봇은 자바스크립트를 실행하지 않아 자가 복구가 닿지 않는다.
 알리기만 하고 저장하지 않으므로 클라이언트가 서버 설정을 뒤집지 않는다
- 레이아웃 렌더링 테스트를 두 모드로 추가. 핵심 단언은 두 모드의 DOM 이 완전히 동일하다는
 것이다 — 모드가 화면 구조로 새어나가면 확장자 없는 환경에서만 깨지고 그 환경은 개발 중
 거의 재현되지 않는다

함께 고친 테스트 격리 결함 4건 (이번 기능과 무관하나 같은 세션에서 발견):

- 인스톨러 공유 클래스 로드가 오토로드를 막고 있어, 앱 루트를 임시 디렉토리로 바꾸는
 테스트에서 없는 파일을 읽으려다 죽었다 (8건 fatal)
- 테스트용 번역 스텁이 실제 구현과의 정의 경쟁에서 이기면 주입된 번역을 무시했다.
 클래스 단독 실행은 통과하고 스위트 실행만 실패하던 원인
- 모듈 활성 목록은 "디스크 로드분 ∩ DB 활성분" 인데 부팅 시 테이블이 비어 디스크 스캔이
 일어나지 않아, 파일 캐시 잔재가 있을 때만 통과하던 순서 의존 테스트
- 라우트 프로바이더 가드 테스트가 Git 미추적 파일(.env) 존재에 의존해 신규 클론·CI 에서
 실패했다. 임시 앱 루트로 격리

또한 일부 테스트가 실제 개발 환경 파일을 삭제·재생성하고 자신의 정리 단계에서 되돌리는데,
그 테스트가 죽으면 되돌아오지 않아 복구 불가능한 유실이 발생한다. 실제로 이번 세션에서
겪었으므로 테스트 부트스트랩에 프로세스 수준 복원 안전망을 추가했다.

검증: 백엔드 344건 / 템플릿 2799건 / 레이아웃 7건 / 브라우저 9건 green, audit 위반 0.
2026-07-23 11:39:57 +09:00
HeuJung 15cd4452bf 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 님께서 제보해주셨습니다.
2026-07-23 11:34:32 +09:00
HeuJung 7f8fb6870f fix(core,board,ecommerce): 조건부 검증 결함 패밀리 일괄 수정 및 실패 안내 노출
여러 화면에서 조건부(FormRequest) 검증이 우회되거나 과도하게 제한되던 결함을 한 계열로 묶어 수정한다.

- 검증 규칙 확장 필터 이름 10개를 나머지 92개와 동일 규칙으로 통일
- 설명 길이를 바이트가 아닌 글자 수로 계산(한글 오검출 해소)
- 비활성 언어팩 번역 보존한 채 수정 허용
- 알림 템플릿 제목 nullable 존중, 미지원 발송 수단 저장 차단
- 일정/실행 이력 목록 조회 검증 정합
- 게시판 첨부 미사용 시 빈 확장자 통과, new_display_hours/최소 길이 0 허용, 일괄 적용 override_values 범위 검증(SSoT)
- 이커머스 클레임 사유 조회 검증, 용어 정정
- 원문 i18n 키 노출 다수 해소, 저장/삭제 실패 시 사유 화면 노출
- 아이콘 크기/다크모드 대비 회귀 수정

번들 확장(모듈3·플러그인6·템플릿2·언어팩5) 버전 new-bump 및 CHANGELOG 반영, ja 언어팩 동기.
2026-07-23 10:54:17 +09:00
HeuJung 062a88ca04 feat(core): DB 최고권한 계정 차단 + OPcache 상태 안내
설치 단계와 코어 부팅의 정책이 엇갈려 있었다. 인스톨러는 root 를 자유롭게
허용하면서, 설치 후 부팅은 같은 값을 보고 확장 로딩 전체를 조용히 스킵했다.
설치는 성공하지만 이후 깨진 화면만 뜨고 원인은 로그 한 줄에만 남았다.

판정을 프레임워크 의존성 0 인 공용 SSoT 로 모아 인스톨러와 코어가 같은 답을
내게 하고, 설치 단계에서 서버 2경로로 차단하며, 설치 후 변경은 전용 에러
페이지로 드러낸다. 가드를 boot 가 아닌 미들웨어에 두어 artisan 이 죽지 않게
해 복구 수단을 남겼다. admin 은 일반 권한 운영 계정 오차단을 막기 위해 차단
목록에서 제외했다.

OPcache 는 상태 조회 경로 자체가 없어 성능 저하를 인지할 방법이 없었다.
설치 단계 경고(비차단)와 관리자 정보 탭 표시를 같은 판정 SSoT 로 추가했다.
확장 로드 여부만으로는 부족해 opcache.enable 지시자까지 확인하며, ini_get 이
차단된 환경은 "확인 불가"로 폴백해 경고도 차단도 하지 않는다.
2026-07-20 14:17:49 +09:00
HeuJung face022073 feat(seo): 사이트맵 재생성 진행상황 + 실시간/폴링 UI
관리자 "전체 재생성"을 큐(Full 모드)로 던지고 진행상황을 SEO 탭에서 확인한다.
Reverb ON 이면 방송 채널 구독으로 실시간 갱신, OFF 면 상태 API 폴링으로 폴백한다.

- SitemapProgress 진행상황 스토어(queued→running→writing→completed/failed,
 캐시 TTL 3600, 방송 스로틀) + SitemapManager.getStatus 확장(progress + realtime_enabled)
- GET /api/admin/seo/sitemap/status(core.settings.read) 신규
- 관리자 재생성을 항상 Full 모드로 큐잉 + 진행률 기록, Job 최종 실패 시 progress=failed 고정
- core.admin.seo.sitemap 방송 채널 인증(core.settings.read)
- SEO 탭 진행상황 UI(상태 배지/단계/누적 URL/실시간·폴링 안내) + 폴링 분기
- 재생성 중 누적 URL 표기(replaceAllForContributor 삽입 건수 반환 — count 쿼리 없이 스트림 누적)
- ja 언어팩 진행상황 UI 번역 동기화
2026-07-18 18:22:16 +09:00
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
HeuJung c819820573 perf(core): JSON 서빙 네트워크 오버헤드 최적화
실측(g7.dev) 결과 줄바꿈 제거·gzip 은 이미 적용된 상태였고,
남은 실질 개선 3종을 구현:

- 모든 API 응답에 JSON_UNESCAPED_UNICODE 적용 — 한글이 \uXXXX(6B)로
 팽창하던 것을 raw UTF-8(3B)로 직렬화 (lang pack gzip -9.1%)
- 공개 레이아웃 서빙에서 개발자용 comment/_comment 재귀 제거,
 편집 모드(with_source_meta)는 보존 (layout gzip -34.9%)
- 번들 JS/CSS(BinaryFileResponse) gzip 압축 사각지대 해소 —
 기존 미들웨어가 getContent==false 로 압축을 건너뛰던 문제 (-73~77%)
2026-07-05 22:31:04 +09:00
HeuJung a958443d69 feat(core): 모듈/플러그인 프론트엔드 에셋 서버측 번들 병합
활성 모듈/플러그인마다 프론트엔드 IIFE JS·CSS 가 개별 HTTP 요청으로 로드되어
확장 수에 비례해 요청이 늘어나는 오버헤드를, 종류별 1개 번들로 서버 병합해 줄인다.
각 확장 IIFE 자가등록 계약(핸들러/리스너/preblocker)은 priority 순 물리 병합으로
실행 순서가 보존되어 그대로 동작한다.

## 백엔드
- 신규 ExtensionBundleService — 정렬(uasort priority)·필터(global)·concat(\n;\n
 구분자로 ASI 경계 보호)·소스맵(prod strip / dev rewrite)·version-in-path 디스크
 캐시·확장별 fine-grained try/catch(한 확장 실패가 번들 전체를 붕괴시키지 않음)
- AbstractModule/AbstractPlugin::getBuiltAssetAbsolutePaths — _bundled 경로
 정확 해석용 절대경로 게터(getAssetFilePath 하드코딩 복제 회피)
- PublicModule/PluginController::serveBundle{Js,Css} + routes/api.php 4개
 (/api/{modules,plugins}/bundle.{js,css}?v={version}, fileResponse ETag/304 재사용)
- config/filesystems.php ext-bundles 디스크 + storage/.gitignore + 루트 .gitignore
- 신규 ext-bundles:cleanup 커맨드 + {module,plugin,template}:cache-clear 번들
 정리 통합 + dev-dashboard 버튼

## 프론트엔드 (engine-v1.52.0)
- TemplateApp.loadExtensionAssets — 개별 로딩 → bundleUrls 기반 모듈→플러그인
 순 번들 로딩. bundleUrls 부재 시 개별 로딩 폴백(회귀 안전)
- ModuleAssetLoader.loadBundle + parseBundleUrlsFromConfig + ExtensionBundleUrls
- TemplateComposer/UserTemplateComposer bundleUrls 뷰변수 + app/admin.blade.php
- core:build 재빌드 산출물(public/build/core/*)

## 버전
- 코어 7.0.1 → 7.0.2 bump. 7.0.1 이 이미 2026-07-03 공개 릴리스이므로 그 위 신규
 기능은 +1 bump(new-bump). config/app.php(SSoT) + .env{.example,.testing.example,
 .testing} + README 뱃지 + INSTALL 예시 + 루트 CHANGELOG 신규 [7.0.2] 섹션 동기화.
- 확장 g7_version 상향 안 함(>=7.0.0 유지) — 6개 프론트 에셋 확장이 신규 코어
 표면을 자기 코드에서 호출하지 않고 개별 서빙 라우트 존치+프론트 폴백으로 코어
 7.0.0 에서도 동작 → 실제 의존 없음( 결정).
- 부수 결함 수정: config/app.php 편집으로 활성화된 core-update-targets-coverage
 룰이 v1.51.0 에서 추가된 vite.config.{editor,devtools}.js 가 app.update.targets
 에 미등재됨을 검출 → 추가. 코어 업데이트 시 편집기/devtools 빌드 설정 갱신 누락
 방지(CHANGELOG [7.0.2] Fixed).

## 규정/audit/문서/테스트
- ("확장 번들 병합" 절+파일유형표+E2E경로) / AGENTS.md /
 docs/extension/module-assets.md / docs/cheatsheet.md
- coverage.json 4항목 + codified 룰 4종(url-same-origin, concat-separator,
 asset-path-getter, clears-template-caches-no-file-delete)
- 루트 CHANGELOG [7.0.2] Added/Fixed / 엔진 CHANGELOG engine-v1.52.0 Added
- Vitest 11(loadBundle + concat 자가등록) + PHPUnit 17(Service 12 + Feature 5)
 + Playwright spec + 시나리오 매니페스트
- 브라우저 실측(g7.dev): 홈/관리자 번들 각 1건·개별 iife 0건, gdpr 인터셉터
 정상, 활성화 토글 반영, 장바구니→체크아웃·게시판 글쓰기 정상. g7_2.dev 미적용
 대비 확장 요청 11~12→4건, DOMContentLoaded ~843~957→605~741ms(페이로드 무손실)

## 도구 보완
- pre-commit-review 스킬 에 엔진 경로 감지 게이트(5-pre + 보고 5e행) 신설 —
 매핑 표에 엔진 파일이 있으면 엔진 CHANGELOG 판정을 강제(이번 초기 누락 재발 방지)
2026-07-05 21:57:39 +09:00
HeuJung ce8055e65c fix(core): 시스템 정보 조회가 일부 호스팅에서 전체 실패하던 문제 수정
disable_functions/open_basedir 제약 호스팅에서 CPU·메모리·디스크 등
개별 probe 실패가 system-info API 전체를 500 으로 떨구던 결함 수정.
buildSystemInfo 각 항목을 safeSystemProbe 로 격리해 실패 항목만 unknown
폴백으로 채우고 나머지는 정상 반환. 개별 실패는 Log::warning, 전체 실패는
컨트롤러 Log::error 로 기록. ( 대시보드 격리가 settings 에 미적용되어
재발한 공개 대응)

동반: SettingsController::restore 를 RestoreSettingsRequest 로 이전
(base Request + 컨트롤러 내 검증 제거), 미정의였던 backup_path_required·
restore_* lang 키 정의(ko/en/ja).
2026-07-02 15:11:59 +09:00
HeuJung c4ea9a6cd1 v7.0.0 release 2026-07-01 10:30:32 +09:00
HeuJung 61f5874fd2 v7.0.0-beta.6 release 2026-05-14 11:13:08 +09:00
HeuJung eb467aca79 v7.0.0-beta.5 release 2026-05-12 17:58:34 +09:00
HeuJung 1db039ff34 v7.0.0-beta.4 release 2026-05-11 11:29:41 +09:00
HeuJung fa8eba7a0d v7.0.0-beta.2 release 2026-04-20 20:37:49 +09:00
HeuJung 6595fd0eb5 v7.0.0-beta.1 release 2026-04-01 10:30:52 +09:00