- 템플릿 update 는 레이아웃 변경 여부와 무관하게 캐시 버전을 올리고(실패 복원 뒤에도), 자산 주소 방식 전환 3경로도 bump 한다
- config:cache 가 컨테이너 인스턴스를 덮어 이후 terminating 재게시가 사라지던 결함을 복원 헬퍼로 차단
- custom/ 변경 감지와 게시가 같은 열거자(재귀·크기 포함)를 쓰고, 서명은 호스트별로 저장
- 캐시 버전·서명 키를 만료시키지 않는다(기본 TTL 24h 로 매일 전체 재생성되던 문제)
- 관리자 > 환경설정 > 일반 「초기 화면 정적 파일」 카드: 상태 조회 + 지금 다시 만들기(확인 모달), 대시보드 알림 버튼 연결, CLI 와 같은 statusReport 소비
- sudo core:update 경로의 root 소유 잔존(설정 디렉토리·임시 폴더·로그·업그레이드 마이그레이션 산출물) 상속·정합화, ext-static 명령 root 경고와 sudo -u 힌트
- 안내·문서·트러블슈팅 4건·audit 룰 ext-cache-version-raw-read·ja 언어팩 동기
TLS 가 앞단에서 종단되고 앱에는 HTTP 로 전달되는 구성에서 X-Forwarded-* 가 전부
무시되어 화면 백지·IP 왜곡·webhook 403 이 함께 발생했다. 코어에 신뢰 프록시 설정
지점이 아예 없던 것이 원인이다.
- config/trustedproxy.php 로 내장 TrustProxies 미들웨어에 값을 공급한다.
bootstrap/app.php 는 건드리지 않는다 — withMiddleware 클로저는 .env 로드 전에
평가되어 env 가 항상 null 이 되는 조용한 no-op 이다.
- 판정은 App\Support\TrustedProxyDiagnostic 단일 SSoT 에서 계산하고 대시보드
알림·환경설정 고급 탭·설치 마법사·trusted-proxy:status 네 면이 소비한다.
판정식은 "HTTPS 인식 실패" 가 아니라 "X-Forwarded-* 수신 중 AND 신뢰 프록시
미설정" 이다 — HTTP 전용 사이트가 프록시 뒤에 있으면 화면은 정상인 채로
나머지만 조용히 어긋나기 때문이다.
- 값 편집 UI 와 쓰기 엔드포인트는 두지 않는다(잠금 역설 + XFF 위조 경로).
- 혼합 콘텐츠 차단을 부트스트랩 폴백의 별도 사유로 가른다. 새로고침으로 낫지
않으므로 버튼을 렌더하지 않고, 원인·조치는 콘솔로 운영자에게 보낸다.
- 대시보드 알림을 심각도로 배치한다 — warning 은 상단 배너, 그 외는 하단 카드.
같은 알림이 두 곳에 뜨지 않으며, 여러 건은 간격을 두고 쌓인다.
공개 이슈: (@lyg-kaban 제보)
공개 저장소 이슈 gnuboard/g7 (@bigmsg) 제보에서 출발한 작업이다.
브라우저가 화면을 그리려고 제3자 CDN 에 도달해야 하면, 그 도달 실패는 예외도 로그도
남기지 않고 화면 기능만 조용히 사라진다. 폐쇄망·방화벽·광고차단기에서 재현되는데
자체 서버 로그에는 흔적이 없어 운영자가 원인을 특정할 수 없다. 제보된 것은 편집기
하나였지만 같은 구조가 아이콘·글꼴·코드편집기·압축 라이브러리·설치 마법사·개발
대시보드에 똑같이 있었으므로, 번들 확장과 템플릿 전체를 자체 제공으로 옮겼다.
런타임에 외부로 나가는 것은 주소 검색 서비스 하나만 남았다.
자체 제공만으로는 부족하다 — 자기 서버에서 받는 파일도 실패할 수 있고, 종전에는 그
실패가 무음이었다. CSS 경로에 재시도 계층을 세우고(스크립트 경로와 동형), 서버가 HTML
에 직접 심는 externals 까지 실패를 붙잡아 안내 배너와 [다시 시도]로 표면화했다.
편집기·코드편집기는 확보 실패 시 평문 입력으로 내려앉되 저장 계약을 유지한다.
두 번째 축은 운영자가 CSS 를 덧붙일 자리가 없던 문제다(sir.kr 문의). 확장 디렉토리의
custom/ 을 운영자 소유로 정해, 확장 교체가 그 디렉토리만은 보존하게 했다. 출처에
의존하지 않는 서술자로 해석하므로 나중에 다른 출처가 붙어도 소비자는 바뀌지 않는다.
확장 자산과 같은 메커니즘으로 정적 게시되어 CSS 내부 상대 url 도 해석되고, 파일을
고치면 그 변경을 감지해 재게시까지 예약된다.
FTP 접근이 없는 운영자에게는 그 자리도 없는 것과 같으므로 레이아웃 편집기에서 직접
넣고 고칠 수 있게 했다. 모듈·플러그인·템플릿이 한 엔드포인트를 공유한다 — 타입별로
나누면 같은 검증이 세 벌로 갈리고 그중 약한 하나가 우회로가 된다. 여기서 올린
스크립트는 그 레이아웃 한 장이 아니라 사이트 전 화면에서 실행되므로 레이아웃 편집과
분리된 전용 권한으로 연다. 운영자 CSS 가 화면을 조작 불능으로 만들면 그것을 고칠
화면에도 같은 CSS 가 실려 스스로 갇히므로, 서버가 목록을 비우는 탈출구(?custom=off)를
함께 뒀다.
동봉 자산은 재생성 경로에 버전 대조 가드를 붙였다. 선언과 다른 버전을 버전 디렉토리에
써 넣는 조용한 거짓말은 배포된 뒤에는 드러나지 않는다.
코어 엔진 번들의 정규식 lookbehind 2건이 Safari 16.4 미만에서 파싱 자체를
거부당해 iOS 15 대 전 기기에서 SPA 가 통째로 뜨지 않았다. 정규식 리터럴은
스크립트 파싱 단계에서 검증되므로 그 문법을 모르는 엔진은 파일을 한 줄도
실행하지 못하고, 번들이 async/defer 없는 동기 classic 스크립트라 폴백 경로도
없었다. 빌드 타깃(es2020)이 이를 막지 못한 이유는 ES 연도와 브라우저 지원
연도가 다르기 때문이며, 정규식 리터럴 문법은 다운레벨 자체가 불가능해
어떤 타깃 설정으로도 걸러지지 않는다.
- 두 정규식을 lookbehind 없는 형태로 교체(engine-v1.60.6). 선행 따옴표·구분자를
소비한 뒤 캡처 그룹으로 분기하며, 식별자 인덱스가 match[1] → match[2] 로
이동한다. 무작위 200,034 표본 퍼징에서 교체 전후 결과 불일치 0건.
파싱 하한 실측 Safari 16.4 → 14.1 (제보자 기기 15.6 포함).
- 부팅 실패 안내를 사유별로 가른다. 번들을 받았는데 실행되지 않은 경우와
끝내 받지 못한 경우는 사용자가 취할 행동이 정반대인데 같은 문구였다.
사유는 추측이 아니라 window error 이벤트로 SyntaxError 를 관측해 판정한다
(파싱 실패 시 script 는 error 가 아니라 load 를 발생시켜 element 이벤트로는
구분 불가). 비호환 분기는 새로고침해도 낫지 않으므로 버튼을 렌더하지 않는다.
인라인 부트스트랩은 ES5 를 유지해야 이 안내 자체가 구형에서 살아남는다.
- 재발 방지: 배포 JS 산출물의 브라우저 하한 초과 문법·API 를 정적 검사로 차단.
부팅 임계 번들(코어 엔진·템플릿 컴포넌트)은 파싱에 실패하면 안내 화면조차
렌더되지 않으므로 하한과 같은 버전을 요구하는 정규식 리터럴 문법도 금지한다.
lookbehind 의 Safari 16.4 는 하한과 정확히 같아 하한 초과만 보는 검사로는
영원히 잡히지 않았다 — 그 지점이 이번 사각이었다.
- 지원 브라우저 최소 버전(Chrome 111 / Safari 16.4 / Firefox 128)과, 하한 미만
브라우저에서도 화면 표시와 기본 이용은 유지한다는 방침을 문서에 명시.
부수 수정: network-resilience E2E 4건이 자산 URL extensionless 모드에서 URL
glob 이 한 건도 매칭되지 않아 실패하고 있었다. 같은 파일의 컴포넌트 번들
테스트만 이중 모드를 알고 형제 케이스는 몰랐던 불균형이라 매처를 통일했다.
관리자 환경설정이 값을 저장은 하지만 실제 동작에는 닿지 않던 결함군을 정리했다.
설정 화면(min/max)·저장 검증(FormRequest)·런타임 적용(Service) 세 계층이 각자
리터럴을 들고 있어 서로 조용히 갈라지던 것이 공통 원인이다.
경계값 SSoT 도입:
- config/core.php `settings_limits` 를 단일 출처로 두고, FormRequest 는 이 값으로
규칙을 만들고 화면은 설정 응답의 `_meta.limits` 로 같은 값을 받아 바인딩한다.
- 게시판·이커머스도 각 모듈 config 의 limits 를 동일 구조로 노출한다.
적용되지 않던 정책을 실제로 적용:
- 비밀번호 최소 길이·특수문자 필수(신규 지정 7개 경로. 로그인은 제외 — 정책을
올렸을 때 기존 회원이 자기 계정에 접근조차 못 하는 데드락을 막기 위함)
- 계정 잠금 시간 0 = 무한대(전용 컬럼 분리. timestamp 상한과 NULL 규약 때문에
기존 컬럼 재사용이 불가능하다) + 관리자 수동 해제 경로
- 업로드 최대 크기·허용 확장자, 게시판 첨부 개수·댓글 깊이, 페이지 첨부 정책,
장바구니 수량·구매 한도, 마일리지 사용 한도, 주문 부가세
파생 결함:
- 큐 Job(sync 드라이버)이 진행 중인 요청의 Request 바인딩을 덮어써, 뒤이어 실행되는
훅 리스너가 헤더를 잃고 조용히 건너뛰었다. Job 이 원 바인딩을 복원하도록 고치고,
전송 헤더가 필요한 리스너는 동기 실행으로 선언한다.
- 확장 업데이트 실패 시 상태만 되돌리고 상태 캐시를 비우지 않아, 그 확장의 관리자
화면만 캐시 TTL(기본 하루) 동안 404 로 남았다. 실패 경로에도 무효화를 대칭으로
두고, 빈 목록 자체를 캐시하지 않는다.
- 글 작성 시 첨부 파일이 Service 로 전달되지 않아 검증·권한은 통과하고 첨부만 사라졌다.
- 환경설정 저장 실패 안내가 내부 설정 키를 그대로 노출했다(105개 항목에 라벨 부여).
재발 방지:
- audit 룰 3종(레이아웃 경계 리터럴 / FormRequest 경계 불일치 / Service 재클램프)
- 테스트 실행 범위 강제 훅(check-test-scope)
- 트러블슈팅 사례 2건(큐 Job 요청 오염 / 확장 상태 캐시 고착)
공개 이슈: gnuboard/g7