You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
모듈·플러그인·템플릿 manifest 의 integrations 에 버전 제약 객체형({"modules": {"sirsoft-board": ">=1.2.1"}})을 쓸 수 있습니다. 목록형({"modules": ["sirsoft-board"]})은 그대로 「버전 무관」이고, 버전식이 아닌 옛 설명형 값은 「버전 무관」으로 받고 로그에 경고를 남깁니다. 연동 확장의 버전이 모자라도 설치·활성화는 막지 않습니다.
확장을 설치·활성화·업데이트하면, 함께 쓰면 연동되는 확장 중 설치되지 않았거나 꺼져 있거나 버전이 모자란 것을 결과 문구와 Artisan 표로 알려 줍니다. 확장을 끄거나 지우면 그 확장과 연동 중인 켜진 확장을 알려 줍니다. 관리자 API 응답에는 각각 integration_notices·integrated_by 로 실립니다. 응답 상태와 명령 종료 코드는 바뀌지 않습니다.
관리자 확장 목록·상세 응답이 연동 확장의 요구 버전·설치 버전·충족 여부와, 이 확장과 연동 중인 확장 목록(integrated_by)을 싣습니다.
관리자 설치 창 미리보기가 함께 쓰면 연동되는 확장 목록(integrations)을 냅니다. 설치 요청의 integrations 로 고른 모듈·플러그인은 본체보다 먼저 설치·활성화합니다. 연동 확장 설치가 실패해도 본체 설치는 계속하고 실패 사유를 알려 줍니다. 함께 처리할 권한이 없으면 의존 확장과 같이 거부합니다.
설치 창에서 고른 연동 확장이 다른 확장을 필수로 요구하면, 그 필수 확장도 연동 확장보다 먼저 함께 설치·활성화합니다(설치 창 미리보기의 연동 항목 requires, 설치 창에 「함께 설치」로 표시). 필요한 확장을 설치할 권한이 없거나 의존을 맞출 수 없으면 그 연동은 고를 수 없게 사유를 표시합니다(blocked_reason·blocked_by_permission). (#153@glitter-gim 님께서 제보해주셨습니다.)
설치 마법사의 확장 선택 단계에서 확장을 고르면, 그 확장이 연동으로 선언한 모듈·플러그인을 한 번 자동으로 체크하고 「연동」 배지로 표시합니다. 체크를 풀 수 있고, 푼 항목은 다시 체크하지 않으며 「다음」도 막지 않습니다. 연동 대상이 템플릿이면 안내만 합니다. 확장 카드에는 「연동: …」 줄이 보이고, 번들 버전이 연동 버전 제약에 못 미치면 「연동 버전 미달」을 표시합니다.
코어 업데이트의 번들 정리(core:bundled-sources prune)가 켜져 있는 확장의 연동 대상 번들을 지우지 않고 남깁니다.
확장 문서 생성기의 연동 표에 버전 제약 열을 더했습니다.
확장 개발 API: ModuleInterface·PluginInterface 에 getIntegrations()·getIntegrationConstraints() 를 더했습니다. AbstractModule·AbstractPlugin 이 구현하므로, 이를 상속하지 않고 인터페이스를 직접 구현한 확장만 두 메서드를 구현하면 됩니다. TemplateManagerInterface::getIntegrationConstraints() 와 저장소 인터페이스의 findActiveByIntegration() 도 더했습니다.
템플릿 엔진·라우트
레이아웃에서 모듈·플러그인이 켜져 있는지 판정하는 전역 상태 _global.activeModules·_global.activePlugins(식별자 배열)를 추가했습니다. 검색봇 화면도 같은 값을 씁니다. {{_global.activeModules?.includes('sirsoft-board') ?? true}} 로 가리면 이 값이 없는 옛 코어에서는 「켜짐」으로 읽힙니다(engine-v1.84.0).
routes.json 라우트에 requires({"modules": [...], "plugins": [...]})를 적으면, 적힌 확장이 모두 켜져 있을 때만 그 라우트를 화면·검색봇·사이트맵·정적 사본에 싣습니다. 형식이 틀리면 라우트를 남기고 로그에 경고를 남깁니다. 레이아웃 편집기는 꺼진 확장의 화면도 계속 편집할 수 있습니다.
layout:check-installed 가 routes.json requires 의 형식 오류(모르는 유형·목록이 아닌 값 등)를 표로 알려 줍니다(--json 은 route_requires_warnings). 경고라 종료 코드는 바뀌지 않습니다.
기본 사용자 템플릿(sirsoft-basic 1.3.0)이 위 두 기능으로 게시판·쇼핑몰·페이지 모듈과 우편번호 플러그인을 선택 연동으로 바꿔, 게시판만 또는 쇼핑몰만 켠 사이트에서도 쓸 수 있습니다. 이 단독 사용은 코어 7.1.4 이상에서 동작합니다.
확장이 요구하는 PHP 확장
모듈·플러그인이 composer.json 의 require.ext-*(필수)·suggest.ext-*(권장)로 필요한 PHP 확장을 선언할 수 있습니다. 필수 확장이 서버에 없으면 설치·활성화를 막고 무엇이 필요한지 알려 줍니다(관리자 API 는 422 php_extension_missing). 업데이트(코어 업데이트의 번들 일괄 업데이트 포함)·강제 활성화는 막지 않고 경고만 남깁니다. 권장 확장은 막지 않고 표시만 합니다. (#151@lyg-kaban 님께서 제보해주셨습니다.)
관리자 모듈·플러그인 목록 카드와 상세 창에 「PHP 확장 필요: 이름(미설치)」·「권장 PHP 확장: 이름(미설치)」 표시를, 환경설정 > 정보 탭에 「확장(모듈·플러그인)이 요구하는 모듈」 목록을 더했습니다. 관리자 API 응답에는 각각 php_extensions·php_extensions.requested_by 로 실립니다. 이미 켜진 확장은 확장이 서버에서 빠져도 자동으로 끄지 않고 이 표시로만 알립니다.
설치 마법사의 확장 선택 단계가 필수 PHP 확장이 없는 번들 확장을 고를 수 없게 하고, 권장 확장이 없으면 경고 문구를 보입니다. 저장할 때도 서버에서 다시 걸러 냅니다. 터미널 설치(php g7-install·php artisan core:install)도 같은 확장을 설치 대상에서 빼고 그 이름과 없는 PHP 확장을 알려 줍니다.
코어 업데이트(core:update)가 새 코어의 필수 PHP 확장이 서버에 없으면 업데이트를 시작하지 않습니다(--source·--zip 은 유지보수 모드 전, GitHub 다운로드는 받은 직후 받은 파일을 지우고 유지보수를 풉니다). 함께 갱신될 확장의 필수 확장이 없으면 경고로 알립니다. PHP zip 확장도 필수라, zip 확장이 없는 서버는 압축을 푼 폴더로 올리는 수동 업데이트(--source)도 zip 확장을 먼저 설치해야 합니다 — 압축 해제 오류 문구와 관리자 [코어 업데이트 안내] 창의 수동 업데이트 설명도 이에 맞춰 zip 확장 설치를 안내합니다.
확장 README 의 요구사항 표에 선언한 PHP 확장(필수·권장)이 표시됩니다.
설치
브라우저 없이 서버 터미널에서 설치할 수 있습니다(php g7-install, vendor 가 있으면 php artisan core:install 도 같습니다). 웹 설치 마법사와 같은 단계·같은 검사를 거치며, 질문에 답하는 대화형과 옵션·환경변수(G7_INSTALL_*)·설정 파일(--config)만으로 끝나는 비대화형(--no-interaction)을 모두 지원합니다. 비밀번호·토큰은 명령줄 인자로 받지 않고 환경변수·파일·숨김 입력으로만 받습니다.
자동화 도구가 결과를 가를 수 있도록 원인별 종료 코드(0 완료 · 1 설치 실패 · 2 입력 오류 · 3 이미 설치됨 · 4 요구사항 미충족 · 5 다른 설치와 충돌 · 6 중단 · 7 root 실행 거부)와 한 줄씩 읽는 JSON 진행 출력(--output=json)을 제공합니다. 이미 설치된 사이트에서는 --skip-if-installed 로 아무것도 하지 않고 성공으로 끝낼 수 있습니다.
중간에 멈춘 터미널 설치는 --resume 으로 이어 갑니다. 웹 마법사가 진행 중인 설치는 덮어쓰지 않으며(--takeover 로만 넘겨받고, 웹 작업이 실행 중이면 넘겨받지 않습니다), 터미널 설치를 브라우저 복구 링크로 가져갈 수도 없습니다.
root 로 실행할 때는 --web-user(웹서버 계정)를 지정해야 하며, 설치가 끝나면 쓰기가 필요한 폴더와 설정 파일의 소유권을 그 계정에 넘깁니다.
설치 가이드(INSTALL.md)에 Docker Compose 로 첫 기동 때 자동 설치하고, 다시 띄울 때는 설치를 건너뛰는 예시를 실었습니다.
설치된 사이트를 처음부터 다시 설치할 수 있습니다(php g7-install --reinstall --drop-existing-tables). vendor·확장·설정·업로드 파일은 각 옵션을 줄 때만 지우고, 설정을 남기면 기존 암호화 키를 이어 써 남은 설정을 계속 읽을 수 있습니다. 입력·DB 검사를 모두 통과한 뒤에만 지우고, 하나라도 못 지우면 설치를 시작하지 않습니다.
확장 설치·업데이트
module:install·plugin:install·template:install 에 --with-dependencies(필요한 의존 확장을 함께 설치·활성화)와 --with-language-packs(함께 설치할 언어팩) 옵션을 추가했습니다. 관리자 설치 창과 같은 순서·같은 규칙으로 설치합니다. --with-language-packs=all 은 지금 설치할 수 없는 언어팩(같은 언어의 코어 언어팩이 꺼져 있거나, 이미 설치된 대상 확장의 버전이 언어팩 요구보다 낮은 경우)을 사유와 함께 건너뛰며, 고른 언어팩 설치가 실패하면 확장 설치는 유지한 채 명령이 실패 종료 코드로 끝납니다.
운영
php artisan list 와 각 명령의 --help 에 보이는 코어 명령 설명이 사이트 언어(한국어·영어·일본어)로 나옵니다. 종전에는 언어와 상관없이 한국어로 고정돼 있었습니다.
메일을 보낼 수 없는 서버에서도 터미널에서 회원 비밀번호를 재설정할 수 있습니다(php artisan user:reset-password). 메일 재설정과 같은 비밀번호 정책을 거쳐 잠금을 풀고 모든 기기에서 로그아웃시키며 변경 알림을 보냅니다. 활동 로그에는 「시스템(CLI)」 재설정으로 남습니다. --generate 는 정책을 만족하는 비밀번호를 한 번만 보여 주고 다음 로그인 때 바꾸게 합니다.
환경설정 값을 터미널에서 조회·지정할 수 있습니다(php artisan settings:get·settings:set, 플러그인은 --plugin=, 모듈은 --module=). 관리자 화면 저장과 같은 검증 규칙과 저장 경로를 거치며, 민감한 값은 조회 때 가리고 지정 때 명령줄 인자로 받지 않습니다. 게시판 기본 권한처럼 이름에 점이 든 항목도 표에 보인 키 그대로 조회·지정할 수 있습니다. .env 가 정하는 값과 다른 값에서 계산되는 값은 지정할 수 없습니다.
콘텐츠 보안 정책 (CSP)
환경설정 > 보안에 「콘텐츠 보안 정책(CSP)」 카드를 추가했습니다. 켜면 사이트 화면 응답에 스크립트 출처 정책(script-src 'self' + 허용 호스트, worker-src·object-src·base-uri)을 붙입니다. 「점검만」은 차단하지 않고 위반을 서버 로그(storage/logs/csp-*.log)에 남기고, 「강제」는 허용하지 않은 스크립트와 인라인 스크립트를 실행하지 않습니다. 기본값은 「끔」입니다. (#158@glitter-gim 님께서 제보해주셨습니다.)
허용 호스트는 켜져 있는 확장이 선언한 외부 스크립트 호스트(trusted_script_hosts·템플릿 externals)를 자동으로 합치고, 운영자는 그 밖의 호스트만 더합니다. 카드에는 자동 포함 호스트와 실제로 붙을 정책 문자열이 보입니다. 따옴표·세미콜론·공백·unsafe-inline 같은 키워드·data:·*·http:// 는 입력할 수 없습니다.
.env 의 G7_CSP_MODE(off·report_only·enforce)가 있으면 관리자 설정보다 우선하고 카드의 모드 선택이 잠깁니다. 강제 모드로 관리자 화면이 막혔을 때 .env 를 고치고 php artisan config:clear 로 되돌릴 수 있습니다.
브라우저가 보내는 위반 보고를 받는 POST /api/csp-report 를 추가했습니다(인증 없음, 분당 60회·16 KB 제한, 주소의 쿼리 문자열은 지우고 기록).
웹서버에서 직접 정책을 거는 경우의 권장 설정과 결제사 SDK 허용 호스트 표는 docs/backend/content-security-policy.md 에 정리했습니다.
Changed
모듈·플러그인을 설치·활성화할 때 의존 확장의 버전 조건까지 확인합니다. 종전에는 템플릿이 설치 때만 버전을 확인해, 지원하지 않는 옛 버전의 의존 확장 위에서도 설치·활성화가 진행됐습니다(--force 로만 넘어갈 수 있습니다). 강제 활성화 창은 버전이 모자란 의존 확장을 「버전 미달」로 표시하고, module:activate·plugin:activate 도 「버전 미달 설치 → 요구」로 출력합니다. 설치 창은 이미 설치됐지만 버전이 모자란 의존 확장을 함께 설치 대상으로 고를 수 없게 막고(먼저 업데이트해야 하므로), 함께 설치한 의존 확장이 켜지지 못한 사유는 「버전 미달 1.2.2 → >=1.3.0」처럼 보여 줍니다.
확장 설치 미리보기가 의존 확장이 다시 필요로 하는 확장(2단계 의존)까지 함께 보여 줍니다.
검색봇용 화면 캐시와 캐시 통계에 저장하는 모듈 화면의 레이아웃 이름이 레이아웃 편집기에 보이는 전체 이름(예: gnuboard7-hello_module.user_memo_list)으로 바뀌었습니다. 모듈 화면의 캐시를 지울 때는 php artisan seo:clear --layout= 와 관리자 캐시 비우기에 이 전체 이름을 씁니다(접두사를 뗀 이름으로는 더 지워지지 않습니다). 템플릿 화면은 원래 점 없는 이름이라 바뀌지 않습니다. 검색봇 렌더 훅(core.seo.filter_context 등)의 컨텍스트에는 기존 layoutName 을 그대로 두고 전체 이름 storedLayoutName 을 더했습니다. (#154@eondcom 님께서 제보해주셨습니다.)
검색봇 화면을 만들 레이아웃을 찾지 못하면 남기던 로그를 경고(warning)에서 오류(error)로 올리고, 레이아웃 전체 이름·모듈/플러그인 식별자·요청 주소·템플릿을 함께 적습니다. 로그 수준을 error 로 둔 사이트에서도 흔적이 남습니다. (#154@eondcom 님께서 제보해주셨습니다.)
PHP 의존성 패키지를 최신 안정 버전으로 갱신했습니다 (Laravel 12.70.2, Symfony 7.4.22, Carbon 3.15, Guzzle 7.16, phpdotenv 5.8 등 — 메이저 상승 없음). Composer 를 실행할 수 없는 환경을 위한 코어와 이커머스 모듈의 vendor-bundle.zip 도 같은 구성으로 다시 만들었습니다. 코어 업데이트가 vendor/ 를 새 구성으로 교체합니다.
.env 에 닫는 따옴표가 빠진 값(예: MAIL_FROM_NAME="그누보드)이 있으면 이제 사이트가 시작되지 않고 「Encountered a missing closing quote」 오류를 냅니다. 종전에는 그 줄부터 아래 설정이 아무 표시 없이 무시됐습니다. 설치 마법사·환경설정이 쓴 값은 해당하지 않으며, .env 를 직접 고친 사이트는 코어 업데이트 전에 따옴표 짝을 확인하세요.
Fixed
콘텐츠 보안 정책 (CSP) 호환
웹서버에 Content-Security-Policy: script-src 'self'(unsafe-inline 없음)를 걸면 관리자·사용자 화면이 하얗게 비던 문제를 고쳤습니다. 화면 틀이 쓰던 인라인 스크립트를 정적 파일과 비실행 데이터 블록으로 옮겼고, 화면 구성 파일을 불러오지 못했을 때의 안내·새로고침 단추도 같은 정책에서 동작합니다. (#158@glitter-gim 님께서 제보해주셨습니다.)
같은 정책에서 오류 화면·레이아웃 로드 실패 화면의 「새로고침」 단추가 눌리지 않던 문제를 고쳤습니다.
같은 정책에서 설치 마법사의 단추·언어 선택·다크 모드·단계 이동이 멈추던 문제와, 미설치 사이트 첫 화면이 설치 화면으로 넘어가지 않던 문제를 고쳤습니다. 미설치 첫 화면은 이제 스크립트 없이 설치 화면으로 이동합니다.
의존 확장 확인
의존 확장이 요구 버전에 못 미치는데도 템플릿이 켜지고, 켜진 직후 사이트 전체가 「서비스 일시 중단(503)」이 되던 문제를 고쳤습니다. 이제 활성화가 막히고, 강제 활성화 창에서 버전 미달(설치 → 요구 버전)과 강제로 켜면 화면이 503 이 된다는 안내를 보여 줍니다. 관리자 템플릿이면 되돌리는 명령을 함께 알려 줍니다. (#152@glitter-gim 님께서 제보해주셨습니다.)
템플릿 설치·활성화·사이트 503 판정·관리자 목록의 「의존 충족」 표시가 같은 기준으로 판정합니다. 종전에는 경로마다 따로 판정해, 같은 템플릿이 목록에서는 「미충족」인데 활성화는 통과하는 식으로 엇갈렸습니다.
의존 확장을 업데이트하는 동안 사이트가 잠깐 503 이 되던 문제를 고쳤습니다.
관리자 대시보드의 「복구」(호환성 복구) 단추가 의존 확장이 맞지 않거나 확장이 활성화를 거부해 켜지 못했는데도 「복구되었습니다」라고 알리던 문제를 고쳤습니다. 이제 실패와 그 사유를 보여 줍니다.
사이트 주소 외의 접속을 막는 허용 도메인이 켜진 사이트에서, 권한이 있는 관리자(최고관리자 포함)에게도 대시보드 경고의 [복구]·[닫기] 단추가 「이 경고를 처리할 권한이 없습니다」로 잠기던 문제를 고쳤습니다.
template:activate 가 켜져 있지만 버전이 모자란 의존 확장을 「비활성」으로 출력하던 문제를 고쳤습니다. 「버전 미달 설치 → 요구」로 보여 주고, 강제로 켜면 화면이 503 이 된다는 안내를 덧붙입니다.
의존 대상 확장을 강제 업데이트·재설치(install --force)·강제 비활성화하거나 멈춘 업데이트를 정리해 판이 내려가거나 꺼지면, 그 확장을 요구하던 켜진 템플릿·모듈·플러그인이 더는 조건을 맞추지 못한다는 안내를 결과 창·터미널에 보여 줍니다. 작업은 막지 않습니다. 결과 창의 그 안내는 대상 칸에 식별자 대신 확장 이름을 보입니다.
확장 설치·업데이트
manifest dependencies 를 목록형({"modules": ["sirsoft-board"]})으로 쓴 확장에서 의존 확장 식별자를 0·1 로 읽어, 의존 검사·의존 템플릿 찾기·번들 일괄 업데이트 순서·확장 문서 의존 표가 틀리던 문제를 고쳤습니다. 설치 마법사도 같은 문제를 고쳤습니다.
설치 마법사가 같은 유형의 확장(모듈끼리·플러그인끼리)을 의존 대상보다 먼저 설치해 실패할 수 있던 문제를 고쳤습니다. 저장할 때 의존 순서로 정렬합니다.
관리자 설치 창에서 의존 확장을 함께 설치할 때 의존 확장의 활성화가 실패해도 그 사유를 버리고 「의존 확장이 활성 상태가 아님」이라는 엉뚱한 오류로 끝나던 문제를 고쳤습니다. 이제 실제 실패 사유를 보여 줍니다. 의존 확장을 함께 설치·활성화한 뒤 본 확장 설치가 실패하면, 되돌리지 않고 남겨 둔 의존 확장 이름도 실패 안내(관리자 화면·터미널)에 함께 보여 줍니다.
의존 확장이 다시 다른 확장을 필요로 하면(2단계 의존) 함께 설치하지 못하던 문제, 화면에서 고른 vendor 설치 방식이 의존 확장에는 적용되지 않던 문제를 고쳤습니다.
관리자 설치 창에서 설치돼 있지만 꺼져 있는 의존 확장이 기본으로 체크되지 않아, 기본값 그대로 설치하면 의존 확장이 꺼져 있다는 오류로 반드시 실패하던 문제를 고쳤습니다. 이제 꺼진 의존 확장도 기본으로 체크되어 함께 켠 뒤 설치를 마칩니다.
의존 확장을 함께 설치하다 실패했을 때 안내에 확장 종류가 영어(module·plugin)로 나오던 문제를 고쳤습니다. 이제 「모듈」·「플러그인」으로 표시합니다.
확장 설치 창에서 지금 설치할 수 없는 언어팩(같은 언어의 코어 언어팩이 꺼져 있거나, 이미 설치된 대상 확장의 버전이 언어팩 요구보다 낮은 경우)이 기본으로 체크되어, 기본값 그대로 설치하면 언어팩 설치 실패 안내가 뜨던 문제를 고쳤습니다. 이제 그런 언어팩은 체크할 수 없고 사유가 함께 보입니다.
여러 확장을 한 번에 처리할 때의 의존 순서
설치 마법사(웹·터미널)에서 모듈이 플러그인에 의존하거나 관리자 템플릿이 모듈·플러그인에 의존하면, 의존 대상보다 먼저 설치하려다 데이터베이스 작업을 마친 뒤 확장 설치 단계에서 멈추던 문제를 고쳤습니다. 이제 종류와 관계없이 의존 대상을 먼저 설치하고, 그래서 앞당겨 설치하는 항목에는 설치 진행 화면에 어느 확장에 필요한지(예: 「페이지에 필요」)를 표시합니다. 언어팩은 이름 순서와 관계없이 코어 언어팩을 먼저 설치합니다. (#153@glitter-gim 님께서 제보해주셨습니다.)
설치 마법사에서 고른 확장끼리 서로를 의존하는 순환(모듈 → 플러그인 → 모듈 등)이 있으면, 종전에는 사전 검증을 통과한 뒤 설치 도중에 실패했습니다. 이제 데이터베이스 작업 전에 순환 경로와 함께 멈추고, 실패 화면의 「Step 4 로 돌아가기」로 확장 선택 단계에 바로 돌아갈 수 있습니다. (#153@glitter-gim 님께서 제보해주셨습니다.)
설치 마법사의 실패 화면 제목에 작업 이름 대신 task_module_install:sirsoft-page 같은 키가 그대로 보이던 문제를 고쳤습니다(호환성 모드로 진행했거나 실패 화면을 다시 열었을 때). (#153@glitter-gim 님께서 제보해주셨습니다.)
설치 마법사의 의존성 사전 검증이 실패하면 데이터베이스를 건드리기 전인데도 「롤백에 실패했습니다 — 데이터베이스를 수동으로 정리」 안내가 붙던 문제, 실패 화면에서 확장 선택 단계로 돌아가면 고른 확장이 모두 풀리던 문제를 고쳤습니다. 실패 화면에서 확장 선택 단계나 설정으로 돌아갈 때의 확인 창과 설치 로그에도 데이터베이스 수동 정리 안내가 붙지 않습니다(데이터베이스 작업을 시작한 뒤 실패한 경우는 종전대로 안내합니다). (#153@glitter-gim 님께서 제보해주셨습니다.)
코어 업데이트 뒤 번들 일괄 업데이트에서 모듈이 플러그인에 의존하면 모듈을 먼저 갱신해, 옛 플러그인 기준으로 「의존 버전 미달」 경고가 나거나 업그레이드 단계가 새 플러그인 코드를 찾지 못해 실패할 수 있던 문제를 고쳤습니다. 이제 모듈·플러그인을 서로의 의존 순서로 갱신합니다(서버가 새 프로세스를 띄우지 못하는 환경 포함 — 이 경우 같은 종류 안의 순서도 맞춥니다). (#153@glitter-gim 님께서 제보해주셨습니다.)
코어 업데이트 뒤 호환되지 않는 확장을 자동으로 끌 때, 아직 켜진 다른 확장(템플릿 등)이 의존해 먼저 끄지 못한 확장이 그 의존 확장이 꺼진 뒤에도 같은 요청에서는 남을 수 있던 문제를 고쳤습니다. 이제 막혔던 확장을 같은 요청에서 다시 끄고 알림도 한 번에 보냅니다. (#153@glitter-gim 님께서 제보해주셨습니다.)
php artisan language-pack:provision 을 처음 쓰는 언어로 실행하면 코어 언어팩만 설치하고 확장 언어팩은 전부 건너뛰어, 두 번 실행해야 끝나던 문제를 고쳤습니다. (#153@glitter-gim 님께서 제보해주셨습니다.)
의존 확장이 없거나 꺼져 있을 때 오류 문구가 플러그인도 「모듈」로 부르던 문제(예: 「의존하는 "sirsoft-ckeditor5" 모듈이…」), 템플릿의 의존 오류 문구에 종류가 영어(module)로 나오던 문제를 고쳤습니다. 이제 「의존하는 플러그인 "sirsoft-ckeditor5"이(가)…」처럼 실제 종류를 표시합니다. (#153@glitter-gim 님께서 제보해주셨습니다.)
주소 끝 슬래시
nginx 에서 주소 끝에 / 를 붙이거나(/admin/) 중간에 / 를 겹쳐 쓰면(/board//free) 화면은 뜨지만 HTTP 404 가 나가고 검색봇이 빈 화면을 받던 문제를 고쳤습니다. 이런 주소는 슬래시를 정리한 주소로 301 이동합니다(쿼리 보존). 화면 안 링크로 이동할 때도 주소창을 같은 형태로 맞춥니다. (#156@glitter-gim 님께서 제보해주셨습니다.)
템플릿 엔진
오류 화면(404 등)이 레이아웃 데이터 소스의 if 조건을 보지 않고 모두 불러, 조건으로 꺼 둔 데이터 소스까지 요청하던 문제를 고쳤습니다(engine-v1.84.0).
검색 엔진 노출
검색봇 화면에서 목록?.includes(…) 식이 늘 참으로 평가되거나, includes(…) ?? 기본값 이 켜진 모듈에서도 거짓이 되던 문제를 고쳤습니다.
검색봇 화면이 값 자리의 +(문자열 잇기·덧셈)·||·&&·비교·산술을 풀지 못해 빈 값이 되거나, ?? 가 빈 문자열을 없는 값으로 보던 문제를 브라우저와 같게 고쳤습니다. Math.min·Math.max 에 인자가 하나뿐이면 오류가 나던 문제와 filter(Boolean) 미지원도 고쳤습니다.
seo-config 의 fields 항목 attrs 에 {값}/경로 처럼 값과 글자를 섞어 쓰면 중괄호가 그대로 링크에 나가던 문제를 고쳤습니다.
모듈·플러그인을 끈 뒤 사이트맵을 다시 만들어도 그 확장의 주소(쇼핑몰 상품·페이지 등)가 사이트맵에 남던 문제를 고쳤습니다. 다시 켜면 그 주소가 곧바로 돌아옵니다.
게시판 정보를 고치면 검색봇용 화면 캐시가 다른 게시판 화면까지 지워지던 문제, 게시판 이름을 바꿔도 글 상세 화면 캐시가 옛 이름으로 남을 수 있던 문제, 글을 고쳐도 목록 2쪽 이후 캐시가 남던 문제를 고쳤습니다.
사이트맵(sitemap.xml)에 홈(/)을 비롯한 정적 화면이 하나도 실리지 않던 문제를 고쳤습니다. 활성 템플릿을 조회하는 부분이 오류로 끝나고 경고 한 줄만 남기고 있었습니다. 이제 검색봇 화면과 같은 기준으로 정적 화면을 싣습니다 — 로그인 필요·비로그인 전용 화면과 경로에 변수(:id)가 있는 화면은 빼고, 레이아웃의 meta.seo.enabled 가 켜져 있고 toggle_setting 설정이 켜진 화면만 넣습니다(404·403·500 오류 화면과 본인인증 화면은 빠집니다). 우선순위·갱신주기는 레이아웃의 meta.seo.priority·changefreq 를 씁니다. 같은 주소를 정적 화면과 확장(기여자)이 함께 내도 사이트맵에는 한 번만 적습니다 — 홈을 싣기 위해 임시로 등록한 기여자는 지워도 되고, 남겨 두어도 중복되지 않습니다. (#155@eondcom 님께서 제보해주셨습니다.)
사이트맵을 만들다 기여자·확장 상태 조회·정적 화면 수집·레이아웃 읽기에서 난 오류를 경고(warning)로만 남겨 오류 로그 수준에서는 보이지 않던 문제를 고쳤습니다. 이제 오류로 기록하고 나머지 주소 생성은 계속합니다.
큐를 즉시 실행(sync)으로 쓰는 환경에서 환경설정 > SEO 탭의 사이트맵 「지금 생성」을 누르면 생성이 끝났는데도 최대 1시간 동안 버튼이 「생성 중…」으로 잠기던 문제를 고쳤습니다. 이제 즉시 실행 큐면 「Sitemap 재생성이 완료되었습니다」로 바로 끝나고, 응답의 마지막 생성 시각도 방금 생성한 시각으로 돌려줍니다. 다른 생성이 진행 중이면 「이미 생성하고 있습니다」, 큐에서 기다리는 생성이 있거나 큐 작업자가 강제 종료돼 이전 작업의 잠금만 남아 있으면 「다른 Sitemap 생성 작업이 대기 중이거나 이전 작업의 잠금이 남아 있습니다」로 거절하고(409), 진행 상태를 「대기」로 덮어쓰지 않습니다.
모듈이 제공하는 사용자 화면(라우트의 레이아웃이 모듈식별자.레이아웃 인 화면)을 검색봇이 요청하면 레이아웃을 찾지 못해 제목·설명·본문 없는 일반 화면이 나가던 문제를 고쳤습니다. 모듈 레이아웃 이름에서 식별자를 뗀 이름이 템플릿 레이아웃 이름과 같으면 템플릿 화면이 그 주소로 대신 나가던 문제도 함께 고쳤습니다. (#154@eondcom 님께서 제보해주셨습니다.)
검색봇 캐시 통계에 모듈 식별자가 기록되지 않아 seo:stats 와 관리자 통계의 모듈별 묶음이 늘 비던 문제를 고쳤습니다. (#154@eondcom 님께서 제보해주셨습니다.)
모듈 설정의 SEO 제목·설명 틀을 다국어로 저장한 모듈 화면이 검색봇에 오류로 일반 화면을 내던 문제를 고쳤습니다(플러그인 설정도 같음). (#154@eondcom 님께서 제보해주셨습니다.)
레이아웃 편집기에서 레이아웃을 저장하거나 이전 버전으로 복원해도 검색봇용 화면 캐시가 남아, 캐시가 만료될 때까지 옛 제목·설명이 검색봇에 나가던 문제를 고쳤습니다. 공통 레이아웃(_user_base 등)을 고치면 그것을 물려받는 화면의 캐시도 함께 비웁니다. 레이아웃 파일 생성·이름 변경·삭제와 레이아웃 확장(오버레이) 변경도 같습니다. (#154@eondcom 님께서 제보해주셨습니다.)
제목 접미사(SEO 설정)를 비워 둔 사이트에서 검색봇이 받는 홈 화면 제목이 비어 나가던 문제를 고쳤습니다. 다른 제목이 없고 접미사도 비었으면 사이트 이름을 제목으로 씁니다. 접미사가 있는 사이트는 종전과 같습니다. (#154@eondcom 님께서 제보해주셨습니다.)
레이아웃 편집기의 검색봇 미리보기가 모듈 화면을 미리 볼 때 모듈 정보를 보내지 않아 모듈 설정 기반 제목 틀 등이 빠지고, 화면 제목 대신 사이트 이름이나 빈 제목을 보여 주어 실제 검색봇 응답과 다르게 보이던 문제를 고쳤습니다. 미리보기도 그 화면 라우트의 제목을 실제 응답과 같은 기준으로 씁니다(engine-v1.84.0). (#154@eondcom 님께서 제보해주셨습니다.)
PHP 확장 요구 목록·선택 확장
설치 마법사·관리자 환경설정 > 정보·요구사항 문서의 필수/선택 PHP 확장 목록이 서로 달라, 관리자 정보 탭은 bcmath 를 필수로, 문서는 선택으로 안내하던 문제를 고쳤습니다. 이제 한 목록에서 나오며 bcmath 는 선택입니다. (#151@lyg-kaban 님께서 제보해주셨습니다.)
설치 마법사가 필수 확장인 zip·pdo_mysql 을 검사하지 않아, 없는 서버에서 설치 도중에 실패하던 문제를 고쳤습니다. 이제 요구사항 단계에서 알려 줍니다.
PHP zip 확장이 없는 서버에서 관리자 환경설정 [백업]·[복원]이 서버 오류(500)로 끝나던 문제를 고쳤습니다. 이제 zip 확장이 필요하다고 안내합니다.
PHP iconv 함수가 없는 서버에서 레이아웃 편집기의 검색봇 미리보기가 서버 오류로 열리지 않던 문제를 고쳤습니다.
PHP zlib 이 없는 서버에서 「사이트맵 gzip 압축」을 켜면 사이트맵 생성이 매번 실패하던 문제를 고쳤습니다. 이제 저장할 때 zlib 이 필요하다고 안내하고, 이미 켜져 있으면 압축 없이 만듭니다.
PHP zlib·phar 가 없는 서버에서 GeoIP 데이터베이스 [갱신]이 「Call to undefined function…」 같은 알 수 없는 메시지로 실패하던 문제를 고쳤습니다. 이제 필요한 확장을 알려 줍니다.
GeoIP 데이터베이스 [갱신]이 실패하면 안내 제목에 settings.geoip.license_key_missing 같은 키가 그대로 보이던 문제를 고쳤습니다. 이제 「MaxMind 라이선스 키가 설정되지 않았습니다」처럼 사유를 문장으로 보여 줍니다.
설치 마법사
설치 마법사를 영어로 진행해도 사이트 기본 언어가 한국어로 저장되던 문제를 고쳤습니다(실시간 진행 모드). 이제 고른 언어가 사이트 기본 언어와 .env 에 같게 들어갑니다.
리눅스·macOS 서버에서 마이그레이션이 실패했을 때 자동 되돌리기가 실행되지 않고 「수동 정리 필요」로 끝나던 문제를 고쳤습니다.
설치 작업 중 PHP 내부 오류가 나면 진행 상태가 「실행 중」으로 멈춰 다시 시도할 수 없던 문제를 고쳤습니다. 이제 실패로 기록되고 재시도할 수 있습니다.
웹 설치 마법사에서 확장을 많이 고르면(모두 고르면 10분 넘게 걸림) 설치 작업이 「Maximum execution time of 600 seconds exceeded」 로 중간에 끊기고, 호환(폴링) 모드에서는 화면이 「설치 진행 중」에 멈춰 설치를 끝낼 수 없던 문제를 고쳤습니다. 이제 작업마다 실행 시간 제한을 다시 걸어 전체 설치 시간에는 제한이 없습니다. 작업 하나가 제한을 넘어 끊기더라도 화면이 「설치 작업 프로세스가 중간에 종료되었습니다」 실패 안내와 「재시도」를 보이고, 재시도하면 끊긴 작업부터 이어서 설치합니다. 실시간 스트리밍 모드에서 연결만 끊긴 경우에는 화면이 새 연결로 남은 작업을 이어 받습니다. 웹 서버의 요청 시간 제한 권장값은 INSTALL.md 「설치 트러블슈팅」에 정리했습니다.
요구사항 미충족, 사이트 이름·관리자 이름 누락, 테이블 접두사 형식, 쓸 수 없는 vendor 설치 방식, 없는 확장·의존 누락·버전 미달 선택을 화면에서만 막고 서버는 받아들이던 문제를 고쳤습니다. 화면이 자동으로 채우던 의존 확장 선택도 서버가 같은 규칙으로 채웁니다.
하위 폴더(예: /g7)에 설치하면 완료 직후 .env 확정 요청이 엉뚱한 주소로 가 설정이 확정되지 않던 문제, 실행 중인 설치 화면을 다시 열 때 진행 상태를 읽지 못하던 문제를 고쳤습니다.
설치가 끝난 뒤에도 설정 캐시에 설치 전 값(사이트 주소 http://localhost, 설치 미완료 표시)이 남을 수 있던 문제를 고쳤습니다. .env 를 확정한 뒤 캐시를 다시 만듭니다. 호환 모드(폴링)로 설치하면 마지막 정리 작업(설정·주소 연결 캐시 생성, 초기 화면 파일 게시)이 끝나기 전에 완료 처리되어 이 작업들이 중간에 멈추던 문제도 함께 고쳐, 이제 모두 끝난 뒤에 완료됩니다. 이 정리 작업 중에 설치 화면을 새로고침해도 「설치가 완료되어 인스톨러를 쓸 수 없다」는 응답으로 멈추지 않고, 설치를 진행하던 브라우저에서는 정리 작업이 끝날 때까지 기다렸다가 완료 화면을 열고 .env 를 확정합니다.
기존 테이블 정리 로그에 삭제 개수 대신 {count} 가 그대로 보이던 문제를 고쳤습니다.
Composer 가 없는 서버에서 의존 패키지 설치 방식을 「자동」(기본)으로 두면, 「번들 vendor 로 자동 전환 예정」이라고 안내한 뒤에도 Composer 를 실행하려다 설치가 실패하던 문제를 고쳤습니다. 이제 포함된 번들 vendor 로 자동 전환해 설치를 이어 갑니다.
환경설정
php artisan settings:install 을 실행한 뒤에도 캐시가 옛 설정 값을 돌려주던 문제를 고쳤습니다.
.env 로 정한 설정 값을 단건 저장 요청이 덮어쓸 수 있던 문제를 고쳤습니다. 이제 그런 키의 단건 저장은 거부합니다(탭 저장은 종전처럼 그 키를 건너뜁니다).
방문자 통계
같은 방문자가 탭을 여러 개 열거나 화면을 빠르게 이동할 때 방문 기록이 버려지고, 관리자 > 방문자 통계 수집 진단의 「최근 적재 실패」에 「DB 오류(Deadlock found …)」가 반복해 쌓이던 문제를 고쳤습니다. 이제 동시에 들어온 요청이 서로 막히면 다시 시도해 같은 방문으로 기록합니다.
화면 크기를 알려 주지 않는 브라우저(앱 안 웹뷰·헤드리스 브라우저 등)로 들어온 방문이 화면 해상도 형식 오류로 방문자 통계에서 빠지던 문제를 고쳤습니다. 이제 방문은 기록되고 해상도만 비워 두며, 해상도 보고서에 「0x0」 같은 항목이 생기지 않습니다(engine-v1.84.0).
방문 통계에서 같은 페이지가 끝 슬래시 유무로 두 줄로 나뉘지 않게 했습니다.
Security
코어 화면 틀·오류 화면·설치 마법사와 번들 결제·본인인증 플러그인이 내는 화면에서 실행형 인라인 스크립트와 onclick 같은 이벤트 속성을 없앴습니다. 이제 'unsafe-inline' 없이 script-src 허용 목록만으로 정책을 걸어, 주입된 인라인 스크립트가 실행되지 않게 막을 수 있습니다(Added 「콘텐츠 보안 정책 (CSP)」·Fixed 「콘텐츠 보안 정책 (CSP) 호환」 참고). (#158@glitter-gim 님께서 제보해주셨습니다.)
메일 헤더 라이브러리(symfony/mime)를 7.4.22 로 올려, 헤더 파라미터 이름에 허용되지 않는 문자를 넣어 메일 헤더를 주입할 수 있던 취약점(CVE-2026-45070)을 해소했습니다.