fix(core): 라우트 캐시 상태에서 DevTools 덤프·브라우저 로그 수집 복구
route:cache 가 걸리면 RouteServiceProvider 가 캐시 로드로 분기해 라우트 파일이 실행되지 않는다. routes/devtools.php 는 전역 함수 5개를 정의하고 핸들러가 호출했고, boost 의 browser-logs 는 프로바이더 boot 등록분이라 setCompiledRoutes 교체로 폐기됐다. 7.0.6 의 RouteCacheHelper 가 12개 라이프사이클 지점에서 캐시를 자동 생성하면서, 확장을 한 번이라도 건드린 사이트에서 항상 재현됐다. 로직을 오토로드되는 App\Support\DevTools\* 로 옮기고 browser-logs URI 를 G7 가 직접 소유해 캐시에 함께 구워지게 했다. 게이트가 참조하던 App\Models\Setting 은 저장소에 없는 유물이라 ConfigRepositoryInterface 로 교체했다. 같은 부류의 재발은 정적 검사(route-file-local-function)로 차단하고, 재현이 in-process 로 불가능하므로 서브프로세스 하네스로 회귀를 고정했다.
This commit is contained in:
@@ -290,9 +290,25 @@ resources/js/core/devtools/ # 브라우저 측 DevTools 코어
|
||||
└── ui/ # DevTools 패널 UI (React)
|
||||
|
||||
routes/devtools.php # 상태 덤프 API 엔드포인트
|
||||
app/Support/DevTools/ # 엔드포인트 실제 구현 (덤프 기록 · 브라우저 로그 · 디버그 게이트)
|
||||
storage/debug-dump/ # 디버그 데이터 저장 (자동 생성, Git 제외)
|
||||
```
|
||||
|
||||
라우트 파일은 위임만 하고 로직은 `app/Support/DevTools/` 의 클래스가 갖는다. 라우트 캐시가
|
||||
걸리면 라우트 파일이 로드되지 않으므로, 파일 안에 정의된 심볼은 핸들러가 호출하는 순간
|
||||
사라진다 (상세: [routing.md "캐시 안전한 라우트 작성"](../../backend/routing.md)).
|
||||
|
||||
### 브라우저 콘솔 로그 수집 (`_boost/browser-logs`)
|
||||
|
||||
이 엔드포인트는 원래 Laravel Boost 패키지가 등록했으나 G7 가 `routes/devtools.php` 에서 직접
|
||||
소유한다. 패키지는 서비스 프로바이더 부팅 시점에 라우트를 등록하는데, 라우트 캐시가 있으면
|
||||
그 시점의 등록이 캐시 교체로 폐기되어 엔드포인트가 사라졌다. 그 결과 브라우저가 보낸 로그
|
||||
요청이 조용히 실패하고 — 실패는 스크립트 안에서 삼켜져 화면에도 로그에도 아무 흔적이 남지
|
||||
않는다 — `storage/logs/browser.log` 만 비어 있었다.
|
||||
|
||||
URI 는 `_boost/browser-logs` 로 고정한다. 이미 배포된 주입 스크립트가 이 주소를 사용하므로
|
||||
바꾸면 로그 수집이 다시 끊긴다. 디버그 모드가 꺼져 있으면 403 을 반환한다.
|
||||
|
||||
---
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
@@ -362,6 +362,32 @@ Route::prefix('products')->group(function () {
|
||||
서버 라우트에 영향을 주지 않는다. 모듈 설정의 경로 값도 마찬가지다(서버 라우트 접두사는
|
||||
`api/modules/{identifier}` 로 식별자에 고정).
|
||||
|
||||
### 캐시 안전한 라우트 작성
|
||||
|
||||
캐시가 걸리면 `RouteServiceProvider::boot()` 이 캐시 파일 로드로 분기해 **라우트 파일 자체가
|
||||
실행되지 않는다.** 라우트 핸들러 클로저는 직렬화된 형태로 복원되지만, 파일이 실행되어야만
|
||||
존재하는 심볼은 어디에도 없다.
|
||||
|
||||
깨지는 것은 클로저가 아니라 **오토로드되지 않는 심볼 참조**다. 클로저 자체는 캐시에서 정상
|
||||
동작한다 — `routes/web.php` 의 SPA catch-all 이 그 증거다. 클래스는 오토로더가 찾아주므로
|
||||
FQCN 정적 호출도 안전하다. 문제가 되는 것은 오토로드 경로가 없는 것들뿐이다.
|
||||
|
||||
| ❌ 금지 | ✅ 올바른 사용 |
|
||||
|--------|---------------|
|
||||
| 라우트 파일에 전역 함수를 선언하고 핸들러가 호출 | 로직을 클래스(`app/Support/…` 등)로 옮기고 핸들러는 위임만 |
|
||||
| 파일 스코프 변수를 핸들러가 `use` 없이 참조 | 클래스 상수 또는 `use ($var)` 로 클로저에 캡처 |
|
||||
| 벤더/서비스 프로바이더가 조건부로 등록하는 라우트에 의존 | 그 URI 를 G7 라우트 파일이 직접 소유 |
|
||||
|
||||
전역 함수 위반의 증상은 특히 추적하기 어렵다. `Call to undefined function` 500 이 나는데
|
||||
예외의 `file` 이 `laravel-serializable-closure://` 라 **원인 파일이 스택에 드러나지 않는다.**
|
||||
정적 검사가 라우트 파일의 함수 선언을 차단한다.
|
||||
|
||||
프로바이더의 `boot()` 에서 등록한 라우트가 폐기되는 이유는 별개다. `Router::setCompiledRoutes()`
|
||||
가 라우트 컬렉션을 **통째로 교체**하고 이 교체가 `booted` 콜백에서 일어나므로, 그보다 앞서
|
||||
등록된 것은 조건 충족 여부와 무관하게 사라진다. 프레임워크 자신의 `BroadcastManager::routes()`
|
||||
는 캐시 여부를 확인하는 가드를 갖고 있지만, 모든 패키지가 그렇지는 않다. 그런 라우트에
|
||||
기능이 의존한다면 해당 URI 를 G7 라우트 파일에서 등록해 캐시에 함께 구워지게 한다.
|
||||
|
||||
---
|
||||
|
||||
## 개발 체크리스트
|
||||
|
||||
Reference in New Issue
Block a user