/** * ModuleAssetLoader * * 모듈/플러그인의 에셋(JS, CSS)을 동적으로 로드하는 클래스입니다. * TemplateApp 초기화 시 window.G7Config.moduleAssets를 기반으로 * 활성화된 모듈의 에셋을 로드합니다. */ import { createLogger } from '../utils/Logger'; const logger = createLogger('ModuleAssetLoader'); /** * 모듈 에셋 정보 인터페이스 */ export interface ModuleAsset { /** 모듈 식별자 (vendor-module 형식) */ identifier: string; /** JS 번들 URL */ js?: string; /** CSS 번들 URL */ css?: string; /** 로드 우선순위 (낮을수록 먼저) */ priority: number; /** 외부 스크립트 정의 (조건부 로드용) */ external?: ExternalScript[]; } /** * 외부 스크립트 정의 인터페이스 */ export interface ExternalScript { /** 스크립트 URL */ src: string; /** 스크립트 ID (중복 로드 방지) */ id: string; /** 조건부 로드 표현식 (예: "{{_global.settings.useLib}}") */ if?: string; } /** * 로드된 에셋 정보 */ interface LoadedAsset { /** 에셋 타입 (js, css) */ type: 'js' | 'css'; /** DOM 요소 */ element: HTMLElement; } /** * ModuleAssetLoader 클래스 * * 모듈 에셋의 동적 로드/언로드를 관리합니다. */ export class ModuleAssetLoader { /** 로드된 에셋 맵 (identifier -> LoadedAsset[]) */ private loadedAssets: Map = new Map(); /** 로드 중인 프로미스 맵 (중복 로드 방지) */ private loadingPromises: Map> = new Map(); /** * 활성화된 모듈들의 에셋을 로드합니다. * * CSS/JS 모두 병렬 fetch로 로드합니다. JS는 `script.async = false` + * 정렬된 DOM append 순서로 **실행 순서는 priority 정렬대로** 보장됩니다. * (HTML 사양: async=false 스크립트는 삽입 순서대로 실행) * * @param extensions 모듈 에셋 배열 */ async loadActiveExtensionAssets(extensions: ModuleAsset[]): Promise { if (!extensions || extensions.length === 0) { logger.log('No module assets to load'); return; } // 우선순위 순으로 정렬 (낮을수록 먼저) const sortedExtensions = [...extensions].sort((a, b) => a.priority - b.priority); logger.log('Loading module assets:', sortedExtensions.map(e => e.identifier)); // CSS 병렬 로드 (렌더링 블로킹 방지) const cssPromises = sortedExtensions .filter(ext => ext.css) .map(ext => this.loadCSS(ext.identifier, ext.css!)); // JS 병렬 fetch (script.async=false로 실행 순서는 append 순서대로 유지) // 정렬된 순서로 순차 append하여 우선순위 기반 실행 순서를 보장 const jsPromises = sortedExtensions .filter(ext => ext.js) .map(ext => this.loadJS(ext.identifier, ext.js!)); await Promise.all([...cssPromises, ...jsPromises]); logger.log('All module assets loaded successfully'); } /** * 서버측에서 병합된 확장 번들(JS/CSS)을 로드합니다. * * 활성 모듈/플러그인 IIFE 를 priority 순으로 이어붙인 단일 파일을 하나의 * `