/* 로고 그리드 자동 렌더링 * * data-logo-category 속성이 붙은 빈 컨테이너를 찾아 /api/logos/ 를 호출하고, * 돌아온 목록만큼 박스를 만들어 로고를 채워 넣는다. * 화면에 를 하나씩 손으로 박아 넣던 방식을 대체하는 것이라, * 관리자가 assets/logos// 폴더에 파일만 넣고 빼면 그대로 반영된다. * * 빈 폴더 / 폴더 없음 / API 실패 모두 에러 없이 조용히 섹션을 숨긴다. */ (function () { 'use strict'; function hideSection(el) { // 그리드만 비우면 제목과 안내 문구만 덩그러니 남으므로 섹션 단위로 숨긴다. // data-logo-empty="keep" 이면 컨테이너만 비우고 섹션은 그대로 둔다. if (el.dataset.logoEmpty === 'keep') { el.hidden = true; return; } var box = el.closest('[data-logo-section]') || el.closest('section') || el; box.style.display = 'none'; } function render(el, items) { var frag = document.createDocumentFragment(); items.forEach(function (item) { if (!item || !item.url) { return; } var cell = document.createElement('div'); cell.className = 'logo-cell'; var img = document.createElement('img'); img.className = 'logo-img'; img.src = item.url; img.alt = item.alt || ''; img.loading = 'lazy'; img.decoding = 'async'; // 개별 파일이 깨져 있어도 그리드 전체가 망가지지 않도록 해당 칸만 제거한다. img.addEventListener('error', function () { cell.remove(); }); cell.appendChild(img); frag.appendChild(cell); }); el.textContent = ''; el.appendChild(frag); } function load(el) { var category = el.dataset.logoCategory; if (!category) { return; } fetch('/api/logos/' + encodeURIComponent(category), { headers: { Accept: 'application/json' } }) .then(function (res) { if (!res.ok) { throw new Error('HTTP ' + res.status); } return res.json(); }) .then(function (items) { if (!Array.isArray(items) || items.length === 0) { hideSection(el); return; } render(el, items); }) .catch(function () { // 네트워크 오류·404 등 — 빈 자리를 남기지 않고 조용히 접는다. hideSection(el); }); } function init() { var nodes = document.querySelectorAll('[data-logo-category]'); for (var i = 0; i < nodes.length; i++) { load(nodes[i]); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();