🪟 HTML · CSS · JavaScript · UI 패턴

모달 팝업 (Modal Popup)
완전 정복 가이드

페이지 위에 레이어로 뜨는 그 창, 모달.
개념부터 3가지 유형 구현까지 — 복붙 한 번에 내 사이트에 바로 적용해요.

⏱️ 약 15분 📅 2026년 4월 🏷️ HTML · CSS · JS 👶 입문 난이도
① 개념

모달(Modal)이란 무엇인가요?

현재 페이지를 떠나지 않고, 그 위에 레이어처럼 덮여서 나타나는 다이얼로그 창

웹사이트에서 버튼을 클릭하면 화면이 어두워지면서 가운데에 창이 하나 떠오르는 걸 본 적 있죠? 그게 바로 모달 팝업이에요. 새 페이지로 이동하는 게 아니라, 현재 페이지 위에 반투명 오버레이를 깔고 그 위에 콘텐츠 박스(.modal-box)를 올리는 방식이에요.

모달은 사용자의 주의를 특정 콘텐츠에 집중시킬 때 탁월해요. 삭제 확인, 로그인 폼, 이미지 확대 보기, 쿠키 동의 안내 등 "잠깐 이것 좀 봐주세요"가 필요한 상황에서 가장 많이 사용돼요. 구현 원리는 생각보다 단순해요 — CSS position: fixedz-index, 그리고 JavaScript 클래스 토글 몇 줄이면 완성이에요.

모달이 열릴 때는 배경 스크롤을 막아야 자연스러워요. JavaScript에서 document.body.style.overflow = 'hidden'을 설정하면 모달 뒤 페이지가 움직이지 않아요. 모달을 닫을 때는 반드시 overflow를 원래대로 복구해야 한다는 것도 기억해주세요.

🎯

집중 유도

배경을 어둡게 만들어 사용자의 시선을 모달 콘텐츠에만 집중시켜요. 중요한 메시지나 선택을 놓치지 않게 도와줘요.

🔄

페이지 이동 없음

현재 페이지 맥락을 그대로 유지하면서 추가 정보나 폼을 보여줄 수 있어요. UX가 부드럽고 자연스러워요.

⚙️

다양한 용도

알림, 확인 다이얼로그, 로그인 폼, 이미지 확대, 쿠키 동의 등 수많은 UI 패턴에 활용할 수 있어요.

💻

순수 코드로 구현

별도 라이브러리 없이 HTML + CSS + JS 30줄 내외로 완전히 구현할 수 있어요. 가볍고 빠르게 동작해요.

💡

모달 vs 팝업 vs 드로어 차이모달은 페이지 가운데 뜨는 레이어, 팝업은 새 브라우저 창(요즘은 차단됨), 드로어(Drawer)는 화면 옆에서 슬라이드로 열리는 패널이에요. 대부분 "팝업"이라고 말하지만 실제로 구현하는 건 모달인 경우가 많아요.

언제 모달을 사용하면 좋을까요?

상황 적합한 모달 유형 효과
데이터 삭제 · 위험한 작업 전 확인 다이얼로그 (취소/확인) 실수 방지
갤러리 · 포트폴리오 이미지 라이트박스 (이미지 확대 보기) 몰입감 향상
회원가입 · 로그인 유도 폼 모달 (인풋 포함) 전환율 ↑
공지사항 · 이벤트 알림 기본 알림 모달 노출 강화
쿠키 · 개인정보 동의 하단 고정형 또는 중앙 모달 법적 요건
⚠️

주의: 모달은 사용자 흐름을 강제로 끊어요. 꼭 필요한 경우에만 사용하고, 항상 쉽게 닫을 수 있어야 해요. 닫기 버튼(×) + 오버레이 클릭 + ESC 키, 세 가지 방법 모두 지원하는 게 좋은 UX예요.

③ 코드 해설

모달 만드는 코드 완전 분석

HTML 구조 → CSS 스타일링 → JavaScript 동작 순서로 한 줄씩 이해해요

HTML: 오버레이 + 박스 구조

.modal-overlay
반투명 배경 레이어
화면 전체를 덮는 어두운 레이어예요. 클릭하면 모달이 닫혀요. position: fixed; inset: 0으로 뷰포트 전체를 채워요.
inset: 0; /* top·right·bottom·left 모두 0 */
.modal-box
콘텐츠 박스
실제 내용이 들어가는 흰 박스예요. 오버레이 위에 올라가서 중앙에 배치돼요. max-width로 너비를 제한해요.
max-width: 440px; /* 적당한 너비 */
z-index: 1000
레이어 순서 제어
모달이 다른 모든 요소 위에 뜨도록 큰 z-index를 설정해요. 네비게이션(z-index:500)보다 높아야 해요.
z-index: 1000; /* nav보다 높게 */
backdrop-filter: blur
배경 흐림 효과
배경을 단순히 어둡게만 하지 않고 흐리게 만드는 효과예요. 현대적이고 깔끔한 느낌을 줘요.
backdrop-filter: blur(4px);
modal.html — 완전한 예시 코드
<!-- HTML 구조 -->
<button onclick="openModal()">모달 열기</button>

<div class="modal-overlay" id="myModal"
     onclick="handleOverlayClick(event)">
  <div class="modal-box">
    <button class="modal-close" onclick="closeModal()"></button>
    <h2>모달 제목</h2>
    <p>모달 내용이 여기에 들어가요.</p>
    <button onclick="closeModal()">확인</button>
  </div>
</div>

/* CSS 스타일 */
.modal-overlay {
  position: fixed;
  inset: 0;                         /* top:0; right:0; bottom:0; left:0 */
  z-index: 1000;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  visibility: hidden;
  transition: opacity .22s ease, visibility .22s ease;
}
.modal-overlay.open {
  opacity: 1;
  visibility: visible;
}
.modal-box {
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 24px 64px rgba(0,0,0,.18);
  width: 100%;
  max-width: 440px;
  padding: 32px;
  position: relative;
  transform: translateY(16px) scale(.97);
  transition: transform .25s cubic-bezier(.34,1.56,.64,1);
}
.modal-overlay.open .modal-box {
  transform: translateY(0) scale(1);
}
.modal-close {
  position: absolute;
  top: 16px; right: 16px;
  background: #f3f4f6;
  border: none;
  border-radius: 50%;
  width: 30px; height: 30px;
  cursor: pointer;
}

// JavaScript
function openModal() {
  document.getElementById('myModal').classList.add('open');
  document.body.style.overflow = 'hidden';  // 배경 스크롤 막기
}

function closeModal() {
  document.getElementById('myModal').classList.remove('open');
  document.body.style.overflow = '';         // 스크롤 복구
}

function handleOverlayClick(e) {
  if (e.target === e.currentTarget) closeModal(); // 오버레이만 클릭 시
}

// ESC 키로 닫기
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') closeModal();
});

핵심 포인트 3가지

visibility: hidden + opacity: 0을 함께 쓰면 display: none과 달리 CSS 트랜지션(페이드 효과)이 부드럽게 작동해요.

transform: translateY(16px) scale(.97)로 시작해서 translateY(0) scale(1)로 끝나면 모달이 살짝 위로 올라오며 열리는 느낌을 줘요.

③ 오버레이 클릭 감지 시 e.target === e.currentTarget 조건을 확인해야 해요. 모달 박스 안쪽을 클릭해도 닫히는 버그를 방지해요.

④ 인터랙티브 데모

직접 열어보는 모달 데모

아래 버튼을 클릭해서 3가지 모달 유형을 직접 체험해보세요. 오버레이 클릭이나 ESC 키로 닫을 수 있어요.

ℹ️

모달을 닫는 방법: × 버튼 클릭 / 어두운 배경(오버레이) 클릭 / 키보드 ESC 키 누르기

⑤ AI 프롬프트 팁

바이브 코딩 — AI에게 이렇게 요청하세요

모달을 만들 때 AI에게 어떻게 설명하면 좋은지, 실제로 잘 동작하는 프롬프트를 소개해요.

✨ 바이브코더 TIP

AI에게 모달을 요청할 때는 어떤 상황에서 열리는지, 버튼이 몇 개인지, 확인 시 어떤 동작을 할지를 구체적으로 알려주면 훨씬 정확한 코드를 받을 수 있어요.

아래 프롬프트를 복붙해서 바로 사용해보세요 👇

내 사이트에 확인 모달을 만들어줘.
"삭제하시겠습니까?" 메시지와 취소/확인 버튼이 있고,
확인 클릭 시 console.log('삭제 완료')가 실행되게 해줘.
배경은 반투명 블러 처리해줘.
닫는 방법은 × 버튼, 오버레이 클릭, ESC 키 세 가지 모두 지원해줘.
모달이 열릴 때는 부드럽게 위로 올라오는 애니메이션도 넣어줘.

상황별 추가 프롬프트 예시

🖼️

이미지 라이트박스

img 태그를 클릭하면 모달이 열리면서 이미지가 크게 보이게 해줘. 배경은 거의 검은색으로 해줘.

📋

폼 입력 모달

이름과 이메일을 입력받는 폼이 있는 모달을 만들어줘. 제출 시 유효성 검사도 해줘.

🍪

쿠키 동의

페이지 처음 방문 시 쿠키 동의 모달이 뜨게 해줘. 동의하면 localStorage에 저장해서 다음엔 안 뜨게 해줘.

⑥ FAQ

자주 묻는 질문

모달 구현 중 막히는 부분, 여기서 해결하세요.

모달이 네비게이션 뒤에 가려져요

z-index 문제예요. 모달 오버레이에 z-index: 1000 이상의 값을 주세요. 사이트 네비게이션이 보통 z-index: 100~500을 사용하기 때문에, 모달은 그보다 훨씬 높은 값이어야 해요.

또한 부모 요소에 transform, filter, will-change 속성이 있으면 position: fixed가 해당 부모를 기준으로 동작해서 화면 전체를 덮지 못할 수 있어요. 모달을 <body>의 직계 자식으로 이동시켜 보세요.

모바일에서 모달 뒤 배경이 스크롤돼요

모달이 열릴 때 document.body.style.overflow = 'hidden'을 설정하면 배경 스크롤이 막혀요. 모달을 닫을 때는 반드시 document.body.style.overflow = ''로 복구해야 해요.

iOS Safari에서는 추가로 document.body.style.position = 'fixed'document.body.style.width = '100%'가 필요할 수 있어요. 단, 이 경우 스크롤 위치가 초기화되므로 열기 전 스크롤 위치를 저장해뒀다가 닫을 때 복구해야 해요.

모달 열 때 애니메이션을 넣고 싶어요

CSS transition을 활용하면 돼요. visibility: hidden + opacity: 0 상태에서 시작해서, .open 클래스가 추가될 때 opacity: 1로 전환하면 부드러운 페이드인이 만들어져요.

더 생동감 있는 효과를 원한다면 모달 박스에 transform: translateY(20px)을 초기값으로 주고, 열릴 때 translateY(0)으로 전환하세요. cubic-bezier(.34,1.56,.64,1) 이징을 쓰면 살짝 튀어오르는 느낌이 나요.

모달 안에서 스크롤이 안 돼요 (내용이 잘려요)

모달 박스(.modal-box)에 max-height: 90vh; overflow-y: auto;를 추가하면 내용이 길어질 때 박스 안에서 스크롤이 생겨요. 모바일 대응을 위해 padding: 24px도 조정해주세요.

모달 헤더(제목 + 닫기버튼)와 푸터(버튼 영역)는 고정하고 가운데 콘텐츠만 스크롤되게 하려면 flex 레이아웃을 활용해보세요.

모달 여러 개를 동시에 관리하려면 어떻게 해요?

각 모달에 고유한 id를 붙이고, openModal('modalId')처럼 ID를 인자로 받는 함수를 만들어요. 함수 안에서 document.getElementById(id)로 특정 모달만 열 수 있어요.

현재 열린 모달을 추적하는 변수를 하나 두면, ESC 키 이벤트도 하나로 처리할 수 있어요: let currentModal = null;로 선언하고, 열 때 currentModal = modal, 닫을 때 currentModal = null로 관리하세요.

화면이 작을 때 모달이 너무 크게 나와요

오버레이에 padding: 16px 이상을 주면 모달 박스가 화면 가장자리에 딱 붙지 않아요. 모달 박스의 width: 100%; max-width: 440px 설정이 모바일에서는 100%, 큰 화면에서는 440px로 제한되도록 동작해요.

세로가 너무 긴 경우엔 max-height: calc(100vh - 48px); overflow-y: auto;를 추가해서 모달이 화면을 넘어가지 않도록 막아요.