📘 12.4.2 전파 막기 (stopPropagation) - 이벤트 여행을 멈추는 따뜻한 손길
안녕하세요, 여러분. 지난 시간에 이벤트가 어떻게 부모와 자식 요소 사이를 자연스럽게 여행하는지 배웠죠? 이벤트 전파라는 아름다운 과정을 통해 자식에서 발생한 이벤트가 부모로 올라가는 버블링을 함께 확인했어요. 이제는 이 여행을 중간에 멈출 수 있는 특별한 방법을 배워보려고 합니다. 바로 stopPropagation이라는 따뜻한 손길을 사용하는 것이에요.
🧠 새로운 단어들과 친해지기
이벤트의 여행을 조절하는 데 필요한 소중한 단어들을 차근차근 알아보겠습니다.
단어 | 마음을 담은 설명 |
---|---|
stopPropagation | 이벤트 전파를 부드럽게 멈추게 하는 특별한 기능이에요. |
이벤트 전파 | 이벤트가 부모나 자식 요소로 전달되어 가는 자연스러운 과정이에요. |
이벤트 정보 묶음 | 이벤트가 발생했을 때 자동으로 만들어지는 소중한 정보 상자예요. |
전파 차단 | 이벤트가 더 이상 다른 요소로 전달되지 않도록 정중히 막는 것이에요. |
stopPropagation은 "stop(멈추다)"과 "propagation(전파)"이 합쳐진 말로, 말 그대로 이벤트의 전파를 멈춘다는 뜻이에요.
✨ stopPropagation
의 핵심 개념
때로는 이벤트가 자유롭게 여행하는 것이 어려움을 가져올 수 있어요. 예를 들어, 버튼을 클릭했는데 버튼의 이벤트와 함께 부모 요소의 이벤트까지 실행되어서 예상과 다른 결과가 나타날 수 있어요.
stopPropagation
은 이런 상황에서 "여기서 잠시 멈춰주세요"라고 정중히 요청할 수 있는 특별한 기능이에요. 이 기능을 사용하면 이벤트가 현재 요소에서 처리된 후, 더 이상 부모 요소로 전파되지 않아요.
마음을 담은 비유: 아파트 엘리베이터의 정지 버튼
stopPropagation
을 좀 더 친근하게 이해하기 위해 '아파트 엘리베이터의 정지 버튼'에 비유해 볼게요.
엘리베이터가 1층에서 10층까지 올라가는 중이라고 상상해보세요. 평소에는 1층 → 3층 → 5층 → 7층 → 10층 순서로 멈추면서 사람들을 태우고 내려줘요. 이것이 바로 이벤트 버블링과 같은 과정이에요.
그런데 5층에서 누군가가 정지 버튼을 눌렀다고 해보세요. 그러면 엘리베이터는 5층에서 조용히 멈추고, 더 이상 7층이나 10층으로 올라가지 않아요. 하지만 이미 지나온 1층과 3층에는 영향을 주지 않아요.
stopPropagation
도 정확히 이와 같습니다. 이벤트가 자식에서 부모로 올라가는 중에 "여기서 멈춰주세요"라고 요청하면, 그 지점에서 이벤트 전파가 자연스럽게 중단돼요. 더 위에 있는 부모 요소들의 이벤트는 실행되지 않지만, 이미 실행된 것들은 되돌릴 수 없어요.
🎯 stopPropagation
을 사용하는 이유
그렇다면 개발자들은 언제 이벤트 전파를 멈추고 싶어할까요? 여러 실용적인 상황들이 있어요.
첫 번째로 원하지 않는 이벤트 실행을 방지하기 위해서예요. 버튼을 클릭했는데 버튼의 부모 요소에도 클릭 이벤트가 있다면, 사용자가 원하지 않는 동작이 추가로 실행될 수 있어요. 이런 경우 버튼에서 전파를 멈추면 깔끔하게 해결돼요.
두 번째로 각각 따로 작동하게 만들기 위해서예요. 카드나 패널 같은 구성 요소 내부의 버튼을 클릭할 때, 그 버튼의 기능만 실행되고 카드 자체의 클릭 이벤트는 실행되지 않도록 하고 싶을 때 사용해요.
세 번째로 모달창과 팝업을 관리하기 위해서예요. 모달창의 내용 부분을 클릭했을 때는 모달이 닫히지 않도록 하고, 배경 부분을 클릭했을 때만 닫히도록 하려면 내용 부분에서 전파를 멈춰야 해요.
네 번째로 복잡한 레이아웃에서의 정확한 제어를 위해서예요. 드롭다운 메뉴, 아코디언, 탭 등 여러 상호작용 요소들이 중첩된 복잡한 UI에서 사용자가 원하는 대로 정확하게 작동하도록 만들기 위해 사용해요.
⚙️ 기본 사용법 살펴보기
stopPropagation
의 사용법은 참으로 간단해요. 이벤트 리스너 함수에서 이벤트 정보 묶음을 받아서 호출하기만 하면 돼요.
요소.addEventListener("이벤트타입", function(event) {
// 현재 요소의 이벤트 처리
console.log("현재 요소에서 처리됨");
// 이벤트 전파 막기
event.stopPropagation();
});
실제 예시:
// 자식 요소에서 전파 막기
let button = document.getElementById("myButton");
button.addEventListener("click", function(event) {
console.log("버튼 클릭됨");
event.stopPropagation(); // 부모로 전파되지 않아요
});
주의사항:
가장 중요한 것은 이벤트 정보 묶음을 매개변수로 받는 것이에요. event
대신 e
나 다른 이름을 사용해도 되지만, 반드시 이벤트 정보 묶음을 받아야 stopPropagation
기능을 사용할 수 있어요. 또한 기능 이름 뒤에 괄호 ()
를 빼먹지 않도록 주의해야 해요.
🧪 직접 해보며 배우기
이제 실제 예시를 통해 stopPropagation
이 어떻게 작동하는지 차근차근 살펴볼게요.
🔹 첫 번째 예시: 버튼 클릭 시 부모 이벤트 실행 막기
첫 번째 예시에서는 가장 기본적인 상황에서 stopPropagation
을 사용하는 방법을 배워볼게요.
// HTML 구조: <div id="container"><button id="actionButton">클릭하세요</button></div>
// 컨테이너(부모 요소)에 클릭 이벤트를 걸어줘요.
let container = document.getElementById("container");
container.addEventListener("click", function() {
console.log("컨테이너가 클릭되었어요!"); // 실행되지 않을 예정이에요
alert("컨테이너 클릭!");
});
// 버튼(자식 요소)에 클릭 이벤트를 걸어주고 전파를 막아요.
let actionButton = document.getElementById("actionButton");
actionButton.addEventListener("click", function(event) {
console.log("버튼이 클릭되었어요!"); // 실행돼요
alert("버튼 클릭!");
// 여기서 이벤트 전파를 멈춰요!
event.stopPropagation();
});
console.log("이벤트가 설정되었어요. 버튼을 클릭해보세요!");
이 과정을 차근차근 살펴보면, 먼저 부모 컨테이너와 자식 버튼에 각각 클릭 이벤트를 걸어줘요. 일반적인 상황이라면 버튼을 클릭했을 때 버튼 이벤트와 컨테이너 이벤트가 모두 실행되겠지만, stopPropagation
을 사용했기 때문에 버튼 이벤트만 실행되고 컨테이너 이벤트는 실행되지 않아요.
🔹 두 번째 예시: 전파를 막았을 때와 막지 않았을 때 비교하기
두 번째 예시에서는 같은 상황에서 stopPropagation
을 사용했을 때와 사용하지 않았을 때의 차이점을 명확히 비교해볼게요.
// 첫 번째 세트: 전파를 막지 않은 경우
let normalBox = document.getElementById("normalBox");
let normalButton = document.getElementById("normalButton");
normalBox.addEventListener("click", function() {
console.log("일반 상자 클릭됨"); // 실행돼요
});
normalButton.addEventListener("click", function(event) {
console.log("일반 버튼 클릭됨"); // 실행돼요
// stopPropagation을 사용하지 않아요 → 부모 이벤트도 실행돼요
});
// 두 번째 세트: 전파를 막은 경우
let blockedBox = document.getElementById("blockedBox");
let blockedButton = document.getElementById("blockedButton");
blockedBox.addEventListener("click", function() {
console.log("차단 상자 클릭됨"); // 실행되지 않아요!
});
blockedButton.addEventListener("click", function(event) {
console.log("차단 버튼 클릭됨"); // 실행돼요
// stopPropagation을 사용해서 부모로의 전파를 막아요
event.stopPropagation();
});
console.log("두 버튼을 각각 클릭해서 차이점을 확인해보세요!");
이 예시를 통해 stopPropagation
의 효과를 직접적으로 비교할 수 있어요. 첫 번째 버튼을 클릭하면 버튼과 상자 이벤트가 모두 실행되지만, 두 번째 버튼을 클릭하면 버튼 이벤트만 실행되고 상자 이벤트는 실행되지 않는 것을 확인할 수 있어요.
🔹 세 번째 예시: 모달창에서 배경 클릭 방지하기
세 번째 예시에서는 실제 웹 개발에서 자주 사용되는 모달창 시나리오에서 stopPropagation
을 활용하는 방법을 배워볼게요.
// 모달창 배경 (클릭하면 모달이 닫혀요)
let modalBackground = document.getElementById("modalBackground");
modalBackground.addEventListener("click", function() {
console.log("모달창을 닫아요"); // 모달창을 숨기는 코드가 들어갈 자리예요
// 실제로는 모달창을 숨기는 코드가 들어가요
alert("모달창이 닫혔어요!");
});
// 모달창 내용 부분 (클릭해도 모달이 닫히지 않아요)
let modalContent = document.getElementById("modalContent");
modalContent.addEventListener("click", function(event) {
console.log("모달 내용 부분이 클릭되었어요"); // 내용 클릭 시 동작
// 배경 클릭 이벤트가 실행되지 않도록 전파를 막아요
event.stopPropagation();
});
// 모달 내부의 버튼들 (각각 고유한 기능 수행)
let saveButton = document.getElementById("saveButton");
saveButton.addEventListener("click", function(event) {
console.log("저장 버튼 클릭됨"); // 저장 기능 실행
alert("정보가 안전하게 저장되었어요!");
// 모달이 닫히지 않도록 전파를 막아요
event.stopPropagation();
});
console.log("모달창 시스템이 준비되었어요!");
이 예시에서는 실제 웹사이트에서 흔히 볼 수 있는 모달창의 동작을 구현해요. 모달창의 배경을 클릭하면 모달이 닫히지만, 모달 내용 부분이나 내부 버튼을 클릭했을 때는 모달이 닫히지 않도록 stopPropagation
을 사용해요. 이렇게 하면 사용자가 실수로 모달을 닫는 것을 방지할 수 있어요.
🔄 stopPropagation
사용 과정 정리하기
지금까지 학습한 stopPropagation
의 사용 단계를 자연스럽게 정리해볼게요.
첫 번째 단계는 상황 분석하기예요. 현재 이벤트가 부모로 전파되어서 문제가 생기는지, 아니면 원하는 동작과 다른 결과가 나타나는지 파악해야 해요. 모든 경우에 stopPropagation
이 필요한 것은 아니거든요.
두 번째는 이벤트 정보 묶음 준비하기 단계예요. 이벤트 리스너 함수에서 반드시 첫 번째 매개변수로 이벤트 정보 묶음을 받아야 해요. 보통 event
나 e
라는 이름을 사용해요.
세 번째는 현재 요소 처리하기 단계예요. stopPropagation
을 호출하기 전에 현재 요소에서 해야 할 작업들을 먼저 처리해요. 메시지 출력, 스타일 변경, 함수 호출 등이 이 단계에 해당해요.
네 번째는 전파 차단하기 단계예요. event.stopPropagation()
을 호출해서 이벤트가 더 이상 부모 요소로 전파되지 않도록 정중히 막아요. 이때 괄호를 빼먹지 않도록 주의해야 해요.
마지막 단계는 결과 확인하기예요. 실제로 부모 요소의 이벤트가 실행되지 않는지, 원하는 동작만 실행되는지 테스트해서 확인해요.
🧚♀️ 이야기로 다시 배우기: 병원에서 환자 이송 시스템
지금까지 배운 내용을 하나의 이야기로 다시 정리해볼까요?
우리 동네 병원에는 특별한 환자 이송 시스템이 있어요. 이 병원은 여러 층으로 이루어져 있고, 1층 → 2층 → 3층 → 4층 순서로 연결되어 있어요. 평소에는 1층에서 환자에게 어떤 일이 생기면 그 소식이 자동으로 위층들로 전달돼요.
예를 들어, 1층 응급실에서 "긴급 환자가 왔다"는 소식이 생기면, 2층 검사실 → 3층 수술실 → 4층 중환자실에게까지 차례로 전달되어서 각 층에서 적절한 준비를 하게 돼요.
하지만 때로는 특별한 상황이 생깁니다. 2층 검사실에서 "이 환자는 검사만 하고 끝"이라는 판단을 내리면, 1층에서 올라온 소식이 2층에서 조용히 멈춰요. 더 이상 3층이나 4층으로 전달되지 않아요.
바로 이 "검사만 하고 끝" 판단이 stopPropagation
이에요! 2층에서 필요한 검사는 하지만, 그 위층들에게는 소식을 전달하지 않겠다는 따뜻한 배려인 것이죠.
이렇게 하면 각 층에서 필요한 만큼만 정확한 대응을 할 수 있게 돼요. 1층의 일이 불필요하게 4층까지 영향을 주지 않고, 각자 필요한 범위에서만 처리되는 것이에요.
🧠 자주 하는 실수와 주의할 점
stopPropagation
을 사용할 때 주의해야 할 몇 가지 실수들을 미리 알아두면 더 안전하게 사용할 수 있어요.
❌ 실수 1: 이벤트 정보 묶음을 받지 않고 stopPropagation 호출하기
// 잘못된 방법 - 이벤트 정보 묶음이 없어요
button.addEventListener("click", function() {
console.log("버튼 클릭됨");
stopPropagation(); // 에러! event 정보 묶음이 없어요
});
// 올바른 방법 - 이벤트 정보 묶음을 매개변수로 받기
button.addEventListener("click", function(event) {
console.log("버튼 클릭됨");
event.stopPropagation(); // 정상 작동
});
이런 실수가 발생하는 이유는 stopPropagation
이 이벤트 정보 묶음의 기능이기 때문이에요. 이벤트 정보 묶음 없이는 이 기능에 접근할 수 없어요. 반드시 함수의 첫 번째 매개변수로 이벤트 정보 묶음을 받아야 해요.
❌ 실수 2: 기능 호출 시 괄호 빼먹기
// 잘못된 방법 - 괄호가 없어서 기능이 호출되지 않아요
button.addEventListener("click", function(event) {
console.log("버튼 클릭됨");
event.stopPropagation; // 기능을 호출하지 않아요!
});
// 올바른 방법 - 괄호를 넣어서 기능 호출
button.addEventListener("click", function(event) {
console.log("버튼 클릭됨");
event.stopPropagation(); // 정상적인 기능 호출
});
JavaScript에서 기능을 호출할 때는 반드시 괄호 ()
를 붙여야 해요. 괄호가 없으면 기능을 참조만 하고 실제로는 실행되지 않아요.
❌ 실수 3: 모든 이벤트에 무분별하게 stopPropagation 사용하기
// 문제가 될 수 있는 방법 - 모든 곳에서 전파 막기
document.addEventListener("click", function(event) {
event.stopPropagation(); // 모든 클릭의 전파를 막아요!
console.log("클릭됨");
});
// 적절한 방법 - 필요한 곳에서만 선택적으로 사용
specialButton.addEventListener("click", function(event) {
console.log("특별한 버튼 클릭됨");
event.stopPropagation(); // 이 버튼에서만 전파 막기
});
stopPropagation
은 꼭 필요한 경우에만 사용해야 해요. 무분별하게 사용하면 이벤트 위임이나 다른 기능들이 제대로 작동하지 않을 수 있어요.
✏️ 연습문제로 개념 다지기
이제 배운 내용을 연습문제를 통해 확실히 익혀볼게요.
Ex1) 버튼을 클릭해도 부모 컨테이너의 이벤트는 실행되지 않게 만들어보자
let container = document.getElementById("container");
let button = document.getElementById("button");
container.addEventListener("click", function() {
console.log("컨테이너 클릭됨"); // 실행되지 않을 거예요
});
button.addEventListener("click", function(event) {
console.log("버튼 클릭됨"); // 실행돼요
event.stopPropagation(); // 여기서 전파를 막아요
});
예상 결과: "버튼 클릭됨"만 출력돼요. (컨테이너 이벤트는 실행되지 않아요)
Ex2) 이벤트 정보 묶음을 매개변수로 받지 않으면 어떻게 될까요? 올바른 코드로 수정해보세요.
// 문제가 있는 코드
button.addEventListener("click", function() {
console.log("버튼 클릭됨");
stopPropagation(); // 이 부분에 문제가 있어요!
});
// 올바른 코드로 수정하세요.
button.addEventListener("click", function(event) {
console.log("버튼 클릭됨");
event.stopPropagation(); // 이제 올바르게 작동해요
});
Ex3) 모달창의 내용 부분을 클릭해도 모달이 닫히지 않도록 코드를 완성해보세요.
// 모달 배경 클릭 시 모달 닫기
let modalBg = document.getElementById("modalBg");
modalBg.addEventListener("click", function() {
console.log("모달창 닫힘");
});
// 모달 내용 클릭 시 모달이 닫히지 않도록 하세요.
let modalContent = document.getElementById("modalContent");
modalContent.addEventListener("click", function(event) {
console.log("모달 내용 클릭됨");
// 여기에 적절한 코드를 추가하세요.
event.stopPropagation(); // 배경으로의 전파를 막아요
});
🤔 조금 더 어려운 문제로 실력 확인하기
기본 연습을 마쳤다면, 이제 조금 더 깊이 있는 문제들을 통해 stopPropagation
에 대한 이해를 확인해볼게요.
Q1. stopPropagation
과 preventDefault
의 차이점을 설명해보세요.
정답: stopPropagation
은 이벤트가 다른 요소로 전파되는 것을 막고, preventDefault
는 브라우저의 기본 동작(링크 클릭, 폼 제출 등)을 막아요. 두 기능은 완전히 다른 목적으로 사용되며, 필요에 따라 함께 사용할 수도 있어요.
Q2. 다음 코드에서 할아버지의 이벤트가 실행되지 않으려면 어디에 stopPropagation
을 넣어야 할까요?
// HTML: <div id="grandpa"><div id="father"><button id="son">버튼</button></div></div>
grandpa.addEventListener("click", function() {
console.log("할아버지 클릭됨");
});
father.addEventListener("click", function(event) {
console.log("아버지 클릭됨");
// 여기에 넣으면?
});
son.addEventListener("click", function(event) {
console.log("아들 클릭됨");
// 아니면 여기에 넣으면?
});
정답: 아버지나 아들 중 어디에 넣어도 할아버지 이벤트는 실행되지 않아요. 아들에 넣으면 아버지와 할아버지 모두 실행되지 않고, 아버지에 넣으면 할아버지만 실행되지 않아요.
🔙 지난 시간 복습하기 (11단원 - DOM 조작)
12단원을 계속 공부하기 전에, 지난 시간에 배운 DOM 조작 내용을 복습해볼까요?
복습 문제 1: 클래스 추가하고 제거하기
다음 중 요소에 "active" 클래스를 추가하는 올바른 방법은?
let element = document.getElementById("myDiv");
// A) element.class.add("active")
// B) element.classList.add("active")
// C) element.addClassName("active")
// D) element.style.addClass("active")
정답: B) element.classList.add("active")
가 정답이에요. 클래스를 조작할 때는 classList
를 사용해요.
설명: 11단원에서 배운 클래스 조작 방법이에요. classList.add()
로 클래스를 추가하고, classList.remove()
로 제거하고, classList.toggle()
로 토글할 수 있어요.
복습 문제 2: 요소 속성 바꾸기
다음 코드에서 이미지의 src 속성을 "new.jpg"로 바꾸는 올바른 방법은?
let img = document.getElementById("myImage");
// A) img.src = "new.jpg"
// B) img.setAttribute("src", "new.jpg")
// C) 둘 다 가능
// D) img.source = "new.jpg"
정답: C) 둘 다 가능해요!
설명: 11단원에서 배운 속성 조작 방법이에요. img.src
처럼 직접 접근하거나 setAttribute()
를 사용할 수 있어요. 오늘 배운 stopPropagation
과 함께 사용하면 이미지를 클릭할 때만 이미지가 바뀌고 부모 요소의 이벤트는 실행되지 않는 효과를 만들 수 있어요.
지금까지 stopPropagation
의 모든 특성과 활용법을 차근차근 알아보았어요. 이 기능은 복잡한 웹 인터페이스에서 사용자가 원하는 대로 정확하게 반응하도록 만드는 데 매우 중요한 도구예요. 마치 교통신호처럼, 이벤트의 흐름을 적절히 제어해서 웹페이지가 사용자가 원하는 대로 정확하게 동작하도록 만들어주는 따뜻한 기능이에요!
✅ 학습 완료 체크리스트
이번 시간에 배운 내용들을 모두 이해했는지 확인해보세요!
학습 내용 | 이해했나요? |
---|---|
stopPropagation의 기본 개념 | ✅ |
기본 사용법과 문법 | ✅ |
엘리베이터 비유 이해하기 | ✅ |
자주 하는 실수들 | ✅ |
실전 예제 활용하기 | ✅ |
🎯 추가 연습 문제들
조금 더 연습하고 싶은 친구들을 위한 추가 문제들이에요!
추가 문제 1. 다음 코드에서 버튼을 클릭했을 때 콘솔에 무엇이 출력될까요?
let container = document.getElementById('container');
let button = document.getElementById('button');
container.addEventListener('click', function() {
console.log('컨테이너 클릭');
});
button.addEventListener('click', function(event) {
console.log('버튼 클릭');
event.stopPropagation();
});
답: "버튼 클릭"만 출력돼요.
설명: stopPropagation이 부모로의 전파를 막기 때문이에요.
추가 문제 2. stopPropagation을 사용하지 않은 코드를 작성해보세요.
let outer = document.getElementById('outer');
let inner = document.getElementById('inner');
outer.addEventListener('click', function() {
console.log('외부');
});
inner.addEventListener('click', function(event) {
console.log('내부');
// 여기에 stopPropagation 추가하기
event.stopPropagation();
});
설명: event.stopPropagation()을 추가하면 내부 클릭 시 외부 이벤트가 실행되지 않아요.
추가 문제 3. 이벤트 객체를 받는 매개변수 이름을 다르게 써도 될까요?
// 이런 식으로 써도 되나요?
button.addEventListener('click', function(e) {
e.stopPropagation();
});
답: 네, 가능해요!
설명: 매개변수 이름은 event, e, evt 등 어떤 이름이든 상관없어요. 중요한 것은 이벤트 객체를 받는 것이에요.
추가 문제 4. 다음 중 올바른 stopPropagation 사용법은?
// A
button.addEventListener('click', function() {
stopPropagation();
});
// B
button.addEventListener('click', function(event) {
event.stopPropagation();
});
답: B
설명: stopPropagation은 이벤트 객체의 메서드이므로 event.stopPropagation()
처럼 써야 해요.
추가 문제 5. stopPropagation을 사용하면 어떤 효과가 있나요?
답: 이벤트가 부모 요소로 전파되지 않아요.
설명: 현재 요소에서만 이벤트가 처리되고, 부모 요소의 이벤트는 실행되지 않아요.
📂 마무리 정보
오늘 배운 12.4.2
내용이 여러분의 자바스크립트 지식 상자에 잘 저장되었나요? 다음 시간에는 더 재미있는 내용으로 만나요!
기억할 점: 오늘 배운 내용을 꼭 연습해보시고, 궁금한 점이 있으면 언제든 다시 돌아와서 읽어보세요.
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
'12. 클릭하고 반응하기 (이벤트 처리) > 12.4 버블링과 캡처링' 카테고리의 다른 글
12.4.1 이벤트 전파 과정 - 물방울이 흘러가는 아름다운 여행 (0) | 2025.07.17 |
---|