모달

Modal

화면 전체를 어둡게 덮고 그 위에 뜨는, 응답하기 전까지 뒤의 페이지와 상호작용을 막는 대화상자.

다른 이름: DialogModal dialogPopup dialog
···
html
<div class="app-bg"><div class="bar" style="width:60%"></div><div class="bar" style="width:88%"></div><div class="bar" style="width:40%"></div></div>
<div class="overlay" id="ov">
  <div class="modal" role="dialog" aria-modal="true" aria-labelledby="mt">
    <h2 id="mt">항목을 삭제할까요?</h2>
    <p>삭제하면 되돌릴 수 없어요.</p>
    <div class="row"><button class="ghost">취소</button><button class="danger">삭제</button></div>
  </div>
</div>
css
.app-bg{position:absolute;inset:0;padding:18px;display:grid;gap:10px;align-content:start}
.bar{height:10px;border-radius:5px;background:var(--line)}
.overlay{position:absolute;inset:0;background:rgba(10,10,16,.5);display:grid;place-items:center;opacity:0;pointer-events:none;transition:opacity .22s}
.overlay[data-open]{opacity:1;pointer-events:auto}
.modal{width:min(260px,88%);background:var(--surface);border-radius:14px;padding:18px;box-shadow:0 20px 50px rgba(0,0,0,.35);transform:scale(.92) translateY(8px);transition:transform .22s}
.overlay[data-open] .modal{transform:scale(1) translateY(0)}
.modal h2{margin:0 0 6px;font-size:15px}
.modal p{margin:0 0 14px;color:var(--muted);font-size:12px}
.row{display:flex;gap:8px;justify-content:flex-end}
.row button{border:0;border-radius:8px;padding:7px 14px;font-size:12px;font-weight:600;cursor:pointer}
.ghost{background:var(--bg);color:var(--fg);border:1px solid var(--line) !important}
.danger{background:#e5484d;color:#fff}
js
const ov = document.getElementById('ov');
let auto = true;
ov.querySelectorAll('button').forEach((b) => b.addEventListener('click', () => { auto = false; ov.removeAttribute('data-open'); }));
ov.addEventListener('click', (e) => { if (e.target === ov) { auto = false; ov.removeAttribute('data-open'); } });
function loop() { if (!auto) return; ov.setAttribute('data-open', ''); setTimeout(() => { if (auto) { ov.removeAttribute('data-open'); setTimeout(loop, 1200); } }, 2200); }
ov.setAttribute('data-open', '');
setTimeout(loop, 2200);

네이티브 <dialog showModal()>을 쓰면 포커스 트랩과 최상위 레이어링을 브라우저가 대신 해줍니다. 직접 만든다면 role="dialog" aria-modal="true"를 주고, 열릴 때 포커스를 모달 안(보통 제목이나 첫 입력 요소)으로 옮기고, Tab이 모달 밖으로 나가지 않게 가두고, 닫히면 모달을 연 버튼으로 포커스를 되돌려야 합니다.

드로어와 자주 비교됩니다. 모달은 화면 중앙에 뜨고 "지금 이것부터 처리하라"는 압박이 강한 반면, 드로어는 가장자리에서 슬라이드해 들어오고 필터·설정처럼 좀 더 가벼운 보조 작업에 쓰이며 때로는 뒤 콘텐츠와 함께 조작 가능한 비모달로도 만듭니다.

Esc로 닫히고, 배경(backdrop) 클릭으로도 닫히게 하는 게 관례지만 데이터를 잃을 수 있는 폼이라면 확인을 한 번 더 받아야 합니다.

언제 쓰나

삭제 확인처럼 사용자의 결정 없이는 진행할 수 없는 순간에만 씁니다. 알림성 메시지에는 모달 대신 토스트가 낫습니다.