viewlogic 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1482,6 +1482,604 @@ var QueryManager = class {
1482
1482
  }
1483
1483
  };
1484
1484
 
1485
+ // src/core/FormHandler.js
1486
+ var FormHandler = class {
1487
+ constructor(router, options = {}) {
1488
+ this.router = router;
1489
+ this.config = {
1490
+ debug: options.debug || false,
1491
+ ...options
1492
+ };
1493
+ this.log("debug", "FormHandler initialized");
1494
+ }
1495
+ /**
1496
+ * 로깅 래퍼 메서드
1497
+ */
1498
+ log(level, ...args) {
1499
+ if (this.router?.errorHandler) {
1500
+ this.router.errorHandler.log(level, "FormHandler", ...args);
1501
+ }
1502
+ }
1503
+ /**
1504
+ * 자동 폼 바인딩
1505
+ */
1506
+ bindAutoForms(component) {
1507
+ const forms = document.querySelectorAll("form.auto-form, form[action]");
1508
+ forms.forEach((form) => {
1509
+ form.removeEventListener("submit", form._boundSubmitHandler);
1510
+ const boundHandler = (e) => this.handleFormSubmit(e, component);
1511
+ form._boundSubmitHandler = boundHandler;
1512
+ form.addEventListener("submit", boundHandler);
1513
+ this.log("debug", `Form auto-bound: ${form.getAttribute("action")}`);
1514
+ });
1515
+ }
1516
+ /**
1517
+ * 폼 서브밋 핸들러
1518
+ */
1519
+ async handleFormSubmit(event, component) {
1520
+ event.preventDefault();
1521
+ const form = event.target;
1522
+ let action = form.getAttribute("action");
1523
+ const method = form.getAttribute("method") || "POST";
1524
+ const successHandler = form.getAttribute("data-success-handler");
1525
+ const errorHandler = form.getAttribute("data-error-handler");
1526
+ const loadingHandler = form.getAttribute("data-loading-handler");
1527
+ const redirectTo = form.getAttribute("data-redirect");
1528
+ try {
1529
+ if (loadingHandler && component[loadingHandler]) {
1530
+ component[loadingHandler](true, form);
1531
+ }
1532
+ action = this.processActionParams(action, component);
1533
+ if (!this.validateForm(form, component)) {
1534
+ return;
1535
+ }
1536
+ const formData = new FormData(form);
1537
+ const data = Object.fromEntries(formData.entries());
1538
+ this.log("debug", `Form submitting to: ${action}`, data);
1539
+ const response = await this.submitFormData(action, method, data, form, component);
1540
+ if (successHandler && component[successHandler]) {
1541
+ component[successHandler](response, form);
1542
+ }
1543
+ if (redirectTo) {
1544
+ setTimeout(() => {
1545
+ component.navigateTo(redirectTo);
1546
+ }, 1e3);
1547
+ }
1548
+ } catch (error) {
1549
+ this.log("warn", `Form submission error:`, error);
1550
+ if (errorHandler && component[errorHandler]) {
1551
+ component[errorHandler](error, form);
1552
+ } else {
1553
+ console.error("Form submission error:", error);
1554
+ }
1555
+ } finally {
1556
+ if (loadingHandler && component[loadingHandler]) {
1557
+ component[loadingHandler](false, form);
1558
+ }
1559
+ }
1560
+ }
1561
+ /**
1562
+ * 액션 파라미터 처리 (ApiHandler 재사용)
1563
+ */
1564
+ processActionParams(actionTemplate, component) {
1565
+ return this.router.routeLoader.apiHandler.processURLParameters(actionTemplate, component);
1566
+ }
1567
+ /**
1568
+ * 폼 데이터 서브밋 (ApiHandler 활용)
1569
+ */
1570
+ async submitFormData(action, method, data, form, component) {
1571
+ const hasFile = Array.from(form.elements).some((el) => el.type === "file" && el.files.length > 0);
1572
+ const options = {
1573
+ method: method.toUpperCase(),
1574
+ headers: {}
1575
+ };
1576
+ if (hasFile) {
1577
+ options.data = new FormData(form);
1578
+ } else {
1579
+ options.data = data;
1580
+ options.headers["Content-Type"] = "application/json";
1581
+ }
1582
+ return await this.router.routeLoader.apiHandler.fetchData(action, component, options);
1583
+ }
1584
+ /**
1585
+ * 클라이언트 사이드 폼 검증
1586
+ */
1587
+ validateForm(form, component) {
1588
+ let isValid = true;
1589
+ const inputs = form.querySelectorAll("input, textarea, select");
1590
+ inputs.forEach((input) => {
1591
+ if (!input.checkValidity()) {
1592
+ isValid = false;
1593
+ input.classList.add("error");
1594
+ return;
1595
+ }
1596
+ const validationFunction = input.getAttribute("data-validation");
1597
+ if (validationFunction) {
1598
+ const isInputValid = this.validateInput(input, validationFunction, component);
1599
+ if (!isInputValid) {
1600
+ isValid = false;
1601
+ input.classList.add("error");
1602
+ } else {
1603
+ input.classList.remove("error");
1604
+ }
1605
+ } else {
1606
+ input.classList.remove("error");
1607
+ }
1608
+ });
1609
+ return isValid;
1610
+ }
1611
+ /**
1612
+ * 개별 입력 검증
1613
+ */
1614
+ validateInput(input, validationFunction, component) {
1615
+ const value = input.value;
1616
+ if (typeof component[validationFunction] === "function") {
1617
+ try {
1618
+ return component[validationFunction](value, input);
1619
+ } catch (error) {
1620
+ this.log("warn", `Validation function '${validationFunction}' error:`, error);
1621
+ return false;
1622
+ }
1623
+ }
1624
+ this.log("warn", `Validation function '${validationFunction}' not found`);
1625
+ return true;
1626
+ }
1627
+ /**
1628
+ * 정리 (메모리 누수 방지)
1629
+ */
1630
+ destroy() {
1631
+ const forms = document.querySelectorAll("form.auto-form, form[action]");
1632
+ forms.forEach((form) => {
1633
+ if (form._boundSubmitHandler) {
1634
+ form.removeEventListener("submit", form._boundSubmitHandler);
1635
+ delete form._boundSubmitHandler;
1636
+ }
1637
+ });
1638
+ this.log("debug", "FormHandler destroyed");
1639
+ this.router = null;
1640
+ }
1641
+ };
1642
+
1643
+ // src/core/ApiHandler.js
1644
+ var ApiHandler = class {
1645
+ constructor(router, options = {}) {
1646
+ this.router = router;
1647
+ this.config = {
1648
+ debug: options.debug || false,
1649
+ timeout: options.timeout || 1e4,
1650
+ retries: options.retries || 1,
1651
+ ...options
1652
+ };
1653
+ this.log("debug", "ApiHandler initialized");
1654
+ }
1655
+ /**
1656
+ * 로깅 래퍼 메서드
1657
+ */
1658
+ log(level, ...args) {
1659
+ if (this.router?.errorHandler) {
1660
+ this.router.errorHandler.log(level, "ApiHandler", ...args);
1661
+ }
1662
+ }
1663
+ /**
1664
+ * 컴포넌트 데이터 가져오기 (파라미터 치환 지원)
1665
+ */
1666
+ async fetchData(dataURL, component = null, options = {}) {
1667
+ try {
1668
+ let processedURL = this.processURLParameters(dataURL, component);
1669
+ const queryString = this.router.queryManager?.buildQueryString(this.router.queryManager?.getQueryParams()) || "";
1670
+ const fullURL = queryString ? `${processedURL}?${queryString}` : processedURL;
1671
+ this.log("debug", `Fetching data from: ${fullURL}`);
1672
+ const requestOptions = {
1673
+ method: options.method || "GET",
1674
+ headers: {
1675
+ "Content-Type": "application/json",
1676
+ "Accept": "application/json",
1677
+ ...options.headers
1678
+ },
1679
+ ...options
1680
+ };
1681
+ if (component?.$getToken && component.$getToken()) {
1682
+ requestOptions.headers["Authorization"] = `Bearer ${component.$getToken()}`;
1683
+ }
1684
+ if (options.data && ["POST", "PUT", "PATCH"].includes(requestOptions.method.toUpperCase())) {
1685
+ if (options.data instanceof FormData) {
1686
+ requestOptions.body = options.data;
1687
+ delete requestOptions.headers["Content-Type"];
1688
+ } else {
1689
+ requestOptions.body = JSON.stringify(options.data);
1690
+ }
1691
+ }
1692
+ const response = await fetch(fullURL, requestOptions);
1693
+ if (!response.ok) {
1694
+ let error;
1695
+ try {
1696
+ error = await response.json();
1697
+ } catch (e) {
1698
+ error = { message: `HTTP ${response.status}: ${response.statusText}` };
1699
+ }
1700
+ throw new Error(error.message || `HTTP ${response.status}`);
1701
+ }
1702
+ try {
1703
+ const data = await response.json();
1704
+ if (typeof data !== "object" || data === null) {
1705
+ throw new Error("Invalid data format: expected object");
1706
+ }
1707
+ return data;
1708
+ } catch (e) {
1709
+ return { success: true };
1710
+ }
1711
+ } catch (error) {
1712
+ this.log("error", "Failed to fetch data:", error);
1713
+ throw error;
1714
+ }
1715
+ }
1716
+ /**
1717
+ * 여러 API 엔드포인트에서 데이터 가져오기
1718
+ */
1719
+ async fetchMultipleData(dataConfig, component = null) {
1720
+ if (!dataConfig || typeof dataConfig !== "object") {
1721
+ return {};
1722
+ }
1723
+ const results = {};
1724
+ const errors = {};
1725
+ const promises = Object.entries(dataConfig).map(async ([key, config]) => {
1726
+ try {
1727
+ let url, options = {};
1728
+ if (typeof config === "string") {
1729
+ url = config;
1730
+ } else if (typeof config === "object") {
1731
+ url = config.url;
1732
+ options = { ...config };
1733
+ delete options.url;
1734
+ }
1735
+ if (url) {
1736
+ const data = await this.fetchData(url, component, options);
1737
+ results[key] = data;
1738
+ }
1739
+ } catch (error) {
1740
+ errors[key] = error;
1741
+ this.log("warn", `Failed to fetch data for '${key}':`, error);
1742
+ }
1743
+ });
1744
+ await Promise.all(promises);
1745
+ return { results, errors };
1746
+ }
1747
+ /**
1748
+ * URL에서 파라미터 치환 처리 ({param} 형식)
1749
+ */
1750
+ processURLParameters(url, component = null) {
1751
+ if (!url || typeof url !== "string") return url;
1752
+ let processedURL = url;
1753
+ const paramMatches = url.match(/\{([^}]+)\}/g);
1754
+ if (paramMatches && component) {
1755
+ paramMatches.forEach((match) => {
1756
+ const paramName = match.slice(1, -1);
1757
+ try {
1758
+ let paramValue = null;
1759
+ if (component.getParam) {
1760
+ paramValue = component.getParam(paramName);
1761
+ }
1762
+ if (paramValue === null || paramValue === void 0) {
1763
+ paramValue = component[paramName];
1764
+ }
1765
+ if (paramValue === null || paramValue === void 0) {
1766
+ if (component.$options?.computed?.[paramName]) {
1767
+ paramValue = component[paramName];
1768
+ }
1769
+ }
1770
+ if (paramValue === null || paramValue === void 0) {
1771
+ paramValue = this.router.queryManager?.getParam(paramName);
1772
+ }
1773
+ if (paramValue !== null && paramValue !== void 0) {
1774
+ processedURL = processedURL.replace(
1775
+ match,
1776
+ encodeURIComponent(paramValue)
1777
+ );
1778
+ this.log("debug", `URL parameter resolved: ${paramName} = ${paramValue}`);
1779
+ } else {
1780
+ this.log("warn", `URL parameter '${paramName}' not found, keeping original: ${match}`);
1781
+ }
1782
+ } catch (error) {
1783
+ this.log("warn", `Error processing URL parameter '${paramName}':`, error);
1784
+ }
1785
+ });
1786
+ }
1787
+ return processedURL;
1788
+ }
1789
+ /**
1790
+ * HTTP 메서드별 헬퍼 함수들
1791
+ */
1792
+ async get(url, component = null, options = {}) {
1793
+ return this.fetchData(url, component, { ...options, method: "GET" });
1794
+ }
1795
+ async post(url, data, component = null, options = {}) {
1796
+ return this.fetchData(url, component, { ...options, method: "POST", data });
1797
+ }
1798
+ async put(url, data, component = null, options = {}) {
1799
+ return this.fetchData(url, component, { ...options, method: "PUT", data });
1800
+ }
1801
+ async patch(url, data, component = null, options = {}) {
1802
+ return this.fetchData(url, component, { ...options, method: "PATCH", data });
1803
+ }
1804
+ async delete(url, component = null, options = {}) {
1805
+ return this.fetchData(url, component, { ...options, method: "DELETE" });
1806
+ }
1807
+ /**
1808
+ * 정리 (메모리 누수 방지)
1809
+ */
1810
+ destroy() {
1811
+ this.log("debug", "ApiHandler destroyed");
1812
+ this.router = null;
1813
+ }
1814
+ };
1815
+
1816
+ // src/core/ComponentLoader.js
1817
+ var ComponentLoader = class {
1818
+ constructor(router = null, options = {}) {
1819
+ this.config = {
1820
+ componentsPath: options.componentsPath || "/components",
1821
+ // srcPath 기준 상대 경로
1822
+ debug: options.debug || false,
1823
+ environment: options.environment || "development",
1824
+ ...options
1825
+ };
1826
+ this.router = router;
1827
+ this.loadingPromises = /* @__PURE__ */ new Map();
1828
+ this.unifiedComponents = null;
1829
+ }
1830
+ /**
1831
+ * 로깅 래퍼 메서드
1832
+ */
1833
+ log(level, ...args) {
1834
+ if (this.router?.errorHandler) {
1835
+ this.router.errorHandler.log(level, "ComponentLoader", ...args);
1836
+ }
1837
+ }
1838
+ /**
1839
+ * 컴포넌트를 비동기로 로드 (캐시 지원)
1840
+ */
1841
+ async loadComponent(componentName) {
1842
+ if (!componentName || typeof componentName !== "string") {
1843
+ throw new Error("Component name must be a non-empty string");
1844
+ }
1845
+ const cacheKey = `component_${componentName}`;
1846
+ const cachedComponent = this.router?.cacheManager?.getFromCache(cacheKey);
1847
+ if (cachedComponent) {
1848
+ this.log("debug", `Component '${componentName}' loaded from cache`);
1849
+ return cachedComponent;
1850
+ }
1851
+ if (this.loadingPromises.has(componentName)) {
1852
+ return this.loadingPromises.get(componentName);
1853
+ }
1854
+ const loadPromise = this._loadComponentFromFile(componentName);
1855
+ this.loadingPromises.set(componentName, loadPromise);
1856
+ try {
1857
+ const component = await loadPromise;
1858
+ if (component && this.router?.cacheManager) {
1859
+ this.router.cacheManager.setCache(cacheKey, component);
1860
+ this.log("debug", `Component '${componentName}' cached successfully`);
1861
+ }
1862
+ return component;
1863
+ } catch (error) {
1864
+ throw error;
1865
+ } finally {
1866
+ this.loadingPromises.delete(componentName);
1867
+ }
1868
+ }
1869
+ /**
1870
+ * 파일에서 컴포넌트 로드
1871
+ */
1872
+ async _loadComponentFromFile(componentName) {
1873
+ const componentRelativePath = `${this.config.componentsPath}/${componentName}.js`;
1874
+ let componentPath;
1875
+ if (this.router && this.router.config.srcPath) {
1876
+ const srcPath = this.router.config.srcPath;
1877
+ if (srcPath.startsWith("http")) {
1878
+ const cleanSrcPath = srcPath.endsWith("/") ? srcPath.slice(0, -1) : srcPath;
1879
+ const cleanComponentPath = componentRelativePath.startsWith("/") ? componentRelativePath : `/${componentRelativePath}`;
1880
+ componentPath = `${cleanSrcPath}${cleanComponentPath}`;
1881
+ } else {
1882
+ componentPath = this.router.resolvePath(`${srcPath}${componentRelativePath}`);
1883
+ }
1884
+ } else {
1885
+ componentPath = this.router ? this.router.resolvePath(`/src${componentRelativePath}`) : `/src${componentRelativePath}`;
1886
+ }
1887
+ try {
1888
+ const module = await import(componentPath);
1889
+ const component = module.default;
1890
+ if (!component) {
1891
+ throw new Error(`Component '${componentName}' has no default export`);
1892
+ }
1893
+ if (!component.name) {
1894
+ component.name = componentName;
1895
+ }
1896
+ this.log("debug", `Component '${componentName}' loaded successfully`);
1897
+ return component;
1898
+ } catch (error) {
1899
+ this.log("error", `Failed to load component '${componentName}':`, error);
1900
+ throw new Error(`Component '${componentName}' not found: ${error.message}`);
1901
+ }
1902
+ }
1903
+ /**
1904
+ * 컴포넌트 모듈 클리어
1905
+ */
1906
+ clearComponents() {
1907
+ this.loadingPromises.clear();
1908
+ this.unifiedComponents = null;
1909
+ this.log("debug", "All components cleared");
1910
+ }
1911
+ /**
1912
+ * 환경에 따른 모든 컴포넌트 로딩 (캐싱 지원)
1913
+ */
1914
+ async loadAllComponents(componentNames = null) {
1915
+ let components;
1916
+ if (this.config.environment === "production") {
1917
+ if (this.unifiedComponents) {
1918
+ this.log("debug", "Using existing unified components");
1919
+ return this.unifiedComponents;
1920
+ }
1921
+ components = await this._loadProductionComponents();
1922
+ } else {
1923
+ components = await this._loadDevelopmentComponents(componentNames);
1924
+ }
1925
+ return components;
1926
+ }
1927
+ /**
1928
+ * 운영 모드: 통합 컴포넌트 로딩
1929
+ */
1930
+ async _loadProductionComponents() {
1931
+ try {
1932
+ const componentsPath = `${this.router?.config?.routesPath || "/routes"}/_components.js`;
1933
+ this.log("info", "[PRODUCTION] Loading unified components from:", componentsPath);
1934
+ const componentsModule = await import(componentsPath);
1935
+ if (typeof componentsModule.registerComponents === "function") {
1936
+ this.unifiedComponents = componentsModule.components || {};
1937
+ this.log("info", `[PRODUCTION] Unified components loaded: ${Object.keys(this.unifiedComponents).length} components`);
1938
+ return this.unifiedComponents;
1939
+ } else {
1940
+ throw new Error("registerComponents function not found in components module");
1941
+ }
1942
+ } catch (error) {
1943
+ this.log("warn", "[PRODUCTION] Failed to load unified components:", error.message);
1944
+ this.unifiedComponents = {};
1945
+ return {};
1946
+ }
1947
+ }
1948
+ /**
1949
+ * 개발 모드: 개별 컴포넌트 로딩
1950
+ */
1951
+ async _loadDevelopmentComponents(componentNames = null) {
1952
+ const namesToLoad = componentNames || [];
1953
+ const components = {};
1954
+ if (namesToLoad.length === 0) {
1955
+ this.log("info", "[DEVELOPMENT] No components to load");
1956
+ return components;
1957
+ }
1958
+ this.log("info", `[DEVELOPMENT] Loading individual components: ${namesToLoad.join(", ")}`);
1959
+ for (const name of namesToLoad) {
1960
+ try {
1961
+ const component = await this.loadComponent(name);
1962
+ if (component) {
1963
+ components[name] = component;
1964
+ }
1965
+ } catch (loadError) {
1966
+ this.log("warn", `[DEVELOPMENT] Failed to load component ${name}:`, loadError.message);
1967
+ }
1968
+ }
1969
+ this.log("info", `[DEVELOPMENT] Individual components loaded: ${Object.keys(components).length} components`);
1970
+ return components;
1971
+ }
1972
+ /**
1973
+ * 템플릿과 레이아웃에서 사용된 컴포넌트 추출
1974
+ */
1975
+ getComponentNames(template, layout = null, layoutName = null) {
1976
+ const componentSet = layout ? this._getLayoutComponents(layout, layoutName) : /* @__PURE__ */ new Set();
1977
+ if (template) {
1978
+ this._extractComponentsFromContent(template, componentSet);
1979
+ }
1980
+ const components = Array.from(componentSet);
1981
+ this.log("debug", `Discovered ${components.length} components:`, components);
1982
+ return components;
1983
+ }
1984
+ /**
1985
+ * 레이아웃에서 컴포넌트 추출 (캐시 활용)
1986
+ */
1987
+ _getLayoutComponents(layout, layoutName) {
1988
+ if (!layout || typeof layout !== "string") return /* @__PURE__ */ new Set();
1989
+ if (!layoutName || typeof layoutName !== "string") return /* @__PURE__ */ new Set();
1990
+ const cacheKey = `layout_components_${layoutName}`;
1991
+ const cachedComponents = this.router?.cacheManager?.getFromCache(cacheKey);
1992
+ if (cachedComponents) {
1993
+ this.log("debug", `Using cached layout components for '${layoutName}'`);
1994
+ return cachedComponents;
1995
+ }
1996
+ const componentSet = /* @__PURE__ */ new Set();
1997
+ this._extractComponentsFromContent(layout, componentSet);
1998
+ if (this.router?.cacheManager) {
1999
+ this.router.cacheManager.setCache(cacheKey, componentSet);
2000
+ this.log("debug", `Cached layout components for '${layoutName}': ${Array.from(componentSet).join(", ")}`);
2001
+ }
2002
+ return componentSet;
2003
+ }
2004
+ /**
2005
+ * HTML 컨텐츠에서 Vue 컴포넌트 추출
2006
+ */
2007
+ _extractComponentsFromContent(content, componentSet) {
2008
+ if (!content || typeof content !== "string") return;
2009
+ const componentPattern = /<([A-Z][a-zA-Z0-9]*)(?:\s[^>]*)?\/?>|<\/([A-Z][a-zA-Z0-9]*)\s*>/gs;
2010
+ let match;
2011
+ while ((match = componentPattern.exec(content)) !== null) {
2012
+ const componentName = match[1] || match[2];
2013
+ if (componentName && !this._isHtmlTag(componentName)) {
2014
+ componentSet.add(componentName);
2015
+ this.log("debug", `Found component: ${componentName}`);
2016
+ }
2017
+ }
2018
+ }
2019
+ /**
2020
+ * HTML 기본 태그인지 확인
2021
+ */
2022
+ _isHtmlTag(tagName) {
2023
+ const htmlTags = [
2024
+ "div",
2025
+ "span",
2026
+ "p",
2027
+ "a",
2028
+ "img",
2029
+ "ul",
2030
+ "ol",
2031
+ "li",
2032
+ "h1",
2033
+ "h2",
2034
+ "h3",
2035
+ "h4",
2036
+ "h5",
2037
+ "h6",
2038
+ "table",
2039
+ "tr",
2040
+ "td",
2041
+ "th",
2042
+ "form",
2043
+ "select",
2044
+ "option",
2045
+ "textarea",
2046
+ "nav",
2047
+ "header",
2048
+ "footer",
2049
+ "main",
2050
+ "section",
2051
+ "article",
2052
+ "aside",
2053
+ "figure",
2054
+ "figcaption",
2055
+ "video",
2056
+ "audio",
2057
+ "canvas",
2058
+ "svg",
2059
+ "iframe",
2060
+ "script",
2061
+ "style",
2062
+ "link",
2063
+ "meta",
2064
+ "title",
2065
+ "body",
2066
+ "html",
2067
+ "head",
2068
+ "template",
2069
+ "slot"
2070
+ ];
2071
+ return htmlTags.includes(tagName);
2072
+ }
2073
+ /**
2074
+ * 메모리 정리
2075
+ */
2076
+ dispose() {
2077
+ this.clearComponents();
2078
+ this.log("debug", "ComponentLoader disposed");
2079
+ this.router = null;
2080
+ }
2081
+ };
2082
+
1485
2083
  // src/core/RouteLoader.js
1486
2084
  var RouteLoader = class {
1487
2085
  constructor(router, options = {}) {
@@ -1493,10 +2091,12 @@ var RouteLoader = class {
1493
2091
  environment: options.environment || "development",
1494
2092
  useLayout: options.useLayout !== false,
1495
2093
  defaultLayout: options.defaultLayout || "default",
1496
- useComponents: options.useComponents !== false,
1497
2094
  debug: options.debug || false
1498
2095
  };
1499
2096
  this.router = router;
2097
+ this.formHandler = new FormHandler(router, this.config);
2098
+ this.apiHandler = new ApiHandler(router, this.config);
2099
+ this.componentLoader = new ComponentLoader(router, this.config);
1500
2100
  this.log("debug", "RouteLoader initialized with config:", this.config);
1501
2101
  }
1502
2102
  /**
@@ -1614,17 +2214,33 @@ ${template}`;
1614
2214
  if (isProduction) {
1615
2215
  template = script.template || this.generateDefaultTemplate(routeName);
1616
2216
  } else {
1617
- template = await this.loadTemplate(routeName);
1618
- style = await this.loadStyle(routeName);
1619
- layout = this.config.useLayout && script.layout !== null ? await this.loadLayout(script.layout || this.config.defaultLayout) : null;
2217
+ const loadPromises = [
2218
+ this.loadTemplate(routeName),
2219
+ this.loadStyle(routeName)
2220
+ ];
2221
+ if (this.config.useLayout && script.layout !== null) {
2222
+ loadPromises.push(this.loadLayout(script.layout || this.config.defaultLayout));
2223
+ } else {
2224
+ loadPromises.push(Promise.resolve(null));
2225
+ }
2226
+ const [loadedTemplate, loadedStyle, loadedLayout] = await Promise.all(loadPromises);
2227
+ template = loadedTemplate;
2228
+ style = loadedStyle;
2229
+ layout = loadedLayout;
1620
2230
  if (layout) {
1621
2231
  template = this.mergeLayoutWithTemplate(routeName, layout, template);
1622
2232
  }
1623
2233
  }
1624
2234
  let loadedComponents = {};
1625
- if (this.config.useComponents && router.componentLoader) {
2235
+ if (this.componentLoader) {
1626
2236
  try {
1627
- loadedComponents = await router.componentLoader.loadAllComponents();
2237
+ let componentNames = null;
2238
+ if (!isProduction) {
2239
+ const layoutName = script.layout || this.config.defaultLayout;
2240
+ componentNames = this.componentLoader.getComponentNames(template, layout, layoutName);
2241
+ this.log("info", `[DEVELOPMENT] Discovered components for route '${routeName}':`, componentNames);
2242
+ }
2243
+ loadedComponents = await this.componentLoader.loadAllComponents(componentNames);
1628
2244
  this.log("debug", `Components loaded successfully for route: ${routeName}`);
1629
2245
  } catch (error) {
1630
2246
  this.log("warn", `Component loading failed for route '${routeName}', continuing without components:`, error.message);
@@ -1666,14 +2282,10 @@ ${template}`;
1666
2282
  await script.mounted.call(this);
1667
2283
  }
1668
2284
  if (script.dataURL) {
1669
- if (typeof script.dataURL === "string") {
1670
- await this.$fetchData();
1671
- } else if (typeof script.dataURL === "object") {
1672
- await this.$fetchMultipleData();
1673
- }
2285
+ await this.$fetchData();
1674
2286
  }
1675
2287
  await this.$nextTick();
1676
- this.$bindAutoForms();
2288
+ router.routeLoader.formHandler.bindAutoForms(this);
1677
2289
  },
1678
2290
  methods: {
1679
2291
  ...script.methods,
@@ -1702,245 +2314,52 @@ ${template}`;
1702
2314
  $removeToken: (storage) => router.authManager?.removeAccessToken(storage) || null,
1703
2315
  $getAuthCookie: () => router.authManager?.getAuthCookie() || null,
1704
2316
  $getCookie: (name) => router.authManager?.getCookieValue(name) || null,
1705
- // 데이터 fetch (단일 API 또는 특정 API)
1706
- async $fetchData(apiName) {
1707
- if (!script.dataURL) return;
2317
+ // 데이터 fetch (ApiHandler 래퍼)
2318
+ async $fetchData(dataConfig = null) {
2319
+ const configToUse = dataConfig || script.dataURL;
2320
+ if (!configToUse) return null;
1708
2321
  this.$dataLoading = true;
1709
2322
  try {
1710
- if (typeof script.dataURL === "string") {
1711
- const data = await router.routeLoader.fetchComponentData(script.dataURL);
1712
- if (router.errorHandler) router.errorHandler.debug("RouteLoader", `Data fetched for ${routeName}:`, data);
2323
+ if (typeof configToUse === "string") {
2324
+ const data = await router.routeLoader.apiHandler.fetchData(configToUse, this);
1713
2325
  Object.assign(this, data);
1714
2326
  this.$emit("data-loaded", data);
1715
- } else if (typeof script.dataURL === "object" && apiName) {
1716
- const url = script.dataURL[apiName];
1717
- if (url) {
1718
- const data = await router.routeLoader.fetchComponentData(url);
1719
- if (router.errorHandler) router.errorHandler.debug("RouteLoader", `Data fetched for ${routeName}.${apiName}:`, data);
1720
- this[apiName] = data;
1721
- this.$emit("data-loaded", { [apiName]: data });
1722
- }
1723
- } else {
1724
- await this.$fetchMultipleData();
1725
- }
1726
- } catch (error) {
1727
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Failed to fetch data for ${routeName}:`, error);
1728
- this.$emit("data-error", error);
1729
- } finally {
1730
- this.$dataLoading = false;
1731
- }
1732
- },
1733
- // 다중 API 데이터 fetch
1734
- async $fetchMultipleData() {
1735
- if (!script.dataURL || typeof script.dataURL !== "object") return;
1736
- const dataURLs = script.dataURL;
1737
- this.$dataLoading = true;
1738
- try {
1739
- const promises = Object.entries(dataURLs).map(async ([key, url]) => {
1740
- try {
1741
- const data = await router.routeLoader.fetchComponentData(url);
1742
- return { key, data, success: true };
1743
- } catch (error) {
1744
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Failed to fetch ${key} for ${routeName}:`, error);
1745
- return { key, error, success: false };
2327
+ return data;
2328
+ } else if (typeof configToUse === "object") {
2329
+ const { results, errors } = await router.routeLoader.apiHandler.fetchMultipleData(configToUse, this);
2330
+ Object.assign(this, results);
2331
+ if (Object.keys(results).length > 0) {
2332
+ this.$emit("data-loaded", results);
1746
2333
  }
1747
- });
1748
- const results = await Promise.all(promises);
1749
- const successfulResults = {};
1750
- const errors = {};
1751
- results.forEach(({ key, data, error, success }) => {
1752
- if (success) {
1753
- this[key] = data;
1754
- successfulResults[key] = data;
1755
- } else {
1756
- errors[key] = error;
2334
+ if (Object.keys(errors).length > 0) {
2335
+ this.$emit("data-error", errors);
1757
2336
  }
1758
- });
1759
- if (router.errorHandler) router.errorHandler.debug("RouteLoader", `Multiple data fetched for ${routeName}:`, successfulResults);
1760
- if (Object.keys(successfulResults).length > 0) {
1761
- this.$emit("data-loaded", successfulResults);
1762
- }
1763
- if (Object.keys(errors).length > 0) {
1764
- this.$emit("data-error", errors);
2337
+ return results;
1765
2338
  }
2339
+ return null;
1766
2340
  } catch (error) {
1767
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Failed to fetch multiple data for ${routeName}:`, error);
2341
+ if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Failed to fetch data for ${routeName}:`, error);
1768
2342
  this.$emit("data-error", error);
2343
+ throw error;
1769
2344
  } finally {
1770
2345
  this.$dataLoading = false;
1771
2346
  }
1772
2347
  },
1773
- // 전체 데이터 새로고침 (명시적 메서드)
1774
- async $fetchAllData() {
1775
- if (typeof script.dataURL === "string") {
1776
- await this.$fetchData();
1777
- } else if (typeof script.dataURL === "object") {
1778
- await this.$fetchMultipleData();
1779
- }
1780
- },
1781
- // 🆕 자동 폼 바인딩 메서드
1782
- $bindAutoForms() {
1783
- const forms = document.querySelectorAll("form.auto-form, form[action]");
1784
- forms.forEach((form) => {
1785
- form.removeEventListener("submit", form._boundSubmitHandler);
1786
- const boundHandler = (e) => this.$handleFormSubmit(e);
1787
- form._boundSubmitHandler = boundHandler;
1788
- form.addEventListener("submit", boundHandler);
1789
- if (router.errorHandler) router.errorHandler.debug("RouteLoader", `Form auto-bound: ${form.getAttribute("action")}`);
1790
- });
1791
- },
1792
- // 🆕 폼 서브밋 핸들러
1793
- async $handleFormSubmit(event) {
1794
- event.preventDefault();
1795
- const form = event.target;
1796
- let action = form.getAttribute("action");
1797
- const method = form.getAttribute("method") || "POST";
1798
- const successHandler = form.getAttribute("data-success-handler");
1799
- const errorHandler = form.getAttribute("data-error-handler");
1800
- const loadingHandler = form.getAttribute("data-loading-handler");
1801
- const redirectTo = form.getAttribute("data-redirect");
1802
- try {
1803
- if (loadingHandler && this[loadingHandler]) {
1804
- this[loadingHandler](true, form);
1805
- }
1806
- action = this.$processActionParams(action);
1807
- if (!this.$validateForm(form)) {
1808
- return;
1809
- }
1810
- const formData = new FormData(form);
1811
- const data = Object.fromEntries(formData.entries());
1812
- if (router.errorHandler) router.errorHandler.debug("RouteLoader", `Form submitting to: ${action}`, data);
1813
- const response = await this.$submitFormData(action, method, data, form);
1814
- if (successHandler && this[successHandler]) {
1815
- this[successHandler](response, form);
1816
- }
1817
- if (redirectTo) {
1818
- setTimeout(() => {
1819
- this.navigateTo(redirectTo);
1820
- }, 1e3);
1821
- }
1822
- } catch (error) {
1823
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Form submission error:`, error);
1824
- if (errorHandler && this[errorHandler]) {
1825
- this[errorHandler](error, form);
1826
- } else {
1827
- console.error("Form submission error:", error);
1828
- }
1829
- } finally {
1830
- if (loadingHandler && this[loadingHandler]) {
1831
- this[loadingHandler](false, form);
1832
- }
1833
- }
2348
+ // HTTP 메서드 래퍼들 (ApiHandler 직접 접근)
2349
+ async $get(url, options = {}) {
2350
+ return await router.routeLoader.apiHandler.get(url, this, options);
1834
2351
  },
1835
- // 🆕 액션 파라미터 처리 메서드 (간단한 템플릿 치환)
1836
- $processActionParams(actionTemplate) {
1837
- let processedAction = actionTemplate;
1838
- const paramMatches = actionTemplate.match(/\{([^}]+)\}/g);
1839
- if (paramMatches) {
1840
- paramMatches.forEach((match) => {
1841
- const paramName = match.slice(1, -1);
1842
- try {
1843
- let paramValue = null;
1844
- paramValue = this.getParam(paramName);
1845
- if (paramValue === null || paramValue === void 0) {
1846
- paramValue = this[paramName];
1847
- }
1848
- if (paramValue === null || paramValue === void 0) {
1849
- if (this.$options.computed && this.$options.computed[paramName]) {
1850
- paramValue = this[paramName];
1851
- }
1852
- }
1853
- if (paramValue !== null && paramValue !== void 0) {
1854
- processedAction = processedAction.replace(
1855
- match,
1856
- encodeURIComponent(paramValue)
1857
- );
1858
- if (router.errorHandler) router.errorHandler.debug("RouteLoader", `Parameter resolved: ${paramName} = ${paramValue}`);
1859
- } else {
1860
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Parameter '${paramName}' not found in component data, computed, or route params`);
1861
- }
1862
- } catch (error) {
1863
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Error processing parameter '${paramName}':`, error);
1864
- }
1865
- });
1866
- }
1867
- return processedAction;
2352
+ async $post(url, data, options = {}) {
2353
+ return await router.routeLoader.apiHandler.post(url, data, this, options);
1868
2354
  },
1869
- // 🆕 데이터 서브밋
1870
- async $submitFormData(action, method, data, form) {
1871
- const hasFile = Array.from(form.elements).some((el) => el.type === "file" && el.files.length > 0);
1872
- const headers = {
1873
- "Accept": "application/json",
1874
- // 인증 토큰 자동 추가
1875
- ...this.$getToken() && {
1876
- "Authorization": `Bearer ${this.$getToken()}`
1877
- }
1878
- };
1879
- let body;
1880
- if (hasFile) {
1881
- body = new FormData(form);
1882
- } else {
1883
- headers["Content-Type"] = "application/json";
1884
- body = JSON.stringify(data);
1885
- }
1886
- const response = await fetch(action, {
1887
- method: method.toUpperCase(),
1888
- headers,
1889
- body
1890
- });
1891
- if (!response.ok) {
1892
- let error;
1893
- try {
1894
- error = await response.json();
1895
- } catch (e) {
1896
- error = { message: `HTTP ${response.status}: ${response.statusText}` };
1897
- }
1898
- throw new Error(error.message || `HTTP ${response.status}`);
1899
- }
1900
- try {
1901
- return await response.json();
1902
- } catch (e) {
1903
- return { success: true };
1904
- }
2355
+ async $put(url, data, options = {}) {
2356
+ return await router.routeLoader.apiHandler.put(url, data, this, options);
1905
2357
  },
1906
- // 🆕 클라이언트 사이드 검증
1907
- $validateForm(form) {
1908
- let isValid = true;
1909
- const inputs = form.querySelectorAll("input, textarea, select");
1910
- inputs.forEach((input) => {
1911
- if (!input.checkValidity()) {
1912
- isValid = false;
1913
- input.classList.add("error");
1914
- return;
1915
- }
1916
- const validationFunction = input.getAttribute("data-validation");
1917
- if (validationFunction) {
1918
- const isInputValid = this.$validateInput(input, validationFunction);
1919
- if (!isInputValid) {
1920
- isValid = false;
1921
- input.classList.add("error");
1922
- } else {
1923
- input.classList.remove("error");
1924
- }
1925
- } else {
1926
- input.classList.remove("error");
1927
- }
1928
- });
1929
- return isValid;
2358
+ async $patch(url, data, options = {}) {
2359
+ return await router.routeLoader.apiHandler.patch(url, data, this, options);
1930
2360
  },
1931
- // 🆕 개별 입력 검증
1932
- $validateInput(input, validationFunction) {
1933
- const value = input.value;
1934
- if (typeof this[validationFunction] === "function") {
1935
- try {
1936
- return this[validationFunction](value, input);
1937
- } catch (error) {
1938
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Validation function '${validationFunction}' error:`, error);
1939
- return false;
1940
- }
1941
- }
1942
- if (router.errorHandler) router.errorHandler.warn("RouteLoader", `Validation function '${validationFunction}' not found`);
1943
- return true;
2361
+ async $delete(url, options = {}) {
2362
+ return await router.routeLoader.apiHandler.delete(url, this, options);
1944
2363
  }
1945
2364
  },
1946
2365
  _routeName: routeName
@@ -1963,61 +2382,6 @@ ${template}`;
1963
2382
  generateDefaultTemplate(routeName) {
1964
2383
  return `<div class="route-${routeName}"><h1>Route: ${routeName}</h1></div>`;
1965
2384
  }
1966
- /**
1967
- * 컴포넌트 데이터 가져오기
1968
- */
1969
- async fetchComponentData(dataURL) {
1970
- try {
1971
- const queryString = this.router.queryManager?.buildQueryString(this.router.queryManager?.getQueryParams()) || "";
1972
- const fullURL = queryString ? `${dataURL}?${queryString}` : dataURL;
1973
- this.log("debug", `Fetching data from: ${fullURL}`);
1974
- const response = await fetch(fullURL, {
1975
- method: "GET",
1976
- headers: {
1977
- "Content-Type": "application/json",
1978
- "Accept": "application/json"
1979
- }
1980
- });
1981
- if (!response.ok) {
1982
- throw new Error(`HTTP error! status: ${response.status}`);
1983
- }
1984
- const data = await response.json();
1985
- if (typeof data !== "object" || data === null) {
1986
- throw new Error("Invalid data format: expected object");
1987
- }
1988
- return data;
1989
- } catch (error) {
1990
- this.log("error", "Failed to fetch component data:", error);
1991
- throw error;
1992
- }
1993
- }
1994
- /**
1995
- * 캐시 무효화
1996
- */
1997
- invalidateCache(routeName) {
1998
- if (this.router.cacheManager) {
1999
- this.router.cacheManager.invalidateComponentCache(routeName);
2000
- }
2001
- this.log("debug", `Cache invalidated for route: ${routeName}`);
2002
- }
2003
- /**
2004
- * 통계 정보 반환
2005
- */
2006
- getStats() {
2007
- return {
2008
- environment: this.config.environment,
2009
- srcPath: this.config.srcPath,
2010
- routesPath: this.config.routesPath,
2011
- useLayout: this.config.useLayout,
2012
- useComponents: this.config.useComponents
2013
- };
2014
- }
2015
- /**
2016
- * 페이지 제목 생성
2017
- */
2018
- generatePageTitle(routeName) {
2019
- return this.toPascalCase(routeName).replace(/([A-Z])/g, " $1").trim();
2020
- }
2021
2385
  /**
2022
2386
  * 로깅 래퍼 메서드
2023
2387
  */
@@ -2030,6 +2394,18 @@ ${template}`;
2030
2394
  * 정리 (메모리 누수 방지)
2031
2395
  */
2032
2396
  destroy() {
2397
+ if (this.formHandler) {
2398
+ this.formHandler.destroy();
2399
+ this.formHandler = null;
2400
+ }
2401
+ if (this.apiHandler) {
2402
+ this.apiHandler.destroy();
2403
+ this.apiHandler = null;
2404
+ }
2405
+ if (this.componentLoader) {
2406
+ this.componentLoader.dispose();
2407
+ this.componentLoader = null;
2408
+ }
2033
2409
  this.log("debug", "RouteLoader destroyed");
2034
2410
  this.router = null;
2035
2411
  }
@@ -2306,176 +2682,6 @@ var ErrorHandler = class {
2306
2682
  }
2307
2683
  };
2308
2684
 
2309
- // src/core/ComponentLoader.js
2310
- var ComponentLoader = class {
2311
- constructor(router = null, options = {}) {
2312
- this.config = {
2313
- componentsPath: options.componentsPath || "/components",
2314
- // srcPath 기준 상대 경로
2315
- debug: options.debug || false,
2316
- environment: options.environment || "development",
2317
- ...options
2318
- };
2319
- this.router = router;
2320
- this.loadingPromises = /* @__PURE__ */ new Map();
2321
- this.unifiedComponents = null;
2322
- }
2323
- /**
2324
- * 로깅 래퍼 메서드
2325
- */
2326
- log(level, ...args) {
2327
- if (this.router?.errorHandler) {
2328
- this.router.errorHandler.log(level, "ComponentLoader", ...args);
2329
- }
2330
- }
2331
- /**
2332
- * 컴포넌트를 비동기로 로드
2333
- */
2334
- async loadComponent(componentName) {
2335
- if (!componentName || typeof componentName !== "string") {
2336
- throw new Error("Component name must be a non-empty string");
2337
- }
2338
- if (this.loadingPromises.has(componentName)) {
2339
- return this.loadingPromises.get(componentName);
2340
- }
2341
- const loadPromise = this._loadComponentFromFile(componentName);
2342
- this.loadingPromises.set(componentName, loadPromise);
2343
- try {
2344
- const component = await loadPromise;
2345
- return component;
2346
- } catch (error) {
2347
- throw error;
2348
- } finally {
2349
- this.loadingPromises.delete(componentName);
2350
- }
2351
- }
2352
- /**
2353
- * 파일에서 컴포넌트 로드
2354
- */
2355
- async _loadComponentFromFile(componentName) {
2356
- const componentRelativePath = `${this.config.componentsPath}/${componentName}.js`;
2357
- let componentPath;
2358
- if (this.router && this.router.config.srcPath) {
2359
- const srcPath = this.router.config.srcPath;
2360
- if (srcPath.startsWith("http")) {
2361
- const cleanSrcPath = srcPath.endsWith("/") ? srcPath.slice(0, -1) : srcPath;
2362
- const cleanComponentPath = componentRelativePath.startsWith("/") ? componentRelativePath : `/${componentRelativePath}`;
2363
- componentPath = `${cleanSrcPath}${cleanComponentPath}`;
2364
- } else {
2365
- componentPath = this.router.resolvePath(`${srcPath}${componentRelativePath}`);
2366
- }
2367
- } else {
2368
- componentPath = this.router ? this.router.resolvePath(`/src${componentRelativePath}`) : `/src${componentRelativePath}`;
2369
- }
2370
- try {
2371
- const module = await import(componentPath);
2372
- const component = module.default;
2373
- if (!component) {
2374
- throw new Error(`Component '${componentName}' has no default export`);
2375
- }
2376
- if (!component.name) {
2377
- component.name = componentName;
2378
- }
2379
- this.log("debug", `Component '${componentName}' loaded successfully`);
2380
- return component;
2381
- } catch (error) {
2382
- this.log("error", `Failed to load component '${componentName}':`, error);
2383
- throw new Error(`Component '${componentName}' not found: ${error.message}`);
2384
- }
2385
- }
2386
- /**
2387
- * 컴포넌트 모듈 클리어
2388
- */
2389
- clearComponents() {
2390
- this.loadingPromises.clear();
2391
- this.unifiedComponents = null;
2392
- this.log("debug", "All components cleared");
2393
- }
2394
- /**
2395
- * 환경에 따른 모든 컴포넌트 로딩 (캐싱 지원)
2396
- */
2397
- async loadAllComponents() {
2398
- if (this.unifiedComponents) {
2399
- this.log("debug", "Using existing unified components");
2400
- return this.unifiedComponents;
2401
- }
2402
- if (this.config.environment === "production") {
2403
- return await this._loadProductionComponents();
2404
- }
2405
- return await this._loadDevelopmentComponents();
2406
- }
2407
- /**
2408
- * 운영 모드: 통합 컴포넌트 로딩
2409
- */
2410
- async _loadProductionComponents() {
2411
- try {
2412
- const componentsPath = `${this.router?.config?.routesPath || "/routes"}/_components.js`;
2413
- this.log("info", "[PRODUCTION] Loading unified components from:", componentsPath);
2414
- const componentsModule = await import(componentsPath);
2415
- if (typeof componentsModule.registerComponents === "function") {
2416
- this.unifiedComponents = componentsModule.components || {};
2417
- this.log("info", `[PRODUCTION] Unified components loaded: ${Object.keys(this.unifiedComponents).length} components`);
2418
- return this.unifiedComponents;
2419
- } else {
2420
- throw new Error("registerComponents function not found in components module");
2421
- }
2422
- } catch (error) {
2423
- this.log("warn", "[PRODUCTION] Failed to load unified components:", error.message);
2424
- this.unifiedComponents = {};
2425
- return {};
2426
- }
2427
- }
2428
- /**
2429
- * 개발 모드: 개별 컴포넌트 로딩
2430
- */
2431
- async _loadDevelopmentComponents() {
2432
- const componentNames = this._getComponentNames();
2433
- const components = {};
2434
- this.log("info", `[DEVELOPMENT] Loading individual components: ${componentNames.join(", ")}`);
2435
- for (const name of componentNames) {
2436
- try {
2437
- const component = await this.loadComponent(name);
2438
- if (component) {
2439
- components[name] = component;
2440
- }
2441
- } catch (loadError) {
2442
- this.log("warn", `[DEVELOPMENT] Failed to load component ${name}:`, loadError.message);
2443
- }
2444
- }
2445
- this.unifiedComponents = components;
2446
- this.log("info", `[DEVELOPMENT] Individual components loaded: ${Object.keys(components).length} components`);
2447
- return components;
2448
- }
2449
- /**
2450
- * 컴포넌트 이름 목록 가져오기
2451
- */
2452
- _getComponentNames() {
2453
- if (Array.isArray(this.config.componentNames) && this.config.componentNames.length > 0) {
2454
- return [...this.config.componentNames];
2455
- }
2456
- return [
2457
- "Button",
2458
- "Modal",
2459
- "Card",
2460
- "Toast",
2461
- "Input",
2462
- "Tabs",
2463
- "Checkbox",
2464
- "Alert",
2465
- "DynamicInclude",
2466
- "HtmlInclude"
2467
- ];
2468
- }
2469
- /**
2470
- * 메모리 정리
2471
- */
2472
- dispose() {
2473
- this.clearComponents();
2474
- this.log("debug", "ComponentLoader disposed");
2475
- this.router = null;
2476
- }
2477
- };
2478
-
2479
2685
  // src/viewlogic-router.js
2480
2686
  var ViewLogicRouter = class {
2481
2687
  constructor(options = {}) {
@@ -2484,7 +2690,6 @@ var ViewLogicRouter = class {
2484
2690
  this.currentHash = "";
2485
2691
  this.currentVueApp = null;
2486
2692
  this.previousVueApp = null;
2487
- this.componentLoader = null;
2488
2693
  this.transitionInProgress = false;
2489
2694
  this.isReady = false;
2490
2695
  this.readyPromise = null;
@@ -2511,8 +2716,6 @@ var ViewLogicRouter = class {
2511
2716
  routesPath: "/routes",
2512
2717
  // 프로덕션 라우트 경로
2513
2718
  enableErrorReporting: true,
2514
- useComponents: true,
2515
- componentNames: ["Button", "Modal", "Card", "Toast", "Input", "Tabs", "Checkbox", "Alert", "DynamicInclude", "HtmlInclude"],
2516
2719
  useI18n: false,
2517
2720
  defaultLanguage: "ko",
2518
2721
  i18nPath: "/i18n",
@@ -2621,21 +2824,6 @@ var ViewLogicRouter = class {
2621
2824
  if (this.config.authEnabled) {
2622
2825
  this.authManager = new AuthManager(this, this.config);
2623
2826
  }
2624
- if (this.config.useComponents) {
2625
- try {
2626
- this.componentLoader = new ComponentLoader(this, {
2627
- ...this.config,
2628
- basePath: `${this.config.basePath}/components`,
2629
- cache: true,
2630
- componentNames: this.config.componentNames
2631
- });
2632
- await this.componentLoader.loadAllComponents();
2633
- this.log("info", "ComponentLoader initialized successfully");
2634
- } catch (componentError) {
2635
- this.log("warn", "ComponentLoader initialization failed, continuing without components:", componentError.message);
2636
- this.componentLoader = null;
2637
- }
2638
- }
2639
2827
  this.isReady = true;
2640
2828
  this.init();
2641
2829
  } catch (error) {