티스토리 뷰

WEB/JavaScript

[JavaScript] 모달 팝업

silverline79 2026. 1. 3. 00:49

모달은 단순히 띄우는 것보다 **"어떻게 닫느냐"**에 대한 로직 설계가 더 중요합니다.

보통 모달은 세 가지 방법으로 닫히게 설계하죠.

  • 닫기(X) 버튼 클릭
  • 배경(Dimmed 영역) 클릭
  • ESC 키 누르기

로직 설계 (이벤트 위임)

여기서 퍼블리셔가 자바스크립트를 직접 짤 때 가장 많이 실수하는 부분이 **'배경 클릭 시 닫기'**입니다. 모달의 배경을 클릭하면 닫혀야 하지만, 모달 내부의 **'하얀 박스(컨텐츠)'**를 클릭했을 때는 닫히면 안 되기 때문입니다.

여기서 질문! 모달 배경(modal-overlay)에 클릭 이벤트를 걸었을 때, 그 안에 있는 컨텐츠 박스를 눌러도 이벤트가 부모로 전달되어 모달이 닫혀버리는 현상이 발생합니다.

이처럼 **"자식 요소에서 발생한 이벤트가 부모 요소로 전달되는 현상"**을 무엇이라고 부를까요? (힌트: 거품이 올라가는 모습과 비슷합니다. ) 이 용어를 알면 이벤트 제어가 훨씬 쉬워집니다.

그 현상은 바로 이벤트 버블링(Event Bubbling) 🫧이라고 합니다. 마치 물속의 거품이 수면으로 올라오듯, 자식 요소에서 발생한 클릭 이벤트가 부모 요소로 계속해서 전달되는 성질을 말해요.

모달 배경을 눌렀을 때만 팝업이 닫히게 하려면, 이 '거품'이 위로 올라가지 않도록 중간에 터뜨려줘야 합니다. 이때 사용하는 아주 중요한 자바스크립트 명령어가 있어요.

그 명령어는 **event.stopPropagation()**입니다. 말 그대로 이벤트의 전파(propagation)를 멈추게(stop) 하는 역할이죠.


모달 닫기 로직 적용해보기

자, 이제 이 지식을 활용해 모달의 '하얀 박스(컨텐츠)' 부분을 클릭했을 때는 팝업이 닫히지 않도록 방어막을 쳐볼까요?

const modalViewBtn = document.querySelector('.btn-modal-view');
const modalOverlay = document.querySelector('.modal-overlay'); // 검은 배경
const modalContent = document.querySelector('.modal-content'); // 하얀 박스


// 0. 모달보기 버튼 클릭시
modalViewBtn.addEventListener('click', () => {
	// 클래스를 추가할 때
    modalContent.classList.add('active');
	modalOverlay.classList.add('active');
});

// 1. 배경을 클릭하면 모달을 닫습니다.
modalOverlay.addEventListener('click', () => {
  modalOverlay.classList.remove('active');
});

// 2. 컨텐츠 박스를 클릭했을 때...
modalContent.addEventListener('click', (event) => {
  // 클릭 이벤트가 부모(배경)로 퍼지는 것을 막습니다.
  event.stopPropagation(); 
  
  // 이제 컨텐츠를 클릭해도 팝업이 닫히지 않습니다!
  console.log("컨텐츠 클릭! 배경으로 이벤트가 전달되지 않아요.");
});

event.stopPropagation()이 바로 그 '거품(버블링)'을 터뜨려주는 핵심 열쇠입니다.

순수 자바스크립트에서 event.stopPropagation()은 독립적인 명령어로 사용되며, 특정 요소를 다시 찾을 필요 없이 그 자리에서 전파를 즉시 중단시킵니다.


함수로 정의하고 호출하기

/* 모달: 속성 변경 및 전파 방지 */
function openModal(modal, targetImg, src) {
  targetImg.setAttribute('src', src); // 이미지 경로 교체
  modal.classList.add('active');      // 모달 열기
}

 

모달 호출 (속성값 활용)

모달은 클릭한 이미지의 src 값을 가져와서 함수로 쏴주는 과정이 필요합니다.

const galleryImgs = document.querySelectorAll('.gallery-item img');
const modal = document.querySelector('.modal-overlay');
const modalImg = document.querySelector('.modal-full-img');

galleryImgs.forEach(img => {
  img.addEventListener('click', () => {
    // 1. 클릭한 이미지의 src 주소를 따옵니다.
    const currentSrc = img.getAttribute('src');
    
    // 2. 함수를 호출하며 (모달창, 모달안의이미지태그, 주소)를 전달!
    openModal(modal, modalImg, currentSrc);
  });
});

// [추가] 모달 배경 클릭 시 닫기
modal.addEventListener('click', () => {
  modal.classList.remove('active');
});

// [추가] 버블링 방지 (컨텐츠 박스 클릭 시 안 닫히게)
const modalContent = document.querySelector('.modal-content');
modalContent.addEventListener('click', (e) => {
  e.stopPropagation();
});

 


💡 실무 팁: this를 쓸 때 주의할 점

함수 안에서 this를 쓰고 싶다면, addEventListener의 콜백 함수를 화살표 함수(() => {})가 아닌 **일반 함수(function() {})**로 써야 합니다.

  • 화살표 함수: this가 내가 클릭한 버튼이 아닐 수 있음.
  • 일반 함수: this가 정확히 클릭된 그 요소를 가리킴.

※ 해당 내용은 Google Gmini3.0과 함께 공부한 내용입니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
글 보관함