확장이 SP Kernel 미들웨어 그룹을 직접 조작하거나 라우트 파일에 미들웨어 FQCN 을 직접 부착하던 임시 방식을, 확장이 부착 대상(targets)을 명시 선언하고 코어가 요청 시점에 라우트명·URI 로 매칭해 실행하는 self-gate 로 정규화. 번들 7건(ecommerce/gdpr/pay 3종) 이전, audit 룰 3건·문서·버전 동기화 동반. 부수적으로 generate-skills.cjs 가 재생성 시 신의성실(부수의무) 섹션을 소실 시키던 회귀를 수정 — 스킬별 불릿을 스크립트 SSoT 로 편입해 재생성에도 보존.
9.2 KiB
9.2 KiB
모듈 라우트 규칙
이 문서는 모듈의 백엔드 라우트(API/Web)와 프론트엔드 라우트(routes.json) 작성 규칙을 설명합니다.
TL;DR (5초 요약)
1. URL prefix 자동: /api/modules/[vendor-module]/...
2. name prefix 자동: api.modules.[vendor-module]....
3. 활성화된 모듈만 라우트 등록됨
4. 프론트엔드: routes/admin.json, routes/user.json으로 admin/user 분기
5. 레거시 routes.json은 admin으로 폴백 (경고 로그 출력)
목차
1. 핵심 원칙
중요: ModuleRouteServiceProvider가 URL prefix와 name prefix를 자동 적용
✅ 필수: 모듈 개발자는 모듈 내부 구조만 정의
✅ 조건: 활성화된 모듈만 라우트가 등록됨
2. 라우트 등록 조건
활성화된 모듈만 라우트 등록
ModuleRouteServiceProvider가 DB에서status = 'active'인 모듈만 조회- 설치되지 않거나 비활성화된 모듈의 라우트는 등록되지 않음
- 모듈 활성화/비활성화 시 라우트가 자동으로 등록/해제됨
네임스페이스 변환 규칙
디렉토리명 → 네임스페이스
sirsoft-sample → Modules\Sirsoft\Sample
sirsoft-ecommerce → Modules\Sirsoft\Ecommerce
vendor-my-module → Modules\Vendor\My\Module
3. 자동 적용되는 Prefix
API 라우트
| 항목 | 값 |
|---|---|
| URL prefix | api/modules/{module-name} |
| Name prefix | api.modules.{module-name}. |
웹 라우트
| 항목 | 값 |
|---|---|
| URL prefix | modules/{module-name} |
| Name prefix | web.modules.{module-name}. |
라우트 이름(
{web\|api}.modules.{module-name}.{name})은 확장 미들웨어 타게팅의 1급 키다.Module::getMiddleware()의targets는 이 라우트명 패턴으로 미들웨어 부착 대상을 지정한다. 상세: docs/backend/middleware.md "확장 미들웨어 선언 (self-gate)".
4. 라우트 파일 작성
라우트 파일 위치
modules/_bundled/{identifier}/src/routes/
├── api.php # API 라우트
└── web.php # 웹 라우트
✅ DO: 모듈 내부 구조만 정의
// modules/_bundled/sirsoft-ecommerce/src/routes/api.php
Route::prefix('admin')->middleware(['auth:sanctum', 'admin'])->group(function () {
Route::get('products', [ProductController::class, 'index'])
->name('admin.products.index');
// 최종 Name: api.modules.sirsoft-ecommerce.admin.products.index
// 최종 URL: /api/modules/sirsoft-ecommerce/admin/products
});
❌ DON'T: prefix 중복 입력
// 잘못된 예시 - prefix가 중복됨
Route::get('products', [ProductController::class, 'index'])
->name('api.modules.sirsoft-ecommerce.admin.products.index');
라우트 파일 주석 템플릿
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| [Module Name] API Routes
|--------------------------------------------------------------------------
|
| 주의: ModuleRouteServiceProvider가 자동으로 prefix를 적용합니다.
| - URL prefix: 'api/modules/{module-name}'
| - Name prefix: 'api.modules.{module-name}.'
|
*/
// 라우트 정의...
5. 라우트 네임 컨벤션
구조
{type}.modules.{module-name}.{area}.{resource}.{action}
예시
api.modules.sirsoft-ecommerce.admin.products.index
api.modules.sirsoft-ecommerce.admin.products.store
api.modules.sirsoft-ecommerce.admin.orders.show
web.modules.sirsoft-ecommerce.checkout.cart
구성 요소
| 구성 요소 | 설명 | 적용 방식 |
|---|---|---|
type |
api 또는 web | 자동 적용 |
modules |
모듈임을 나타냄 | 자동 적용 |
module-name |
모듈 식별자 | 자동 적용 |
area |
admin, public 등 영역 | 개발자 정의 |
resource |
리소스명 | 개발자 정의 |
action |
index, store, show, update, destroy 등 | 개발자 정의 |
6. 프론트엔드 라우트 (routes.json)
중요: 백엔드 라우트(routes/api.php)와 별개로 프론트엔드 라우트도 지원
✅ 위치: modules/{identifier}/resources/routes/admin.json, routes/user.json
✅ 병합: 템플릿 타입(admin/user)에 맞는 모듈 routes만 필터링 병합
✅ 레거시: routes.json은 admin으로 폴백 (경고 로그 출력)
✅ 캐시: 모듈 활성화/비활성화 시 자동 무효화
디렉토리 구조
modules/{identifier}/resources/
├── routes/
│ ├── admin.json ← admin 템플릿 전용 라우트
│ └── user.json ← user 템플릿 전용 라우트 (선택)
└── routes.json ← [레거시] 있으면 admin으로 취급 + 경고 로그
admin/user 분기 규칙
| 라우트 파일 | 서빙 대상 | 설명 |
|---|---|---|
routes/admin.json |
admin 타입 템플릿 | 관리자 화면 라우트 |
routes/user.json |
user 타입 템플릿 | 일반 사용자 화면 라우트 |
routes.json (레거시) |
admin 타입 템플릿만 | 하위 호환성, 경고 로그 출력 |
탐색 우선순위:
routes/{templateType}.json우선 탐색- (admin 타입만)
routes.json레거시 폴백 - user 타입은 레거시
routes.json을 로드하지 않음
기본 구조
{
"version": "1.0.0",
"routes": [
{
"path": "*/admin/sample",
"layout": "admin_sample_index",
"auth_required": true,
"meta": {
"title": "$t:sirsoft-sample.admin.index.title"
}
}
]
}
병합 방식
병합 우선순위:
- 템플릿 routes 배열이 기본
- 활성화된 모듈 routes를 템플릿 타입에 맞게 필터링하여 병합 (
array_merge) - 플러그인 routes는 admin 템플릿에만 포함 (settings 페이지 등 admin 전용)
- version은 템플릿의 version 사용
레이아웃 필드 자동 변환:
- 모듈 routes의
layout필드에 모듈 식별자 접두사 자동 추가 - 예:
"admin_sample_index"→"sirsoft-sample.admin_sample_index"
최종 API 응답:
{
"version": "1.0.0",
"routes": [
// 템플릿 routes...
{
"path": "*/admin/sample",
"layout": "sirsoft-sample.admin_sample_index",
"auth_required": true,
"meta": {
"title": "$t:sirsoft-sample.admin.index.title"
}
}
// 다른 모듈 routes (템플릿 타입에 맞는 것만)...
]
}
모듈 레이아웃과의 연계
모듈 레이아웃 정의:
modules/{identifier}/resources/layouts/admin/{layout_name}.json
modules/{identifier}/resources/layouts/user/{layout_name}.json
routes에서 레이아웃 참조:
{
"path": "*/admin/sample",
"layout": "admin_sample_index"
}
레이아웃 조회 우선순위:
- 템플릿 오버라이드:
templates/{template}/layouts/overrides/{module}/{layout}.json - 모듈 원본:
modules/{module}/resources/layouts/admin/{layout}.json
다국어 키 사용
routes.json에서 다국어 참조:
{
"meta": {
"title": "$t:sirsoft-sample.admin.index.title"
}
}
모듈 다국어 파일:
// modules/sirsoft-sample/resources/lang/ko.json
{
"admin": {
"index": {
"title": "샘플 모듈 관리"
}
}
}
프론트엔드에서 최종 키:
- 템플릿 다국어 API가 모듈 다국어 자동 병합
- 최종 키:
sirsoft-sample.admin.index.title
구현 세부사항
TemplateService:
// 템플릿 + 모듈 routes 병합 (템플릿 타입에 따라 필터링)
public function getRoutesDataWithModules(string $identifier): array
// 템플릿 타입에 맞는 모듈 routes 로드 (routes/{type}.json 우선, 레거시 폴백)
private function loadActiveModulesRoutesData(string $templateType = 'admin'): array
PublicTemplateController API:
GET /api/public/templates/{identifier}/routes
ClearsTemplateCaches Trait:
protected function clearAllTemplateRoutesCaches(): void
7. 캐시 관리
캐시 키 패턴
template.routes.{identifier}
자동 캐시 무효화
| Manager | 메서드 | 동작 |
|---|---|---|
| ModuleManager | activateModule() |
routes 캐시 무효화 |
| ModuleManager | deactivateModule() |
routes 캐시 무효화 |
| PluginManager | activatePlugin() |
routes 캐시 무효화 |
| PluginManager | deactivatePlugin() |
routes 캐시 무효화 |