style: 표/패널 UI 전면 통일 + 화면설명 드로어·글씨크기·탭 개선

표 디자인
- 모든 표를 가벼운 스타일로 통일(.data-table 경량화: 작은 회색 헤더·연한 구분선·hover)
- 표/패널 바깥 테두리 둥글게(rounded-lg) 일괄 적용, 표 래퍼에 패딩 카드(p-4) 통일
- 표 헤더·데이터 정렬을 전 화면 좌측 기준으로 통일
  - .data-table th/td text-align:left (전역), 흩어진 center/right 정렬 정리
  - 재디자인 Tailwind 표(포장단위·단가·기본코드·담당자·업체·판매대행소·무료대상자·지정판매소)도 셀 좌측화
- 기본정보관리 등 나머지 소메뉴 표를 기본 코드 관리 스타일(가벼운 표·상태 pill)로 재디자인

워크스페이스/공통
- "이 화면 설명" → 새 탭 대신 우측 드로어 팝업(현재 화면과 동시에 보기, Esc·드래그 폭조절)
- 상단바 글씨 크기 조절(A−/A+), 작업 내용에 zoom 적용
- 탭 최대치 도달 시 자동 삭제 대신 안내 토스트, "모두 닫기"(업무 현황 탭은 보존)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
taekyoungc
2026-06-11 17:26:36 +09:00
parent 912ffdbe23
commit a8afaf4af2
108 changed files with 1198 additions and 965 deletions

View File

@@ -43,11 +43,22 @@ tailwind.config = {
.embed-flash.ok { background: #ecfdf5; border: 1px solid #a7f3d0; color: #065f46; }
.embed-flash.err { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }
.data-table { width: 100%; border-collapse: collapse; font-family: 'Pretendard', 'Malgun Gothic', 'Noto Sans KR', sans-serif; }
.data-table th, .data-table td { border: 1px solid #ccc; padding: 4px 8px; white-space: nowrap; font-size: 13px; }
.data-table th { background-color: #e9ecef; text-align: center; vertical-align: middle; font-weight: bold; color: #333; }
.data-table tbody tr:nth-child(even) td { background-color: #f9f9f9; }
.data-table tbody tr:hover td { background-color: #e6f7ff !important; }
.data-table { font-size: 13px; }
.data-table th, .data-table td { text-align: left; padding: 0.55rem 0.5rem; white-space: nowrap; border: 0; border-bottom: 1px solid #e5e7eb; }
.data-table thead th { font-size: 0.6875rem; font-weight: 600; color: #6b7280; background: transparent; vertical-align: middle; }
.data-table tbody td { color: #374151; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover td { background-color: #f9fafb; }
@media print { .no-print { display: none !important; } .embed-titlebar { display: none; } }
/* 화면 설명 드로어(팝업) — 현재 화면 위 오른쪽에 겹쳐 띄움 */
.help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; }
.help-drawer.open { display: flex; }
.help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; }
.help-drawer-head .hd-btns { display: flex; gap: 4px; }
.help-drawer-head .hd-btn { color: #fff; background: rgba(255,255,255,.14); border: 0; width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 13px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
.help-drawer-head .hd-btn:hover { background: rgba(255,255,255,.28); }
.help-drawer iframe { flex: 1; width: 100%; border: 0; background: #fff; }
.help-drawer-grip { position: absolute; left: -4px; top: 0; bottom: 0; width: 8px; cursor: col-resize; }
</style>
</head>
<body>
@@ -76,6 +87,20 @@ tailwind.config = {
</div>
<?php endif; ?>
</div>
<!-- 화면 설명 드로어(팝업) -->
<div id="helpDrawer" class="help-drawer no-print" aria-hidden="true">
<div class="help-drawer-grip" id="helpDrawerGrip"></div>
<div class="help-drawer-head">
<span><i class="fa-regular fa-circle-question"></i> 화면 설명</span>
<div class="hd-btns">
<a id="helpDrawerTab" href="#" class="hd-btn" title="탭으로 열기"><i class="fa-solid fa-up-right-from-square"></i></a>
<button type="button" id="helpDrawerClose" class="hd-btn" title="닫기">&times;</button>
</div>
</div>
<iframe id="helpDrawerFrame" title="화면 설명"></iframe>
</div>
<script>
(function () {
// 세션 만료 등으로 iframe 안에서 로그인 페이지가 열리면 상위 프레임을 로그인으로 보낸다.
@@ -106,19 +131,46 @@ tailwind.config = {
return u.href;
} catch (e) { return null; }
}
// "이 화면 설명" → 워크스페이스 새 탭으로 매뉴얼 열기(없으면 새 창)
// "이 화면 설명" → 현재 화면 위 오른쪽 드로어(팝업)로 띄워 동시에 보기
var drawer = document.getElementById('helpDrawer');
var dFrame = document.getElementById('helpDrawerFrame');
function withEmbedUrl(url) { try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); return x.href; } catch (e) { return url; } }
function openHelp(url) {
var u = withEmbedUrl(url);
if (dFrame.getAttribute('data-src') !== u) { dFrame.src = u; dFrame.setAttribute('data-src', u); }
var tab = document.getElementById('helpDrawerTab'); if (tab) tab.setAttribute('href', url);
drawer.classList.add('open');
}
function closeHelp() { drawer.classList.remove('open'); }
document.addEventListener('click', function (e) {
var h = e.target.closest ? e.target.closest('a.embed-help') : null;
if (!h) return;
e.preventDefault(); e.stopPropagation();
var url = h.getAttribute('href');
try {
if (window.parent && window.parent !== window && typeof window.parent.wsOpenTab === 'function') {
window.parent.wsOpenTab(url, '도움말'); return;
}
} catch (err) {}
window.open(url, '_blank');
openHelp(h.getAttribute('href'));
}, true);
document.getElementById('helpDrawerClose').addEventListener('click', closeHelp);
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeHelp(); });
// 드로어 헤더의 "탭으로 열기" → 워크스페이스 탭으로(없으면 새 창)
document.getElementById('helpDrawerTab').addEventListener('click', function (e) {
e.preventDefault();
var url = this.getAttribute('href') || '';
try { if (window.parent && window.parent !== window && typeof window.parent.wsOpenTab === 'function') { window.parent.wsOpenTab(url, '도움말'); closeHelp(); return; } } catch (err) {}
window.open(url, '_blank');
});
// 드로어 폭 드래그 조절
(function () {
var grip = document.getElementById('helpDrawerGrip'), dragging = false;
grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; document.body.style.userSelect = 'none'; });
document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; });
document.addEventListener('mouseup', function () { dragging = false; document.body.style.userSelect = ''; });
})();
// 글씨 크기(zoom) — 상단바에서 조절한 값을 적용. localStorage 공유 + storage 이벤트로 실시간 반영.
function applyFontScale() {
try { var s = parseInt(localStorage.getItem('jrj_font_scale') || '100', 10); if (!(s >= 70 && s <= 150)) s = 100; document.documentElement.style.zoom = (s / 100); } catch (e) {}
}
applyFontScale();
window.addEventListener('storage', function (e) { if (e.key === 'jrj_font_scale') applyFontScale(); });
document.addEventListener('click', function (e) {
var a = e.target.closest ? e.target.closest('a[href]') : null;

View File

@@ -50,10 +50,12 @@ html { font-size: 18px; }
</style>
<style data-purpose="table-layout">
.data-table { width: 100%; border-collapse: collapse; font-family: 'Malgun Gothic', 'Noto Sans KR', sans-serif; }
.data-table th, .data-table td { border: 1px solid #ccc; padding: 4px 8px; white-space: nowrap; font-size: 13px; }
.data-table th { background-color: #e9ecef; text-align: center; vertical-align: middle; font-weight: bold; color: #333; }
.data-table tbody tr:nth-child(even) td { background-color: #f9f9f9; }
.data-table tbody tr:hover td { background-color: #e6f7ff !important; }
.data-table { font-size: 13px; }
.data-table th, .data-table td { text-align: left; padding: 0.55rem 0.5rem; white-space: nowrap; border: 0; border-bottom: 1px solid #e5e7eb; }
.data-table thead th { font-size: 0.6875rem; font-weight: 600; color: #6b7280; background: transparent; vertical-align: middle; }
.data-table tbody td { color: #374151; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover td { background-color: #f9fafb; }
.main-content-area { height: calc(100vh - 130px); overflow: auto; }
body { overflow: hidden; }
@media print {

View File

@@ -72,10 +72,12 @@ tailwind.config = {
<?php include __DIR__ . '/../../home/_dashboard_gov_portal_chrome_css.php'; ?>
/* 업무 본문 표/유틸 (기존 사이트 레이아웃에서 계승) */
.data-table { width: 100%; border-collapse: collapse; font-family: 'Pretendard', 'Malgun Gothic', 'Noto Sans KR', sans-serif; }
.data-table th, .data-table td { border: 1px solid #ccc; padding: 4px 8px; white-space: nowrap; font-size: 13px; }
.data-table th { background-color: #e9ecef; text-align: center; vertical-align: middle; font-weight: bold; color: #333; }
.data-table tbody tr:nth-child(even) td { background-color: #f9f9f9; }
.data-table tbody tr:hover td { background-color: #e6f7ff !important; }
.data-table { font-size: 13px; }
.data-table th, .data-table td { text-align: left; padding: 0.55rem 0.5rem; white-space: nowrap; border: 0; border-bottom: 1px solid #e5e7eb; }
.data-table thead th { font-size: 0.6875rem; font-weight: 600; color: #6b7280; background: transparent; vertical-align: middle; }
.data-table tbody td { color: #374151; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover td { background-color: #f9fafb; }
@media print {
.portal-header, .sidebar, .portal-footer, .no-print, nav.portal-top-nav { display: none !important; }
body.gov-portal-shell { background: #fff; display: block; }
@@ -90,6 +92,11 @@ tailwind.config = {
<?= view('home/_dashboard_gov_portal_brand', ['brandHref' => base_url('/')]) ?>
<?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?>
<div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;">
<div class="ws-fontctl" title="글씨 크기 조절" style="display:inline-flex;align-items:center;gap:2px;background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);border-radius:6px;padding:1px;">
<button type="button" id="wsFontMinus" title="글씨 작게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:11px;line-height:1;border-radius:5px;">A</button>
<span id="wsFontPct" style="min-width:34px;text-align:center;color:#fff;font-size:.68rem;font-weight:600;">100%</span>
<button type="button" id="wsFontPlus" title="글씨 크게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:14px;line-height:1;border-radius:5px;">A+</button>
</div>
<span class="user-line">
<?php if ($lgLabel !== ''): ?><strong><?= esc($lgLabel) ?></strong> · <?php endif; ?>
<?= esc($levelName) ?> · <?= esc($mbName) ?>님
@@ -120,7 +127,7 @@ tailwind.config = {
<div style="display:flex;align-items:center;justify-content:space-between;gap:.5rem;">
<h1 class="work-titlebar" style="margin-bottom:0;"><i class="fa-solid fa-folder-open tb-ico"></i><?= esc($title) ?></h1>
<?php if ($helpUrl !== ''): ?>
<a href="<?= esc($helpUrl, 'attr') ?>" target="_blank" rel="noopener" class="no-print" style="display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .6rem;border-radius:6px;background:#fff;border:1px solid var(--border);color:#1a2b4b;text-decoration:none;font-size:.75rem;font-weight:600;white-space:nowrap;">
<a href="<?= esc($helpUrl, 'attr') ?>" rel="noopener" class="no-print portal-help" style="display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .6rem;border-radius:6px;background:#fff;border:1px solid var(--border);color:#1a2b4b;text-decoration:none;font-size:.75rem;font-weight:600;white-space:nowrap;">
<i class="fa-regular fa-circle-question"></i> 이 화면 설명
</a>
<?php endif; ?>
@@ -207,5 +214,62 @@ tailwind.config = {
else run();
})();
</script>
<!-- 화면 설명 드로어(팝업) -->
<style>
.help-drawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(460px, 92vw); background: #fff; box-shadow: -8px 0 26px rgba(0,0,0,.18); z-index: 9999; display: none; flex-direction: column; }
.help-drawer.open { display: flex; }
.help-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: .5rem .75rem; background: #1a2b4b; color: #fff; font-size: .8rem; font-weight: 700; flex-shrink: 0; }
.help-drawer-head .hd-btn { color: #fff; background: rgba(255,255,255,.14); border: 0; width: 26px; height: 26px; border-radius: 6px; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; margin-left: 4px; }
.help-drawer-head .hd-btn:hover { background: rgba(255,255,255,.28); }
.help-drawer iframe { flex: 1; width: 100%; border: 0; }
.help-drawer-grip { position: absolute; left: -4px; top: 0; bottom: 0; width: 8px; cursor: col-resize; }
</style>
<div id="helpDrawer" class="help-drawer no-print" aria-hidden="true">
<div class="help-drawer-grip" id="helpDrawerGrip"></div>
<div class="help-drawer-head">
<span><i class="fa-regular fa-circle-question"></i> 화면 설명</span>
<div><button type="button" id="helpDrawerClose" class="hd-btn" title="닫기">&times;</button></div>
</div>
<iframe id="helpDrawerFrame" title="화면 설명"></iframe>
</div>
<script>
(function () {
var drawer = document.getElementById('helpDrawer'), dFrame = document.getElementById('helpDrawerFrame');
function openHelp(url) {
var u = url; try { var x = new URL(url, location.href); x.searchParams.set('embed', '1'); u = x.href; } catch (e) {}
if (dFrame.getAttribute('data-src') !== u) { dFrame.src = u; dFrame.setAttribute('data-src', u); }
drawer.classList.add('open');
}
function closeHelp() { drawer.classList.remove('open'); }
document.addEventListener('click', function (e) {
var h = e.target.closest ? e.target.closest('a.portal-help') : null;
if (!h) return;
e.preventDefault();
openHelp(h.getAttribute('href'));
});
document.getElementById('helpDrawerClose').addEventListener('click', closeHelp);
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeHelp(); });
var grip = document.getElementById('helpDrawerGrip'), dragging = false;
grip.addEventListener('mousedown', function (e) { e.preventDefault(); dragging = true; document.body.style.userSelect = 'none'; });
document.addEventListener('mousemove', function (e) { if (!dragging) return; var w = window.innerWidth - e.clientX; drawer.style.width = Math.min(window.innerWidth * 0.92, Math.max(300, w)) + 'px'; });
document.addEventListener('mouseup', function () { dragging = false; document.body.style.userSelect = ''; });
// 글씨 크기 조절 — 본문(.work-main) 영역에 zoom 적용. 헤더/사이드바는 그대로.
var FONT_KEY = 'jrj_font_scale';
var target = document.querySelector('.work-main') || document.body;
function curScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; }
function applyScale(s) {
s = Math.min(150, Math.max(70, s));
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
target.style.zoom = (s / 100);
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
}
applyScale(curScale());
var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus');
if (plus) plus.addEventListener('click', function () { applyScale(curScale() + 10); });
if (minus) minus.addEventListener('click', function () { applyScale(curScale() - 10); });
})();
</script>
</body>
</html>

View File

@@ -76,6 +76,8 @@ if ($effectiveLgIdx) {
.ws-drag-overlay.v { cursor: col-resize; }
.ws-drag-overlay.h { cursor: row-resize; }
.ws-empty { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #94a3b8; font-size: .9rem; gap: .5rem; z-index: 2; background: #f0f4f8; }
.ws-toast { position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(10px); background: #243a5e; color: #fff; padding: .6rem 1rem; border-radius: 8px; font-size: .8rem; font-weight: 600; box-shadow: 0 8px 22px rgba(0,0,0,.25); opacity: 0; pointer-events: none; transition: opacity .2s, transform .2s; z-index: 10000; max-width: 80vw; text-align: center; }
.ws-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
</style>
</head>
<body class="gov-portal-shell">
@@ -84,6 +86,11 @@ if ($effectiveLgIdx) {
<?= view('home/_dashboard_gov_portal_brand', ['brandHref' => base_url('workspace')]) ?>
<?= view('home/_dashboard_gov_portal_topnav_click', $navPartial) ?>
<div class="portal-header-utils" style="display:flex;align-items:center;gap:.5rem;">
<div class="ws-fontctl" title="글씨 크기 조절" style="display:inline-flex;align-items:center;gap:2px;background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);border-radius:6px;padding:1px;">
<button type="button" id="wsFontMinus" title="글씨 작게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:11px;line-height:1;border-radius:5px;">A</button>
<span id="wsFontPct" style="min-width:34px;text-align:center;color:#fff;font-size:.68rem;font-weight:600;">100%</span>
<button type="button" id="wsFontPlus" title="글씨 크게" style="width:24px;height:22px;border:0;background:transparent;color:#fff;cursor:pointer;font-size:14px;line-height:1;border-radius:5px;">A+</button>
</div>
<span class="user-line">
<?php if ($lgLabel !== ''): ?><strong><?= esc($lgLabel) ?></strong> · <?php endif; ?>
<?= esc($levelName) ?> · <?= esc($mbName) ?>님
@@ -107,8 +114,10 @@ if ($effectiveLgIdx) {
<button type="button" data-mode="lr" title="2분할 (좌우)"><i class="fa-solid fa-table-columns"></i></button>
<button type="button" data-mode="tb" title="2분할 (상하)"><i class="fa-solid fa-table-columns fa-rotate-90"></i></button>
<button type="button" data-mode="quad" title="4분할"><i class="fa-solid fa-table-cells-large"></i></button>
<button type="button" id="wsCloseAll" title="모든 탭 닫기" style="margin-left:6px;width:auto;padding:0 8px;gap:4px;"><i class="fa-regular fa-rectangle-xmark"></i><span style="font-size:11px;">모두 닫기</span></button>
</div>
</div>
<div class="ws-toast" id="wsToast" role="status" aria-live="polite"></div>
<div class="ws-panels" id="wsPanels">
<div class="ws-empty" id="wsEmpty">
<i class="fa-regular fa-window-restore" style="font-size:1.6rem;opacity:.5;"></i>
@@ -133,6 +142,15 @@ if ($effectiveLgIdx) {
var slots = [null]; // 칸별로 배치된 tab id (길이 = 칸 수)
var focused = 0; // 포커스된 칸 인덱스
var toastEl = document.getElementById('wsToast'), toastTimer = null;
function showToast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove('show'); }, 2800);
}
function norm(u) { try { var a = new URL(u, location.origin); return a.pathname + a.search; } catch (e) { return u; } }
function withEmbed(u) {
try { var a = new URL(u, location.origin); a.searchParams.set('embed', '1'); return a.pathname + a.search; }
@@ -342,6 +360,20 @@ if ($effectiveLgIdx) {
b.addEventListener('click', function () { setLayout(b.getAttribute('data-mode')); });
});
// 모든 탭 닫기 — 기본 "업무 현황"(대시보드) 탭은 남긴다
var DASH_URL = '<?= base_url('/') ?>';
function closeAllTabs() {
var dashId = norm(DASH_URL);
var others = order.filter(function (id) { return id !== dashId; });
if (!others.length) return; // 업무 현황만 있으면 닫을 것 없음
if (!window.confirm('업무 현황을 제외한 모든 탭(' + others.length + '개)을 닫을까요?')) return;
others.forEach(function (id) { closeTab(id); });
if (!tabs[dashId]) openTab(DASH_URL, '업무 현황'); else placeInFocused(dashId);
showToast('업무 현황만 남기고 모두 닫았습니다.');
}
var closeAllBtn = document.getElementById('wsCloseAll');
if (closeAllBtn) closeAllBtn.addEventListener('click', closeAllTabs);
function reloadTab(id) {
var t = tabs[id];
if (!t) return;
@@ -362,7 +394,11 @@ if ($effectiveLgIdx) {
function openTab(url, title, opts) {
var id = norm(url);
if (tabs[id]) { if (!(opts && opts.noFocus)) placeInFocused(id); return; }
if (order.length >= MAX) { closeTab(order[0]); } // 오래된 탭 정리
// 최대치 도달 시: 자동 삭제하지 않고 안내만 (복원 중에는 안내 생략)
if (order.length >= MAX) {
if (!(opts && opts.noFocus)) showToast('탭은 최대 ' + MAX + '개까지 열 수 있습니다. 사용하지 않는 탭을 닫아 주세요.');
return;
}
var frame = document.createElement('iframe');
frame.className = 'ws-frame';
frame.src = withEmbed(url);
@@ -430,6 +466,22 @@ if ($effectiveLgIdx) {
}
document.addEventListener('keydown', handleShortcut);
// 글씨 크기 조절 — 각 탭(iframe) 내용에 zoom 적용. localStorage 공유 + storage 이벤트로 새 탭/실시간 반영.
var FONT_KEY = 'jrj_font_scale';
function curFontScale() { var s = parseInt(localStorage.getItem(FONT_KEY) || '100', 10); return (s >= 70 && s <= 150) ? s : 100; }
function setFontScale(s) {
s = Math.min(150, Math.max(70, s));
try { localStorage.setItem(FONT_KEY, String(s)); } catch (e) {}
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = s + '%';
Object.keys(tabs).forEach(function (k) { try { tabs[k].frame.contentDocument.documentElement.style.zoom = (s / 100); } catch (e) {} });
}
(function () {
var pct = document.getElementById('wsFontPct'); if (pct) pct.textContent = curFontScale() + '%';
var plus = document.getElementById('wsFontPlus'), minus = document.getElementById('wsFontMinus');
if (plus) plus.addEventListener('click', function () { setFontScale(curFontScale() + 10); });
if (minus) minus.addEventListener('click', function () { setFontScale(curFontScale() - 10); });
})();
// 좌측 사이드바 소메뉴 클릭 → 포커스 칸에 열기
document.querySelector('.sidebar').addEventListener('click', function (e) {
var a = e.target.closest('a[href]');