# 모듈 프론트엔드 에셋 시스템 > 이 문서는 G7의 모듈 프론트엔드 에셋 로딩 시스템을 다룹니다. --- ## TL;DR (5초 요약) ```text 1. module.json에 에셋 매니페스트 정의 (js, css, loading strategy) 2. Vite IIFE 빌드로 dist/에 번들 생성 3. TemplateApp 초기화 시 자동 로드 (global 전략) 4. 핸들러 네이밍: {module-identifier}.{handler-name} 5. 빌드 명령: php artisan module:build [identifier] (기본: _bundled, --active로 활성) ``` --- ## 목차 - [개요](#개요) - [에셋 매니페스트 스키마](#에셋-매니페스트-스키마) - [모듈 프론트엔드 구조](#모듈-프론트엔드-구조) - [핸들러 등록](#핸들러-등록) - [빌드 및 배포](#빌드-및-배포) - [에셋 로딩 전략](#에셋-로딩-전략) - [외부 라이브러리](#외부-라이브러리) - [관련 문서](#관련-문서) --- ## 개요 모듈/플러그인은 자체 프론트엔드 에셋(JS, CSS, 이미지, 폰트 등)을 포함할 수 있습니다. 이 시스템은 활성화된 모듈의 에셋을 동적으로 로드하여 ActionDispatcher에 핸들러를 등록합니다. ### 작동 원리 ``` 1. admin.blade.php 렌더링 └─ window.G7Config.moduleAssets 주입 2. TemplateApp.init() └─ ModuleAssetLoader.loadActiveExtensionAssets() ├─ CSS 로드 (병렬) └─ JS 로드 (병렬 fetch + 순차 실행) ├─ priority 오름차순 정렬 후 DOM append ├─ script.async = false → 삽입 순서대로 실행 보장 (HTML 사양) └─ 각 모듈의 initModule() 실행 └─ ActionDispatcher.registerHandler() 3. 레이아웃 렌더링 └─ 모듈 핸들러 사용 가능 ``` > **성능 참고**: JS 번들은 `Promise.all` 로 병렬 fetch 되며, `script.async = false` 와 > priority 정렬된 DOM append 순서로 **실행 순서는 유지**됩니다. N 개의 확장 IIFE 로딩이 > N × (fetch 시간) 에서 max(fetch 시간) 으로 단축됩니다. 단, 확장 간 런타임 의존성 > (다른 확장의 window 전역/핸들러 참조) 이 발생하면 priority 필드로 실행 순서를 명시해야 합니다. --- ## 에셋 매니페스트 스키마 ### module.json (통합 스키마) 모듈 루트에 `module.json` 파일을 생성합니다. 메타데이터와 에셋 설정이 하나의 파일에 통합되어 있습니다. ```json { "identifier": "sirsoft-ecommerce", "vendor": "sirsoft", "name": { "ko": "이커머스", "en": "Ecommerce" }, "version": "1.0.0", "description": { "ko": "상품 및 주문 관리를 위한 이커머스 모듈", "en": "E-commerce module for product and order management" }, "g7_version": ">=1.0.0", "dependencies": { "modules": {}, "plugins": {} }, "github_url": null, "github_changelog_url": null, "assets": { "js": { "entry": "resources/js/index.ts", "output": "dist/js/module.iife.js" }, "css": { "entry": "resources/css/main.css", "output": "dist/css/module.css" }, "handlers": true, "static": "resources/assets/" }, "loading": { "strategy": "global", "priority": 100 } } ``` > **참고**: `identifier`, `vendor`는 디렉토리명에서 자동 추론되므로 생략 가능합니다. > `name`, `version`, `description`은 AbstractModule에서 자동 파싱됩니다. ### 스키마 설명 #### 메타데이터 필드 | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `identifier` | `string` | 선택 | 모듈 식별자 (디렉토리명에서 자동 추론) | | `vendor` | `string` | 선택 | 벤더명 (identifier에서 자동 추론) | | `name` | `string\|object` | 필수 | 모듈명 (다국어: `{"ko": "...", "en": "..."}`) | | `version` | `string` | 필수 | 시맨틱 버전 (예: `1.0.0`) | | `description` | `string\|object` | 필수 | 모듈 설명 (다국어 지원) | | `g7_version` | `string` | 선택 | 그누보드7 코어 버전 제약 (예: `>=1.0.0`) | | `dependencies` | `object` | 선택 | 모듈/플러그인 의존성 | | `github_url` | `string\|null` | 선택 | GitHub 저장소 URL (업데이트 감지용) | | `github_changelog_url` | `string\|null` | 선택 | GitHub 변경 이력 URL | | `trusted_script_hosts` | `string[]` | 선택 | 레이아웃이 로드할 수 있는 외부 스크립트 신뢰 호스트 목록 (아래 참조) | #### `trusted_script_hosts` — 외부 스크립트 신뢰 호스트 **구동에 필요한 자산은 확장이 함께 담아 자체 제공하는 것이 원칙입니다.** 브라우저가 화면을 그리기 위해 제3자 CDN 에 도달해야 하면, 그 도달 실패는 예외도 로그도 남기지 않고 화면 기능만 조용히 사라집니다 — 폐쇄망·방화벽·광고차단기에서 재현되며 자체 서버 로그에 흔적이 없어 운영자가 원인을 특정할 수 없습니다. 동봉 위치는 `dist/vendor/{라이브러리}/{버전}/` 입니다. 이 필드는 **자체 제공이 불가능한 경우**에만 씁니다 — 라이브러리가 아니라 그 회사 서버와 통신하는 서비스 SDK(주소 검색 등)가 그렇습니다. 자체 호스팅해도 동작하지 않으므로 외부 의존이 남습니다. 레이아웃 보안 정책은 `scripts[].src`·`data_sources[].endpoint` 를 기본적으로 same-origin 경로(`/` 로 시작)만 허용하고, 외부 origin·protocol-relative(`//host`)·scheme 포함 URL 은 저장 시점과 렌더 시점 양쪽에서 차단합니다. 같은 판정은 레이아웃 파일뿐 아니라 **브라우저에 새 `